How does Rails handle exceptions ?
Rails exception handling depends on two factors and we are going to discuss both of them here.
1# ~/lib/action_controller/rescue.rb 2if consider_all_requests_local || local_request? 3 rescue_action_locally(exception) 4else 5 rescue_action_in_public(exception) 6end
When exceptions are handled by rescue_action_locally then we get to see the page with stacktrace. When exceptions are handled by rescue_action_in_public, we get to see the public/500.html or an error page matching the error code.
As you can see Rails uses two different methods consider_all_requests_local and local_request? to decide how exception should be handled.
consider_all_requests_local is a class level variable for ActionController::Base . We hardly pay attention to it but it is configured through files residing in config/environments
1# config/environments/development.rb 2config.action_controller.consider_all_requests_local = true 3 4# config/environments/production.rb 5config.action_controller.consider_all_requests_local = false
As you can see in development environment all the requests are considered local.
I have overridden the method local_request? but I am still not able to see public page when exception is raised.
That is a common question I see in the mailing list. As you can see the condition to decide how to handle exception is
1if consider_all_requests_local || local_request?
In development environment consider_all_requests_local is always true as I showed before. Since one of the conditions is true Rails always handles the exception using rescue_action_locally .
I am running in production mode but I am still not able to see public/500.html page when I get exception at http://localhost:3000.
Same issue. In this case you are running in production mode so consider_all_requests_local is false but local_request? is still true because of localhost.
I want local_request? to be environment dependent
Recently I started using hoptoad and I needed to test how hoptoad will handle exception in production mode. However without any change local_request? was always returning true for http://localhost:30000 .
Then I stick following file under config/initializers
1# config/initializers/local_request_override.rb 2module CustomRescue 3 def local_request? 4 return false if Rails.env.production? || Rails.env.staging? 5 super 6 end 7end 8 9ActionController::Base.class_eval do 10 include CustomRescue 11end
Now all request in production or in staging mode are treated as NOT local.
Now in both staging and production mode I get to see 500.html page even if I am accessing the application from http://localhost:3000 .