Rescue From If JSON - ruby-on-rails

I'd like to rescue from a RecordNotFound exception if, and only if, the request is JSON. Now if I was doing this for a skip_before_action, I would do the below:
skip_before_action :verify_authenticity_token, if: :json_request?
Is there syntax for this in rescue_from? Something like:
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found, if: :json_request?
Helper method:
protected
def json_request?
request.format.json?
end

I am assuming if the request is not JSON then you want to raise? If so you should be able to do this
rescue_from ActiveRecord::RecordNotFound { request.format.json? ? record_not_found : super }
OR
rescue_from ActiveRecord::RecordNotFound, with: lambda{|e| request.format.json? ? record_not_found(e) : raise(e) }
These will have identical impact because if a block is given it assigns it to the options[:with] where as if with: is supplied it uses this as the block and ignores any other block passed to it
rescue_from takes a splat argument called *klasses and a block. It then parses *klasses to determine the options passed in of which it only cares about :with. It then applies the :with block to the key(s) which will represent exception class names to handle.
There is no additional options that will be acknowledged.
Please be advised I have not tested this

You could do:
rescue_from ActiveRecord::RecordNotFound do
record_not_found if json_request?
end
I dont think there is another syntax for that exact case :-(

Related

Need a handler. Pass the with: keyword argument or provide a block

Recently I update my app from Ruby version 2.6.1 to 3.0.1 & I'm using rbenv as a version manager.
but when I try to run the rails server I got an error
=> Booting Puma
=> Rails 6.1.3 application starting in development
=> Run `bin/rails server --help` for more startup options
Exiting
/home/humayun/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/activesupport-6.1.3/lib/active_support/rescuable.rb:56:in `rescue_from': Need a handler. Pass the with: keyword argument or provide a block. (ArgumentError)
from /home/humayun/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/will_paginate-3.1.8/lib/will_paginate/railtie.rb:67:in `rescue_from'
from /home/humayun/umerfarooq/Alchemy/app/controllers/application_controller.rb:2:in `<class:ApplicationController>'
from /home/humayun/umerfarooq/Alchemy/app/controllers/application_controller.rb:1:in `<main>'
I just read Here about the function which is causing an error on line 56.
applciation_controller.rb
rescue_from Exception, with: :handle_exception
protect_from_forgery prepend: true, with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :initialize_api
def not_found
raise ActionController::RoutingError.new('Not Found')
end
def handle_exception(exception = nil)
return render_404 if [ActionController::RoutingError, ActiveRecord::RecordNotFound].include?(exception.class)
render_500
end
I think this is because of depreciation.
can anyone please tell me how to handle these errors?
your handle_exception probably will need a block that render a view or returns a status
as mentioned in the error in app/controllers/application_controller.rb:2 you probably have a rescue_from without an error or a handler you need to follow any of below syntax
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :deny_access # self defined exception
rescue_from ActiveRecord::RecordInvalid, with: :show_errors
rescue_from 'MyAppError::Base' do |exception|
render xml: exception, status: 500
end
private
def deny_access
...
end
def show_errors(exception)
exception.record.new_record? ? ...
end
end
as per documentation here https://apidock.com/rails/ActiveSupport/Rescuable/ClassMethods/rescue_from
------- Update:
This is due to the will_paginate gem that you are using that overrides the rescue_from method in your controller alongside the new ruby updates that change the behavior around the keyword attribute
if your base controller include ControllerRescuePatch you might be able to remove it and this will fix it but not sure what will happen to your pagination.
Otherwise, hold off the ruby update until the will_paginate update their code to fix this

Rails - rescue_from exception handling a exception type differently based on what method the exception is raised from

Rails's :rescue_from takes in a specific exception type and a method as parameter as follow:
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :deny_access # self defined exception
rescue_from ActiveRecord::RecordInvalid, with: :show_errors
rescue_from 'MyAppError::Base' do |exception|
render xml: exception, status: 500
end
protected
def deny_access
...
end
def show_errors(exception)
exception.record.new_record? ? ...
end
end
but this implies that it will deal with the specified exception in the same way ALL ACROSS the controller.
What if I want to handle an exception type differently based on what method the exception is raised from, Example:
class MyController < ActionController::Base
def method_1
# Do Something
rescue MyCustomError => e
handle_exception_for_method_1(e)
end
def method_2
# Do Something
rescue MyCustomError => e
handle_exception_for_method2(e)
end
protected
def handle_exception_for_method_1(exception)
# Do Something
end
def handle_exception_for_method_2(exception)
# Do Something
end
end
I have the following questions:
Can this be done by using :rescue_from as well (with any sort of options to pass in)?
If not, is there any better solution of dealing with this kind of situations?
(Kind of off topic but) Is it a bad practice to handle the same type of error differently in different methods in general?
Rails provides access to the controller and action names through controller_name and action_name methods. You could use this to handle exceptions differently based on the what method the exception was raised.
Example:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordInvalid, with: :show_errors
protected
def show_errors
if action_name == "create"
...
elsif action_name == ...
...
end
end

Undefined method - record_not_found

class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordNotFound, :with => record_not_found #spazzing out
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to record_not_found_path
true
end
end
How is that wrong? When I try and run a spec i get:
in `<class:ApplicationController>': undefined local variable or method `record_not_found' for ApplicationController:Class (NameError)
Am I missing something Oo
In the :with => record_not_found argument to rescue_from, record_not_found has not been defined yet, so it's raising the error. You should be providing a symbol instead, as in:
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
in keeping with the example in http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html#method-i-rescue_from
First the parameter accepted in rescue_from :with => has to be a string or a symbol
Second you should protect a called method with protected to prevent a possible miss usage
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
# parameter for :with has to be a string or symbol
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
# to prevent an external access
protected
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to record_not_found_path
true
end
end

rescue_from ::AbstractController::ActionNotFound not working

I have the following code:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :render_exception
rescue_from ActiveRecord::RecordNotFound, with: :render_exception
rescue_from ActionController::UnknownController, with: :render_exception
rescue_from ::AbstractController::ActionNotFound, with: :render_exception
rescue_from ActiveRecord::ActiveRecordError, with: :render_exception
rescue_from NoMethodError, with: :render_exception
end
They all work flawless, except ::AbstractController::ActionNotFound
I've also tried
AbstractController::ActionNotFound
ActionController::UnknownAction
error:
AbstractController::ActionNotFound (The action 'show' could not be found for ProductsController):
This similar question suggests that you can no longer catch an ActionNotFound exception. Check the link for workarounds. This suggestion to use a Rack middleware to catch 404s looks the cleanest to me.
To rescue AbstractController::ActionNotFound in a controller, you can try something like this:
class UsersController < ApplicationController
private
def process(action, *args)
super
rescue AbstractController::ActionNotFound
respond_to do |format|
format.html { render :404, status: :not_found }
format.all { render nothing: true, status: :not_found }
end
end
public
# actions must not be private
end
This overrides the process method of AbstractController::Base that raises AbstractController::ActionNotFound (see source).
I think we should catch AbstractController::ActionNotFound in ApplicationController. I have tried following that does not appears to be working.
rescue_from ActionController::ActionNotFound, with: :action_not_found
I have found much cleaner way to handle this exception in ApplicationController. To handle the ActionNotFound Exception in your application, you have to override the action_missing method in your application controller.
def action_missing(m, *args, &block)
Rails.logger.error(m)
redirect_to not_found_path # update your application 404 path here
end
Solution Adapted from: coderwall handling exceptions in your rails application
Overriding process, as described by Grégoire in his answer, seems to work. However, the Rails code says to instead override process_action. That doesn't work, though, because process_action never gets called due to the check for action_name in process.
https://github.com/rails/rails/blob/v3.2.21/actionpack/lib/abstract_controller/base.rb#L115
https://github.com/rails/rails/blob/v3.2.21/actionpack/lib/abstract_controller/base.rb#L161

Can I execute a method everytime I get an exception on Rails 3?

I tried almost everything on web, all I want is to call a method whenever an exception like "ActiveRecord::RecordNotFound" or "No route matches" appears.
Rescues from ApplicationController does not work, but why?
class ApplicationController < ActionController::Base
protect_from_forgery
private
def self.send_report_error(message)
Notifier.page_failure(message).deliver
end
rescue ActiveRecord::RecordNotFound
# handle not found error
send_report_error ActiveRecord::RecordNotFound.to_s
rescue ActiveRecord::ActiveRecordError
# handle other ActiveRecord errors
send_report_error ActiveRecord::ActiveRecordError.to_s
rescue # StandardError
# handle most other errors
send_report_error "common error"
rescue Exception
# handle everything else
send_report_error "common exception"
end
Use rescue_from. For example:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :send_report_error
end
http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html

Resources