diff options
author | Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com> | 2009-05-11 17:07:05 -0700 |
---|---|---|
committer | Yehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com> | 2009-05-11 17:07:05 -0700 |
commit | 00a9d4b91cccdd88146cbe716eca844dcdfa08e7 (patch) | |
tree | 77398a4e98eb391258813fef47f1ecf57ae58972 /actionpack/lib/action_dispatch/middleware/show_exceptions.rb | |
parent | 0f6e764e4060b75ea8a335e6971209a08bf8b40a (diff) | |
parent | 0cac68d3bed3e6bf8ec2eb994858e4a179046941 (diff) | |
download | rails-00a9d4b91cccdd88146cbe716eca844dcdfa08e7.tar.gz rails-00a9d4b91cccdd88146cbe716eca844dcdfa08e7.tar.bz2 rails-00a9d4b91cccdd88146cbe716eca844dcdfa08e7.zip |
Merge branch 'master' into wip_abstract_controller
Conflicts:
actionpack/lib/action_controller/abstract/callbacks.rb
actionpack/lib/action_controller/abstract/renderer.rb
actionpack/lib/action_controller/base/base.rb
actionpack/lib/action_controller/dispatch/dispatcher.rb
actionpack/lib/action_controller/routing/route_set.rb
actionpack/lib/action_controller/testing/process.rb
actionpack/test/abstract_controller/layouts_test.rb
actionpack/test/controller/filters_test.rb
actionpack/test/controller/helper_test.rb
actionpack/test/controller/render_test.rb
actionpack/test/new_base/test_helper.rb
Diffstat (limited to 'actionpack/lib/action_dispatch/middleware/show_exceptions.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb new file mode 100644 index 0000000000..71c1e1b9a9 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -0,0 +1,144 @@ +module ActionDispatch + class ShowExceptions + include StatusCodes + + LOCALHOST = '127.0.0.1'.freeze + + DEFAULT_RESCUE_RESPONSE = :internal_server_error + DEFAULT_RESCUE_RESPONSES = { + 'ActionController::RoutingError' => :not_found, + 'ActionController::UnknownAction' => :not_found, + 'ActiveRecord::RecordNotFound' => :not_found, + 'ActiveRecord::StaleObjectError' => :conflict, + 'ActiveRecord::RecordInvalid' => :unprocessable_entity, + 'ActiveRecord::RecordNotSaved' => :unprocessable_entity, + 'ActionController::MethodNotAllowed' => :method_not_allowed, + 'ActionController::NotImplemented' => :not_implemented, + 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity + } + + DEFAULT_RESCUE_TEMPLATE = 'diagnostics' + DEFAULT_RESCUE_TEMPLATES = { + 'ActionView::MissingTemplate' => 'missing_template', + 'ActionController::RoutingError' => 'routing_error', + 'ActionController::UnknownAction' => 'unknown_action', + 'ActionView::TemplateError' => 'template_error' + } + + RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') + + cattr_accessor :rescue_responses + @@rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE) + @@rescue_responses.update DEFAULT_RESCUE_RESPONSES + + cattr_accessor :rescue_templates + @@rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE) + @@rescue_templates.update DEFAULT_RESCUE_TEMPLATES + + def initialize(app, consider_all_requests_local = false) + @app = app + @consider_all_requests_local = consider_all_requests_local + end + + def call(env) + @app.call(env) + rescue Exception => exception + raise exception if env['rack.test'] + + log_error(exception) if logger + + request = Request.new(env) + if @consider_all_requests_local || local_request?(request) + rescue_action_locally(request, exception) + else + rescue_action_in_public(exception) + end + end + + private + # Render detailed diagnostics for unhandled exceptions rescued from + # a controller action. + def rescue_action_locally(request, exception) + template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], + :template => template, + :request => request, + :exception => exception + ) + file = "rescues/#{@@rescue_templates[exception.class.name]}.erb" + body = template.render(:file => file, :layout => 'rescues/layout.erb') + + headers = {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s} + status = status_code(exception) + + [status, headers, body] + end + + # Attempts to render a static error page based on the + # <tt>status_code</tt> thrown, or just return headers if no such file + # exists. At first, it will try to render a localized static page. + # For example, if a 500 error is being handled Rails and locale is :da, + # it will first attempt to render the file at <tt>public/500.da.html</tt> + # then attempt to render <tt>public/500.html</tt>. If none of them exist, + # the body of the response will be left empty. + def rescue_action_in_public(exception) + status = status_code(exception) + locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale + path = "#{public_path}/#{status}.html" + + if locale_path && File.exist?(locale_path) + render_public_file(status, locale_path) + elsif File.exist?(path) + render_public_file(status, path) + else + [status, {'Content-Type' => 'text/html', 'Content-Length' => '0'}, []] + end + end + + # True if the request came from localhost, 127.0.0.1. + def local_request?(request) + request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST + end + + def render_public_file(status, path) + body = File.read(path) + [status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, body] + end + + def status_code(exception) + interpret_status(@@rescue_responses[exception.class.name]).to_i + end + + def public_path + if defined?(Rails) + Rails.public_path + else + "public" + end + end + + def log_error(exception) #:doc: + ActiveSupport::Deprecation.silence do + if ActionView::TemplateError === exception + logger.fatal(exception.to_s) + else + logger.fatal( + "\n#{exception.class} (#{exception.message}):\n " + + clean_backtrace(exception).join("\n ") + "\n\n" + ) + end + end + end + + def clean_backtrace(exception) + defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ? + Rails.backtrace_cleaner.clean(exception.backtrace) : + exception.backtrace + end + + def logger + if defined?(Rails.logger) + Rails.logger + end + end + end +end |