Rails: Begin Rescue block not being called in private method - ruby-on-rails

In my rails app I have this bit of logic in my SessionsController's create method that I want to refactor out by extracting it into a method:
if login_form.validate(params[:user])
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
flash.now.alert = 'invalid user credentials'
render :new and return
end
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
All this does is check if user exists in the system. I want to refactor this into
if login_form.validate(params[:user])
find_user
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
And then the private find_user method:
private
def find_user
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
flash.now.alert = 'invalid user credentials'
render :new and return
end
end
But now after I do this, the ActiveRecord::RecordNotFound exception is ignored! The method does not stop at the return command. Why is that? What am I doing wrong?
UPDATE:
I understand what I am doing wrong. But how do I do this in the right way?

The return statement just returns from the function where it is called, that is the find_user method.

Ok so I can use a block like so:
if login_form.validate(params[:user])
find_user do
flash.now.alert = 'invalid user credentials'
render :new and return
end
else
flash.now.alert = login_form.errors.full_messages
render :new and return
end
And then refactor the private method like so:
def find_user(&block)
begin
#user = User.find_by!(email: params[:user][:email])
rescue ActiveRecord::RecordNotFound => e
yield
end
end

Related

how to stop execution after redirect_to is called

i wrote an action which is again invoking another private method,
if a specific condition is met i want to redirect to some other page or else continue the execution.
but i was unable to do,
when i tried to use redirect_to root_path it says double render,
it is actually trying to execute the action statements which was actually called instead of rendering from the private method.
def actual_method_called
data1 = params[:data1]
method_2(data1)
data2 = params[:data1]
method_2(data2)
render json: {status: 'ok' }
end
private
def method_2(data)
if data.valid?
puts 'continue the execution'
else
redirect_to root_path and return
end
end
You can return a value from the called method...
def method_2(data)
if data.valid?
puts 'continue the execution'
return
else
redirect_to root_path
return :redirected
end
end
Wen you call it you store the returned value
def actual_method_called
data1 = params[:data1]
return_status ||= method_2(data1)
data2 = params[:data1]
return_status ||= method_2(data2)
render json: {status: 'ok' } unless return_status == :redirected
end

Retrieve multiple records with find_by method

