Redirect_to in model after error - ruby-on-rails

I have a function in my model to import data from a CSV file and I'd like to have validations should there be any errors. For example, when I upload the file, I search for a User based on an ID in the file. If there is no User with that ID, I'd like to redirect_to a different page with an error.
def self.getUser(scale_id)
#user = User.find_by(scale_id: scale_id)
if #user == nil
redirect_to users_path
else
return #user
end
end
def self.bulk_upload_weigh_ins(file)
output = []
errors = []
CSV.foreach(file.path, headers: true, ) do |row|
row = row.to_hash
#scale_id = row["scale_id"]
#user = getUser(#scale_id)
row.merge!(user_id: #user_id)
WeighIn.create! row.to_hash
end
end
...and no matter what path I put there, I get the following: undefined local variable or method 'users_path' for #<Class:0x007fa06f466998> even when it is a valid path.
Is there something wrong with redirecting like this? If yes, how should I do it?

The cleanest way for custom validations is to do something like:
In your model:
User < ActiveRecord::Base
validate :get_user
def initialize(params={})
self.id = params[:id]
end
def get_user
#user = User.find_by(self.id)
if #user.nil?
errors.add(:base, "Invalid User")
return false
else
return #user
end
end
In your controller you'd then do:
def whatever_action_youre_using
#user = User.new(user_params)
unless #user.valid?
redirect_to users_path
end
end
def user_params
params.require(:user).permit(:first_name, :email, :etc, :whatever_your_atts)
end

I have a function in my model that is searching for a User based on an
ID
undefined local variable or method 'users_path' for
Class:0x007fa06f466998
As #Dave Newton mentioned in the comments, the path helpers are not available in models unless you specifically include them. Also you can't and never need to use redirect_to in a model. The application logic should be moved to the controller. Something like below should work
def get_user
#user = User.find_by(scale_id: params[:user][:scale_id])
if #user.nil?
redirect_to users_path, notice: 'Your error message here'
else
return #user
end
end

Related

Rails how to require the current password to allow user update

I am having a rather difficult problem i want to update the user profile only if they submit the current password.I am not using devise.And another post here at stack overflow didn't really help me.
This is my User controller code:
class UsersController < ApplicationController
def new
#user = User.new
end
def show
#user = User.find(params[:id])
#user_posts = #user.posts if #user
if #user
if #user_posts
render 'show.html'
else
render 'show.html'
end
else
render file: 'public/404.html', status: 404, formats: [:html]
end
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_path
flash[:notice] = "Successfully Signed up :-)"
else
redirect_to signup_path
flash[:notice] = "You didn't sign up successfully :-("
end
end
def edit
#user = User.find(params[:id])
if current_user.id = #user.id
render 'edit'
else
redirect_to #user
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:notice] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
:password == :password_confirmation
private
def user_params
params.require(:user).permit(:user_name, :email, :password, :password_confirmation)
end
end
And this is my user.rb:
class User
has_secure_password
has_many :posts
has_many :comments
def admin?
self.role == 'admin'
end
def moderator?
self.role == 'moderator'
end
end
Please help because I have been working with this for a long time now. And the other solution about this topic here at stack overflow didn't work.
One way is to use virtual attributes
1. The User model
class User < ActiveRecord::Base
attr_accessor :current_password
end
2. The form
add the current_password attribute to the form as a text_field input
3. The UsersController
def update
#user = User.find params[:id]
if #user.authenticate(update_params[:current_password])
# update the user
# maybe check if the data are valid
#user.update(update_params)
else
flash[:warning] = "Please provide your password"
#user.errors.add :current_password, "invalid"
render :edit
end
end
def update_params
params.require(:user).permit(:current_password, :email)
end
First, you have a problem in your edit action:
current_user.id = #user.id
That assigns #user.id to current_user.id - you wanted == to test that it's the correct User. You should put a similar check on update, and probably extract it into a before_action so you can easily apply it anywhere you want to.
To check that the password is present, add it to your form like any other field and then get it out of params to verify it. That would look something like this:
class UsersController < ApplicationController
def update
encrypted = encrypt(params[:password]) # Using whatever your mechanism is
if encrypted == #user.encrypted_password
# Update the user
else
flash[:notice] = 'Password is required to update user information.'
redirect_to edit_user(path(#user))
end
end
end

Why is this Mailer giving an error after applying validations?

The error is coming in the create method user mailer section.
I am trying to resolve it, but nothing happens.
How can it be resolved? I come when I apply the validations.
The error is: Expected a URI like gid://app/Person/1234: #<URI::GID gid://email>
My mailer controller:
class UserMailer < ApplicationMailer
def welcome_email(user)
#user = user
#url = 'http:3000//example.com/login#u'
mail(to: #user.email, subject: 'Welcome to My Awesome Site')
end
end
My user controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.save
UserMailer.welcome_email(#user).deliver_later[here the error come about the invalid url]
render 'token'
end
def verify
#user = User.authenticate(params[:auth_token])
if #user
redirect_to edit_user_path(#user)
else
flash.now.alert = "Invalid email or password"
render 'token', :alert =>"Invalid email or password"
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to new_login_path
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :dob, :email,
:password, :confirm_password, :auth_token)
end
end
You are trying to pass the Mailer an unpersisted object, ie. an object that was not saved to the database.
From your code, that means that the previous #user.save statement failed probably due to a validation error. In that case, you don't want to send the email anyway.
Change your create action like this:
def create
#user = User.new(user_params)
if #user.save
UserMailer.welcome_email(#user).deliver_later
render 'token'
else
flash[:error] = 'User was not saved'
render 'new'
end
end

Ruby on Rails Accessing params from controller from form

I'm trying to access parameters in my controller as strings
When I log
Rails.logger.debug "!!!" << "#{params[:username]}"
nothing comes after the exclamation points even though
Rails.logger.debug params.inspect
shows that username has a value.
Can someone please tell me how to get the username value to show up in my log debugging statement?
Controller Code:
def create
Rails.logger.debug params.inspect
Rails.logger.debug "!!!" << "#{#user[:username]}"
#user = Users.new(user_params)
{params[:birth_year]}"
if #user.save
redirect_to(:controller => 'users', :action => 'index')
else
render(:controller => 'access', :action => 'index')
end
end
last few lines of logged info:
"user"=>{"username"=>"TrevorTT" ...
!!!
#user would not work in this line Rails.logger.debug "!!!" << "#{#user[:username]}" because it's not created yet. Either move
#user = Users.new(user_params) above logger.
But as per my understanding you just want to log username params only.
So you can do it like this params[:user][:username] because username nested under user params. So your code should be:
def create
Rails.logger.debug params.inspect
Rails.logger.debug "!!!" << "#{params[:user][:username]}"
....
end
#user is called before it is defined. It will return nil.
def create
#...
Rails.logger.debug "!!!" << "#{#user[:username]}" # <-- #user is nil
#user = User.new(user_params)
You can either leave the line in place and use user_params
def create
#...
Rails.logger.debug "!!!" << "#{user_params[:username]}"
#user = User.new(user_params)
or move the line below #user =
def create
#...
#user = User.new(user_params)
Rails.logger.debug "!!!" << "#{#user[:username]}"
Also, your model names should be singluar (eg. User).

Nested form validation rails 3.2

I have a job and user(devise) form in the same view. When I am trying to submit with errors in the user fields it gives me an exception page with the validation messages. Submitting errors in the job fields works fine!
job_controller.rb
def new
#job = Job.new
if !current_user
#job.user = User.new
end
respond_to do |format|
format.html # new.html.erb
end
end
def create
#types = Type.all
#categories = Category.all
#job = Job.new(params[:job])
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes])
else
#user = current_user
end
#job.user_id = #user.id
respond_to do |format|
if #job.save
if !current_user
sign_in(:user, #user)
end
format.html { redirect_to #job }
else
format.html { render action: "new" }
end
end
end
job.rb
attr_accessible :user_attributes, :description, :name ....
belongs_to :user
accepts_nested_attributes_for :user
Thanks!
That becuase you are calling, #user.save! which will generate an exception. Also doing it this way won't put the job in the same transaction as User. What you want are nested_attributes:
class Job < ActiveRecord::Base
accepts_nested_attributes_for :user
end
If the user is logged in, don't show that part of the form and filter those params.
See more in the Rails documentation here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
EDIT:
Simplify your controller code, since you're using nested attributes you no longer need to manually create a user.
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes]) # this is no longer needed
else
#user = current_user
end
#job.user_id = #user.id # this is redundant
To something more like:
# if logged in, manually assign the user (also you may want to reject any user attributes)
#job.user = current_user if current_user

Updating a record via a action method

User signs up, is redirected to a page to be collected info, pretty straight forward
I for my life can't figure out how to do this
My controller for the user
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def additional_info
#user = User.find session[:user_id]
#user = User.update(user_addinfo)
redirect_to user_path
end
def create
#user = User.new(user_params)
if #user.save
#session[:user_id] = #user.id
#UserMailer.welcome_email(#user).deliver
sign_in #user
redirect_to additional_info_path
flash[:success] = "Welcome to InYourShoes!"
else
render'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def user_addinfo
params.require(:user).permit(:year)
end
end
user_addinfo is the action method that i want to call updating my record on for my additional_info method.
the def create method has commented line that i'm unsure if necessary, particularly the session[:user_id] = #user.id. I was told that i need this in order to keep track of my session, but perhaps someone can debunk this for me, as im following michael hartl's tutorial.
as of right now with this code, rails is giving me a parameter missing in the
params.require(:user).permit(:year) line.
Much help is greatly appreciated. Ive been trying many different things, and cant seem to figure this out
Change your controller code as below:
def additional_info
#user = User.find params[:id] ## Set #user
end
def update
if #user.update(user_addinfo)
redirect_to user_path(#user), notice: 'User was successfully updated.'
else
render action: 'additional_info'
end
end
def create
#user = User.new(user_params)
if #user.save
#session[:user_id] = #user.id
#UserMailer.welcome_email(#user).deliver
sign_in #user
redirect_to additional_info_path(#user) ## Pass #user
flash[:success] = "Welcome to InYourShoes!"
else
render'new'
end
end
and in your routes.rb update the additional_info route as
get 'info/:id' => 'users#additional_info', :as => 'additional_info'
You additional_info action seems to be wrong. You need to pass in the id of the user for whom you are collecting additional information.
def additional_info
#user = User.find params[:id]
#user.update_attributes(user_addinfo)
redirect_to user_path(#user)
end
The line you have commented in your create method:
#session[:user_id] = #user.id
Is what is storing the user id to a session variable and not a param in the url.
You then have this line commented in your additional_info method
#user = User.find session[:user_id]
This is looking up the user by the id that you would have previously stored in the session variable.
At that point the user object would be stored in user
If you need it in your instance variable, make sure to modify the line to be
#user = User.find session[:user_id]
Your user would then be stored in #user and be able to be accessed in the view

Resources