Exceptions in Ruby on Rails - ruby-on-rails

simple index action in my app
def index
#reports = CustomReport.all
end
when the application was to ready I was told to add exceptions, I copied it from other application as I did not have any idea,
def index
begin
#reports = CustomReport.all
rescue Exception => e
Rails.logger.info "Exception in design_custom_reports controller, index action"
Rails.logger.info e
flash[:notice] = "Sorry, something went wrong. Please inform administration"
redirect_to :controller => :user, :action => :dashboard
end
end
and now it looks clumsy isn't it?
Best way to handle the scenario in RoR?

Short answer
That rescue clause doesn't really make sense. Remove it.
Long answer
That's a very bad code. First of all, the question is: who told you to "add exceptions" and for which purpose?
The code
CustomReport.all
is known to raise an exception only in very special cases. Generally speaking, when the application can't connect to the database. In fact, there are no external user inputs or conditions that may cause the code to naturally fail.
Conversely, this code can fail more frequently
CustomReport.find(params[:id])
because find raises an error if the object is not found, which is definitely a more common case.
If your method crashes for a database error, it's likely the entire application is affected so the rescue management probably makes sense in your global application, not really in that method. There's not that much you can do there to rescue it.
Last but not least, rescuing an exception of class Exception
rescue Exception => e
is considered a code smell in Ruby. In fact, you should rescue only StandardErrors or greater. If you rescue an Exception class you must be very aware of what it means. System level errors and syntax errors inherits from Exception, so if you rescue an Exception it's likely that you will hide a lot of potential errors in your code.
But again, even rescuing StandardError does not make a lot of sense. In fact, we said before that CustomReport.all could only fail in case of database connection errors. This means that, if you really want to rescue something, you should rescue only database failures there.

you can put that code in Application controller . and it will capture all error in your whole application. and you do not need to write any more thing in any other controller as they are already inherited by application controller
rescue_from Exception do |exception|
#error = exception
exception_logger
end
private
def exception_logger
Rails.logger.error "Exception in design_custom_reports controller, index action"
Rails.logger.error #error
flash[:notice] = "Sorry, something went wrong. Please inform administration"
redirect_to :controller => :user, :action => :dashboard
end

Related

How to test with rspec a returned exception?

def show
begin
#cart = Cart.find(params[:id])
authorize(#cart)
#cart_entries = CartEntry.where(:cart_id => #cart.id)
#products = {}
#pr_references = {}
#cart_entries.each do |cart_entry|
#pr_references[cart_entry.id] = Reference.find(cart_entry.reference_id)
#products[cart_entry.id] = Product.find(#pr_references[cart_entry.id].product_id)
end
rescue ActiveRecord::RecordNotFound => e
respond_to do |format|
format.json {render json: {'error': e}, status: :not_found}
end
end
I want to test when Cart.find() doesn't find the cart and I want to test the method return a 404 HTTP code with the test below.
it 'don\'t find cart, should return 404 error status' do
delete :destroy, params: {id: 123, format: 'json'}
expect(response).to have_http_status(404)
end
Have you got some indications or solution to do that ?
I'm a nooby with ruby on rails, if you have some tips with the code I posted I'll take it.
Thank you :)
It seems some other code is raising an exception before your Cart.find statement is executed. For this reason, the ActiveRecord::RecordNotFound exception is never risen, and it is never captured by the rescue block.
Based on the exception that is raising, it seems you are using Pundit gem for dealing with authorization. The authorization rules offered by this gem are surely running before your show method starts. Probably this is happening as a consequence of a before_filter statement, either in this controller or in a parent controller.
You will need to handle this kind of errors in your application. It may be handy to use a rescue_form statement in a base controller that is inherited by all other controllers, so that you don't have to deal with this kind of errors in every controller.

Rails default error page doesn't show on development

I built an error handler that, when there's an error in any controller method in production, reroutes the user to an error page and sends me, the developer, a notification email. This works, but I want the normal error screen to appear when I'm in development. I assumed the code to produce this was just raise e, but instead in development I'm now getting the default production error page (the one that says "We're sorry, but something went wrong".), instead of the detailed error message and trace that used to appear.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error
#error handler
def handle_error(e)
if Rails.env.production?
#code to send email and redirect to error page
else
raise e
end
end
end
I also tried the following:
raise
raise StandardError.new e
raise e, e.message
raise e.message
and if I run any of those in a binding.pry console, they produce the error message I'm looking for, but the error page still just says "We're sorry, but something went wrong."
Anyone know how I can just show the default development error page?
UPDATE
This is insane...so the code to display an error normally should definitely work, but something somewhere is preventing that. If I change config.consider_all_requests_local = true on production, the errors show up on production, but then even if I copy and paste my config/environments/production.rb file into my config/environments/development.rb, the errors still don't show on development. If I enter a pry console, request.local? returns "0", signifying true, and Rails.env returns "development". I have no idea what is going on.
UPDATE 2
Apparently I'm not supposed to be rescuing exceptions on development, but even if I delete every bit of custom error handling code so my Application Controller is just empty, my errors still don't show on development. Further, I have a different app with the same exact error handling code, and for that the errors do show.
Search your code for consider_all_requests_local, it's this configuration that show the full error log.
It must be set as true on your development.rb config file. It's either missing from your configs, or other config is overwriting it
This is not "insane", this is completely expected behavior. You cannot raise from within a rescue_from handler. That would cause an infinite loop.
You also cannot rescue_from StandardError as stated specifically in the documentation:
Using rescue_from with Exception or StandardError would cause serious side-effects as it prevents Rails from handling exceptions properly. As such, it is not recommended to do so unless there is a strong reason.
Instead of conditionally handling the exception inside your rescue_fromhandler, you should conditionally bind the handler, and pick a more specific exception class to handle.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error if Rails.env.production?
#error handler
def handle_error(e)
#code to send email and redirect to error page
end
end
create ErrorsController like below
class ErrorsController < ApplicationController
skip_before_action :login
def not_found
respond_to do |format|
format.html { render status: 404 }
format.json { render json: { error: "Not found" }, status: 404 }
end
end
end

