Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic error pages in Rails 3

In Rails 2.3.x, you can override render_optional_error_file like so:

# ApplicationController.rb protected   def render_optional_error_file(status_code)     render :template => "errors/500", :status => 500, :layout => 'application'   end 

However, Rails 3 no longer has render_optional_error_file. Instead, you need to override rescue_action_in_public, which you can do like so:

# config/initializers/error_page.rb module ActionDispatch   class ShowExceptions      protected           def rescue_action_in_public(exception)         status = status_code(exception).to_s          template = ActionView::Base.new(["#{Rails.root}/app/views"])         if ["404"].include?(status)           file = "/errors/404.html.erb"         else           file = "/errors/500.html.erb"         end                 body = template.render(:file => file)          render(status, body)       end    end end 

This works, but does not use the application's layout. However, if you specify the layout path like so:

body = template.render(:file => file, :layout => "layouts/application") # line 15 

You get an Error during failsafe response: ActionView::Template::Error.

Line 4 of application.html.erb:4 is:

<%= stylesheet_link_tag "app", "jquery-ui", "jquery.fancybox", :cache => "all" %> 

Whatever ActionView normally uses to render templates isn't getting loaded.

The stack trace is:

  /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:794:in `join'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:794:in `rails_asset_id'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:817:in `rewrite_asset_path'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:746:in `compute_public_path'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:424:in `path_to_stylesheet'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:875:in `ensure_stylesheet_sources!'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:874:in `each'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:874:in `ensure_stylesheet_sources!'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/helpers/asset_tag_helper.rb:512:in `stylesheet_link_tag'   /data/sites/fundraisers-stage/releases/20110316194843/app/views/layouts/application.html.erb:4:in `_app_views_layouts_application_html_erb___19482063_70294907435920_0'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/template.rb:135:in `send'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/template.rb:135:in `render'   /var/lib/gems/1.8/gems/activesupport-3.0.5/lib/active_support/notifications.rb:54:in `instrument'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/template.rb:127:in `render'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/render/layouts.rb:80:in `_render_layout'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/render/rendering.rb:62:in `_render_template'   /var/lib/gems/1.8/gems/activesupport-3.0.5/lib/active_support/notifications.rb:52:in `instrument'   /var/lib/gems/1.8/gems/activesupport-3.0.5/lib/active_support/notifications/instrumenter.rb:21:in `instrument'   /var/lib/gems/1.8/gems/activesupport-3.0.5/lib/active_support/notifications.rb:52:in `instrument'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/render/rendering.rb:56:in `_render_template'   /var/lib/gems/1.8/gems/actionpack-3.0.5/lib/action_view/render/rendering.rb:26:in `render'   /data/sites/fundraisers-stage/releases/20110316194843/config/initializers/error_pages.rb:15:in `rescue_action_in_public' 
like image 856
Paul Schreiber Avatar asked Mar 16 '11 19:03

Paul Schreiber


2 Answers

I would suggest using rescue_from instead. You would just rescue from specific errors rather than overriding rescue_action_in_public. This is especially useful when dealing with user-defined errors or controller-specific errors.

# ApplicationController rescue_from ActionController::RoutingError, :with => :render_404 rescue_from ActionController::UnknownAction, :with => :render_404 rescue_from ActiveRecord::RecordNotFound, :with => :render_404 rescue_from MyApp::CustomError, :with => :custom_error_resolution  def render_404   if /(jpe?g|png|gif)/i === request.path     render :text => "404 Not Found", :status => 404   else     render :template => "shared/404", :layout => 'application', :status => 404   end end  # UsersController rescue_from MyApp::SomeReallySpecificUserError, :with => :user_controller_resolution 

http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html

like image 34
mnelson Avatar answered Oct 05 '22 01:10

mnelson


In rails 3.2, it's easier than that:

Add this to config/application.rb:

config.exceptions_app = self.routes 

That causes errors to be routed via the router. Then you just add to config/routes.rb:

match "/404", :to => "errors#not_found" 

I got this info from item #3 on the blog post "My five favorite hidden features in Rails 3.2" by By José Valim.

like image 128
docwhat Avatar answered Oct 05 '22 01:10

docwhat