The method below works and authenticates a user who has been sent a token-link by email.
def login
inv = Invitation.find_by(email: params[:email])
if inv && inv.authenticated?(:invitation, params[:id])
#organization = inv.organization
unless #organization.nil?
render 'profiles/show' and return
else
flash[:danger] = "Error"
redirect_to root_path
end
else
flash[:danger] = "Invalid link"
redirect_to root_path
end
end
This method however seems to assume a person (inv) can only exist once in the Invitation database. This is not the case, the same person/email address can have multiple records in the database. Therefore, I need to rewrite the method to account for such a situation. How can I do this? Can I use .all as added on line 2 below, and use .each?
def login
inv = Invitation.find_by(email: params[:email]).all
if inv
inv.each do |person|
if person.authenticated?(:invitation, params[:id])
#organization = person.organization
unless #organization.nil?
render 'profiles/show' and return
else
flash[:danger] = "Error"
redirect_to root_path and return
end
end
flash[:danger] = "Invalid link"
redirect_to root_path
end
else
flash[:danger] = "Invalid link"
redirect_to root_path
end
end
Error messages:
This code produces the error message below but I'm not sure what else than .all to use:
NoMethodError: undefined method `all' for #<Invitation:0x0000000b869ee8>
You need to use find_all_by or where
inv = Invitation.find_all_by(email: params[:email])
or
inv = Invitation.where(email: params[:email])

Ruby on Rails: redirect_to not working after create and save

I want to redirect_to slider_path after a user submits their email. Currently, only the success message is displayed without a redirect. Here's the code:
class Splash::SubscribersController < ApplicationController
def create
#subscriber = Subscriber.new(params[:subscriber])
if #subscriber.save
success = true
message = "Success! We'll let you know when we launch."
else
success = false
message = "Fail."
end
respond_to do |format|
format.html {
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
end
redirect_to root_path
}
format.json { render :json => { :success => success, :message => message }.to_json }
end
end
end
Just replace this part of your code:
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
end
redirect_to root_path
with this:
if success
flash[:success] = message
redirect_to slider_path
else
flash[:error] = message
redirect_to root_path
end
Rails API states:
An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
def do_something
redirect_to :action => "elsewhere"
render :action => "overthere" # raises DoubleRenderError
end
If you need to redirect on the condition of something, then be sure to add “and return” to halt execution.
def do_something
redirect_to(:action => "elsewhere") and return if monkeys.nil?
render :action => "overthere" # won't be called if monkeys is nil
end
Note the use of and return
Neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
Add a return statement after your redirect. If the action also renders a template by default, any redirects need to be followed by a return statement.
if success
flash[:success] = message
redirect_to slider_path
return # <= Add a return.
else
flash[:error] = message
end
redirect_to root_path

ambethia's reCAPTCHA plugin on Rails 3. Override flash message div defaults?

I have ambethia's reCAPTCHA plugin on Rails 3 working. Does anyone know how to override it's flash message markup? I'd like to reuse my own flash_error div id instead of using the plugin's flash_recaptcha_error div id:
<div id="flash_recaptcha_error">incorrect-captcha-sol</div>
Also, how would you clean up this controller#create?
def create
#post = Post.new(params[:post])
respond_to do |format|
if verify_recaptcha(:model => #post, :error => "reCAPTCHA incorrect. Try again.") && #post.save
flash.now[:notice] = "Created \"#{#post.title}\""
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
else
flash.now[:error] = "Incorrect word verification. Are you sure you\'re human?"
format.html { redirect_to(:back, :error => 'reCAPTCHA incorrect. Try again.') }
end
end
end
Thanks for reading my question.
Because flash[] is an array you could delete element inside it. When we use recaptcha gem, the flash array contain recaptcha_error element, so you just only delete this element with :
flash.delete(:recaptcha_error) inside your controller.
For example :
if verify_recaptcha(:model=>#object,:message=>"Verification code is wrong", :attribute=>"verification code") && #object.save
#your code if succes
else
flash.delete(:recaptcha_error)
#your code if its fail
end
Maybe it could help you. Thanks
If you're making a User Authentication System from scratch, you may have to do something like this:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
respond_to do |format|
if verify_recaptcha(:model => #user )
if #user.save
format.html { redirect_to root_url, :notice => "You have Signed up!" }
else
format.html { render :new }
end
else
flash.delete(:recaptcha_error)
format.html { redirect_to( root_path , :flash => { :error => 'Please retry the two words of the reCaptcha' } ) }
end
end
end
end

work with rescue in Rails

I am working with the following piece;
def index
#user = User.find(params[:id])
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
else
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
Now I either case whether I have a correct ID or not, I am always getting "OK" in my view, what am I doing wrong?
I need that when I have no ID in the DB to show "ERROR". I have also tried to use rescue ActiveRecord::RecordNotFound but same happens.
All help is appreciated.
All code after the end of the rescue block is interpreted only if there are no returns in the rescue block. So you can call return at the end of your rescue block.
def index
begin
#user = User.find(params[:id])
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
return
end
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
or
def index
#user = User.find(params[:id])
# after is interpret only if no exception before
flash[:notice] = "OK"
redirect_to(:action => 'index')
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end
But in your case the better is to use rescue_from or rescue_in_public
like
class UserController < ApplicationController
def rescue_in_public(exception)
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end
def index
#user = User.find(params[:id])
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
end
But the using of rescue_in_public is not really good advice
Just an overall Rails Rescue answer:
I found this to be very cool:
#user = User.find(params[:id]) rescue ""
If there is no user with that id, then User.find will return nil. Returning nil is not an error case and will not trigger a rescue.

Resources