Handling exceptions in rails

I am bit unclear about exception handling while using Active record transactions in rails. I have seen many of them using,
Method: 1
def update
ActiveRecord::Base.transaction do
begin
# Some logic
rescue StandardError => e
raise ActiveRecord::Rollback
end
end
end
and have seen the below logics in many of the places.
Method:2
def update
ActiveRecord::Base.transaction do
if object.update(update_params)
# success
else
# error handling
end
end
rescue => e
# error handling
end
What I think is the second method itself is enough. I thought that, Transaction itself will rollback if anything unexpected happens or any logical error inside the transaction and we can catch them and do whatever we want. Is catching exception inside the transaction and raising Rollback manually needed anywhere?. What is the difference between both the methods and in which case?
You don't need to manually to rollback the transaction the code below should be good enough
def update
ActiveRecord::Base.transaction do
foo.update(foo_update_params)
end
rescue ActiveRecord::RecordInvalid
# Handle your exception here
end
Have a look here for a better explanation.

Rails: Test against and handle ActionController::InvalidAuthenticityToken

I have a Rails web app with some forms users can use to comment etc. They are frequently being used by spammers trying to create spam comments. Nothing new about that.
Sometimes I get CSRF-hack attempts which causes an ActionController::InvalidAuthenticityToken exception. This happens quite a lot and I would like to rescue and send the user/bot to a "You failed to comment"-page.
This has turned out to be a tricky exception to catch though since I can't recreate the error myself. I first put a rescue in the #create when the model (created by the form) was to be saved but it doesn't catch the exception. In order to do so I consider having it cover the entire controller section but that seems over the top.
My two questions:
Is there a way to re-create an ActionController::InvalidAuthenticityToken error by myself, so I can test it?
When is the exception raised? During .save? On Comment.new? Basically, where should I put my begin/rescue?
Thanks!
You can rescue from this exception in your controller. One way you can set this up is to add a rescue in your ApplicationController.
class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_auth_token
private
def record_not_found
render :text => "You failed to comment", :status => 422
end
end
You could also just capture the exception locally in your controller action.
def create
begin
# Create your comment
rescue ActionController::InvalidAuthenticityToken
# Render your last view with some error text.
end
end
You can add raise ActionController::InvalidAuthenticityToken inside your action if you want to test it out.

How do I disable rescue handlers in Ruby on Rails apps when I'm running functional tests?

I have a number of controllers in my Ruby on Rails apps with a rescue handler at the end of the action that basically catches any unhandled errors and returns some kind of "user friendly" error. However, when I'm doing rake test I'd like to have those default rescue handlers disabled so I can see the full error & stack trace. Is there any automated way to do this?
Update to clarify: I have an action like this:
def foo
# do some stuff...
rescue
render :text => "Exception: #{$!}" # this could be any kind of custom render
end
Now when I functional test this, if the exception is raised then I'm going to get just a little bit of info about the exception, but what I'd like is for it to act as though there's no rescue handler there, so I get the full debug information.
Update: SOLUTION
I did this:
rescue:
raise unless Rails.env.production?
render :text => "Exception: #{$!}" # this could be any kind of custom render
end
Not quite automated, but how modifying your code to re-throw exceptions whenever called within a test?
Perhaps something like this:
def foo
# do some stuff...
rescue
raise if ENV["RAILS_ENV"] == "test"
render :text => "Exception: #{$!}" # this could be any kind of custom render
end
Have you looked at using the assert_raise( exception1, exception2, ... ) { block } call and then printing the exception from the block?
Which method are you using? There are two rescue methods in ActionController.
I have this in my base controller:
def rescue_action_in_public(exception)
response_code = response_code_for_rescue(exception)
status = interpret_status(response_code)
respond_to do |format|
format.html { render_optional_error_file response_code}
format.js { render :update, :status => status do |page| page.redirect_to(:url => error_page_url(status)) end}
end
end
This only displays custom errors in production mode.
I think the easiest thing to do is verify that the correct render was called-- or whatever was different from the regular, non-exceptional case.
You shouldn't need to disable your rescue block. Use the assert_raise method (as suggested by Scott), and in the block, call the method that you expect an exception from.
For example:
def test_throws_exception
assert_raise Exception do
raise_if_true(true)
end
end

Resources