From b4359bc7234b61c9a4a104542fa77f63bb84d7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 19:16:19 +0100 Subject: Allow rescue responses to be configured through a railtie. --- actionpack/CHANGELOG.md | 2 ++ .../action_dispatch/middleware/show_exceptions.rb | 12 +++------ actionpack/lib/action_dispatch/railtie.rb | 13 +++++++++- activerecord/lib/active_record/railtie.rb | 7 ++++++ .../application/middleware/show_exceptions_test.rb | 29 ++++++++++++++++++++++ railties/test/application/middleware_test.rb | 22 +--------------- 6 files changed, 55 insertions(+), 30 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 26c4703150..51e377b084 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 3.2.0 (unreleased) ## +* Allow rescue responses to be configured through a railtie as in `config.action_dispatch.rescue_responses`. Please look at ActiveRecord::Railtie for an example. *José Valim* + * Assets should use the request protocol by default or default to relative if no request is available *Jonathan del Strother* * Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts *José Valim* diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index c850e25507..420cd99ca1 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -12,26 +12,22 @@ module ActionDispatch cattr_accessor :rescue_responses @@rescue_responses = Hash.new(:internal_server_error) - @@rescue_responses.update({ + @@rescue_responses.merge!( 'ActionController::RoutingError' => :not_found, 'AbstractController::ActionNotFound' => :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 - }) + ) cattr_accessor :rescue_templates @@rescue_templates = Hash.new('diagnostics') - @@rescue_templates.update({ + @@rescue_templates.merge!( 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', 'AbstractController::ActionNotFound' => 'unknown_action', 'ActionView::Template::Error' => 'template_error' - }) + ) FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, ["

500 Internal Server Error

" << diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index f18ebabf29..36a31be0c2 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -9,11 +9,22 @@ module ActionDispatch config.action_dispatch.best_standards_support = true config.action_dispatch.tld_length = 1 config.action_dispatch.ignore_accept_header = false - config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true} + config.action_dispatch.rescue_templates = { } + config.action_dispatch.rescue_responses = { } + + config.action_dispatch.rack_cache = { + :metastore => "rails:/", + :entitystore => "rails:/", + :verbose => true + } + initializer "action_dispatch.configure" do |app| ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header + ActionDispatch::ShowExceptions.rescue_responses.merge!(config.action_dispatch.rescue_responses) + ActionDispatch::ShowExceptions.rescue_templates.merge!(config.action_dispatch.rescue_templates) + config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil? ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 47133e77e8..c2e31579a4 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -22,6 +22,13 @@ module ActiveRecord config.app_middleware.insert_after "::ActionDispatch::Callbacks", "ActiveRecord::ConnectionAdapters::ConnectionManagement" + config.action_dispatch.rescue_responses.merge!( + 'ActiveRecord::RecordNotFound' => :not_found, + 'ActiveRecord::StaleObjectError' => :conflict, + 'ActiveRecord::RecordInvalid' => :unprocessable_entity, + 'ActiveRecord::RecordNotSaved' => :unprocessable_entity + ) + rake_tasks do load "active_record/railties/databases.rake" end diff --git a/railties/test/application/middleware/show_exceptions_test.rb b/railties/test/application/middleware/show_exceptions_test.rb index 7dbadc6ce3..f3cae6a11e 100644 --- a/railties/test/application/middleware/show_exceptions_test.rb +++ b/railties/test/application/middleware/show_exceptions_test.rb @@ -16,6 +16,35 @@ module ApplicationTests teardown_app end + test "show exceptions middleware filter backtrace before logging" do + my_middleware = Struct.new(:app) do + def call(env) + raise "Failure" + end + end + + app.config.middleware.use my_middleware + + stringio = StringIO.new + Rails.logger = Logger.new(stringio) + + get "/" + assert_no_match(/action_dispatch/, stringio.string) + end + + test "renders active record exceptions as 404" do + my_middleware = Struct.new(:app) do + def call(env) + raise ActiveRecord::RecordNotFound + end + end + + app.config.middleware.use my_middleware + + get "/" + assert_equal 404, last_response.status + end + test "unspecified route when set action_dispatch.show_exceptions to false" do app.config.action_dispatch.show_exceptions = false diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 4703a59326..dba79bfd96 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -104,7 +104,7 @@ module ApplicationTests assert !middleware.include?("ActionDispatch::Static") end - test "includes show exceptions even action_dispatch.show_exceptions is disabled" do + test "includes show exceptions even if action_dispatch.show_exceptions is disabled" do add_to_config "config.action_dispatch.show_exceptions = false" boot! assert middleware.include?("ActionDispatch::ShowExceptions") @@ -191,26 +191,6 @@ module ApplicationTests assert_equal nil, last_response.headers["Etag"] end - # Show exceptions middleware - test "show exceptions middleware filter backtrace before logging" do - my_middleware = Struct.new(:app) do - def call(env) - raise "Failure" - end - end - - make_basic_app do |app| - app.config.middleware.use my_middleware - end - - stringio = StringIO.new - Rails.logger = Logger.new(stringio) - - env = Rack::MockRequest.env_for("/") - Rails.application.call(env) - assert_no_match(/action_dispatch/, stringio.string) - end - private def boot! -- cgit v1.2.3 From 0b677b18ff75cd4bf8fd0b2f9579cd84ca3dc2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 20:02:00 +0100 Subject: Add an ExceptionWrapper that wraps an exception and provide convenience helpers. --- actionpack/lib/action_controller/log_subscriber.rb | 2 +- actionpack/lib/action_dispatch.rb | 1 + .../middleware/exception_wrapper.rb | 77 +++++++++++++++++ .../action_dispatch/middleware/show_exceptions.rb | 97 +++++----------------- actionpack/lib/action_dispatch/railtie.rb | 4 +- 5 files changed, 104 insertions(+), 77 deletions(-) create mode 100644 actionpack/lib/action_dispatch/middleware/exception_wrapper.rb diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 8666ecc9c8..de250053d3 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -20,7 +20,7 @@ module ActionController status = payload[:status] if status.nil? && payload[:exception].present? - status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil + status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code) end message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration message << " (#{additions.join(" | ")})" unless additions.blank? diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 1e92d14542..1f403b5e1a 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -51,6 +51,7 @@ module ActionDispatch autoload :BestStandardsSupport autoload :Callbacks autoload :Cookies + autoload :ExceptionWrapper autoload :Flash autoload :Head autoload :ParamsParser diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb new file mode 100644 index 0000000000..96598b529d --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -0,0 +1,77 @@ +require 'active_support/core_ext/exception' + +module ActionDispatch + class ExceptionWrapper + cattr_accessor :rescue_responses + @@rescue_responses = Hash.new(:internal_server_error) + @@rescue_responses.merge!( + 'ActionController::RoutingError' => :not_found, + 'AbstractController::ActionNotFound' => :not_found, + 'ActionController::MethodNotAllowed' => :method_not_allowed, + 'ActionController::NotImplemented' => :not_implemented, + 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity + ) + + cattr_accessor :rescue_templates + @@rescue_templates = Hash.new('diagnostics') + @@rescue_templates.merge!( + 'ActionView::MissingTemplate' => 'missing_template', + 'ActionController::RoutingError' => 'routing_error', + 'AbstractController::ActionNotFound' => 'unknown_action', + 'ActionView::Template::Error' => 'template_error' + ) + + attr_reader :env, :exception + + def initialize(env, exception) + @env = env + @exception = original_exception(exception) + end + + def rescue_template + @@rescue_templates[@exception.class.name] + end + + def status_code + Rack::Utils.status_code(@@rescue_responses[@exception.class.name]) + end + + def application_trace + clean_backtrace(:silent) + end + + def framework_trace + clean_backtrace(:noise) + end + + def full_trace + clean_backtrace(:all) + end + + private + + def original_exception(exception) + if registered_original_exception?(exception) + exception.original_exception + else + exception + end + end + + def registered_original_exception?(exception) + exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name) + end + + def clean_backtrace(*args) + if backtrace_cleaner + backtrace_cleaner.clean(@exception.backtrace, *args) + else + @exception.backtrace + end + end + + def backtrace_cleaner + @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner'] + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 420cd99ca1..d5ed89ee9f 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -1,7 +1,6 @@ -require 'active_support/core_ext/exception' require 'action_controller/metal/exceptions' -require 'active_support/notifications' require 'action_dispatch/http/request' +require 'action_dispatch/middleware/exception_wrapper' require 'active_support/deprecation' module ActionDispatch @@ -10,25 +9,6 @@ module ActionDispatch class ShowExceptions RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') - cattr_accessor :rescue_responses - @@rescue_responses = Hash.new(:internal_server_error) - @@rescue_responses.merge!( - 'ActionController::RoutingError' => :not_found, - 'AbstractController::ActionNotFound' => :not_found, - 'ActionController::MethodNotAllowed' => :method_not_allowed, - 'ActionController::NotImplemented' => :not_implemented, - 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity - ) - - cattr_accessor :rescue_templates - @@rescue_templates = Hash.new('diagnostics') - @@rescue_templates.merge!( - 'ActionView::MissingTemplate' => 'missing_template', - 'ActionController::RoutingError' => 'routing_error', - 'AbstractController::ActionNotFound' => 'unknown_action', - 'ActionView::Template::Error' => 'template_error' - ) - FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, ["

500 Internal Server Error

" << "If you are the administrator of this website, then please read this web " << @@ -59,13 +39,13 @@ module ActionDispatch private def render_exception(env, exception) - log_error(env, exception) - exception = original_exception(exception) + wrapper = ExceptionWrapper.new(env, exception) + log_error(env, wrapper) if env['action_dispatch.show_detailed_exceptions'] == true - rescue_action_diagnostics(env, exception) + rescue_action_diagnostics(wrapper) else - rescue_action_error_page(exception) + rescue_action_error_page(wrapper) end rescue Exception => failsafe_error $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" @@ -74,17 +54,17 @@ module ActionDispatch # Render detailed diagnostics for unhandled exceptions rescued from # a controller action. - def rescue_action_diagnostics(env, exception) + def rescue_action_diagnostics(wrapper) template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], - :request => Request.new(env), - :exception => exception, - :application_trace => application_trace(env, exception), - :framework_trace => framework_trace(env, exception), - :full_trace => full_trace(env, exception) + :request => Request.new(wrapper.env), + :exception => wrapper.exception, + :application_trace => wrapper.application_trace, + :framework_trace => wrapper.framework_trace, + :full_trace => wrapper.full_trace ) - file = "rescues/#{@@rescue_templates[exception.class.name]}" + file = "rescues/#{wrapper.rescue_template}" body = template.render(:template => file, :layout => 'rescues/layout') - render(status_code(exception), body) + render(wrapper.status_code, body) end # Attempts to render a static error page based on the @@ -94,8 +74,8 @@ module ActionDispatch # it will first attempt to render the file at public/500.da.html # then attempt to render public/500.html. If none of them exist, # the body of the response will be left empty. - def rescue_action_error_page(exception) - status = status_code(exception) + def rescue_action_error_page(wrapper) + status = wrapper.status_code locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale path = "#{public_path}/#{status}.html" @@ -108,10 +88,6 @@ module ActionDispatch end end - def status_code(exception) - Rack::Utils.status_code(@@rescue_responses[exception.class.name]) - end - def render(status, body) [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] end @@ -120,53 +96,26 @@ module ActionDispatch defined?(Rails.public_path) ? Rails.public_path : 'public_path' end - def log_error(env, exception) - return unless logger(env) + def log_error(env, wrapper) + logger = logger(env) + return unless logger + + exception = wrapper.exception ActiveSupport::Deprecation.silence do message = "\n#{exception.class} (#{exception.message}):\n" message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) - message << " " << application_trace(env, exception).join("\n ") - logger(env).fatal("#{message}\n\n") + message << " " << wrapper.application_trace.join("\n ") + logger.fatal("#{message}\n\n") end end - def application_trace(env, exception) - clean_backtrace(env, exception, :silent) - end - - def framework_trace(env, exception) - clean_backtrace(env, exception, :noise) - end - - def full_trace(env, exception) - clean_backtrace(env, exception, :all) - end - - def clean_backtrace(env, exception, *args) - env['action_dispatch.backtrace_cleaner'] ? - env['action_dispatch.backtrace_cleaner'].clean(exception.backtrace, *args) : - exception.backtrace - end - def logger(env) env['action_dispatch.logger'] || stderr_logger end def stderr_logger - Logger.new($stderr) - end - - def original_exception(exception) - if registered_original_exception?(exception) - exception.original_exception - else - exception + @stderr_logger ||= Logger.new($stderr) end - end - - def registered_original_exception?(exception) - exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name) - end end end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 36a31be0c2..2ba3198ebd 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -22,8 +22,8 @@ module ActionDispatch ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header - ActionDispatch::ShowExceptions.rescue_responses.merge!(config.action_dispatch.rescue_responses) - ActionDispatch::ShowExceptions.rescue_templates.merge!(config.action_dispatch.rescue_templates) + ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses) + ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates) config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil? ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie -- cgit v1.2.3 From 956ecff8337908d7c3908977a5c8e12296a5b576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 20:16:12 +0100 Subject: Add a deprecation to old show exceptions API (even though it was not public). --- .../lib/action_dispatch/middleware/show_exceptions.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index d5ed89ee9f..aa7a5e4d64 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -15,6 +15,20 @@ module ActionDispatch "application's log file and/or the web server's log file to find out what " << "went wrong."]] + class << self + def rescue_responses + ActiveSupport::Deprecation.warn "ActionDispatch::ShowExceptions.rescue_responses is deprecated. " \ + "Please configure your exceptions using a railtie or in your application config instead." + ExceptionWrapper.rescue_responses + end + + def rescue_templates + ActiveSupport::Deprecation.warn "ActionDispatch::ShowExceptions.rescue_templates is deprecated. " \ + "Please configure your exceptions using a railtie or in your application config instead." + ExceptionWrapper.rescue_templates + end + end + def initialize(app, consider_all_requests_local = nil) ActiveSupport::Deprecation.warn "Passing consider_all_requests_local option to ActionDispatch::ShowExceptions middleware no longer works" unless consider_all_requests_local.nil? @app = app -- cgit v1.2.3 From 750bb5c865ac9234da91ec451eec7d9de55b8f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 20:46:18 +0100 Subject: Split ShowExceptions responsibilities in two middlewares. --- actionpack/lib/action_dispatch.rb | 1 + .../action_dispatch/middleware/debug_exceptions.rb | 77 +++++++++++++++ .../middleware/exception_wrapper.rb | 1 + .../action_dispatch/middleware/show_exceptions.rb | 110 ++++++--------------- actionpack/test/abstract_unit.rb | 22 +++-- actionpack/test/controller/show_exceptions_test.rb | 1 + actionpack/test/dispatch/show_exceptions_test.rb | 4 +- railties/lib/rails/application.rb | 3 + .../test/application/middleware/exceptions_test.rb | 89 +++++++++++++++++ .../application/middleware/show_exceptions_test.rb | 89 ----------------- railties/test/application/middleware_test.rb | 4 +- 11 files changed, 218 insertions(+), 183 deletions(-) create mode 100644 actionpack/lib/action_dispatch/middleware/debug_exceptions.rb create mode 100644 railties/test/application/middleware/exceptions_test.rb delete mode 100644 railties/test/application/middleware/show_exceptions_test.rb diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 1f403b5e1a..89f515ee3d 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -51,6 +51,7 @@ module ActionDispatch autoload :BestStandardsSupport autoload :Callbacks autoload :Cookies + autoload :DebugExceptions autoload :ExceptionWrapper autoload :Flash autoload :Head diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb new file mode 100644 index 0000000000..5da21ddd3d --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -0,0 +1,77 @@ +require 'action_dispatch/http/request' +require 'action_dispatch/middleware/exception_wrapper' + +module ActionDispatch + # This middleware is responsible for logging exceptions and + # showing a debugging page in case the request is local. + class DebugExceptions + RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') + + def initialize(app) + @app = app + end + + def call(env) + begin + response = @app.call(env) + + if response[1]['X-Cascade'] == 'pass' + raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}" + end + rescue Exception => exception + raise exception if env['action_dispatch.show_exceptions'] == false + end + + response ? response : render_exception(env, exception) + end + + private + + def render_exception(env, exception) + wrapper = ExceptionWrapper.new(env, exception) + log_error(env, wrapper) + + if env['action_dispatch.show_detailed_exceptions'] + template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], + :request => Request.new(env), + :exception => wrapper.exception, + :application_trace => wrapper.application_trace, + :framework_trace => wrapper.framework_trace, + :full_trace => wrapper.full_trace + ) + + file = "rescues/#{wrapper.rescue_template}" + body = template.render(:template => file, :layout => 'rescues/layout') + render(wrapper.status_code, body) + else + raise exception + end + end + + def render(status, body) + [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] + end + + def log_error(env, wrapper) + logger = logger(env) + return unless logger + + exception = wrapper.exception + + ActiveSupport::Deprecation.silence do + message = "\n#{exception.class} (#{exception.message}):\n" + message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) + message << " " << wrapper.application_trace.join("\n ") + logger.fatal("#{message}\n\n") + end + end + + def logger(env) + env['action_dispatch.logger'] || stderr_logger + end + + def stderr_logger + @stderr_logger ||= Logger.new($stderr) + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 96598b529d..c0532c80c4 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -1,3 +1,4 @@ +require 'action_controller/metal/exceptions' require 'active_support/core_ext/exception' module ActionDispatch diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index aa7a5e4d64..346770acb7 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -1,4 +1,3 @@ -require 'action_controller/metal/exceptions' require 'action_dispatch/http/request' require 'action_dispatch/middleware/exception_wrapper' require 'active_support/deprecation' @@ -36,100 +35,47 @@ module ActionDispatch def call(env) begin - status, headers, body = @app.call(env) - exception = nil - - # Only this middleware cares about RoutingError. So, let's just raise - # it here. - if headers['X-Cascade'] == 'pass' - raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}" - end + response = @app.call(env) rescue Exception => exception raise exception if env['action_dispatch.show_exceptions'] == false end - exception ? render_exception(env, exception) : [status, headers, body] + response ? response : render_exception(env, exception) end private - def render_exception(env, exception) - wrapper = ExceptionWrapper.new(env, exception) - log_error(env, wrapper) - if env['action_dispatch.show_detailed_exceptions'] == true - rescue_action_diagnostics(wrapper) - else - rescue_action_error_page(wrapper) - end - rescue Exception => failsafe_error - $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" - FAILSAFE_RESPONSE - end + def render_exception(env, exception) + wrapper = ExceptionWrapper.new(env, exception) - # Render detailed diagnostics for unhandled exceptions rescued from - # a controller action. - def rescue_action_diagnostics(wrapper) - template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], - :request => Request.new(wrapper.env), - :exception => wrapper.exception, - :application_trace => wrapper.application_trace, - :framework_trace => wrapper.framework_trace, - :full_trace => wrapper.full_trace - ) - file = "rescues/#{wrapper.rescue_template}" - body = template.render(:template => file, :layout => 'rescues/layout') - render(wrapper.status_code, body) - end + status = wrapper.status_code + locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale + path = "#{public_path}/#{status}.html" - # Attempts to render a static error page based on the - # status_code 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 public/500.da.html - # then attempt to render public/500.html. If none of them exist, - # the body of the response will be left empty. - def rescue_action_error_page(wrapper) - status = wrapper.status_code - locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale - path = "#{public_path}/#{status}.html" - - if locale_path && File.exist?(locale_path) - render(status, File.read(locale_path)) - elsif File.exist?(path) - render(status, File.read(path)) - else - render(status, '') - end + if locale_path && File.exist?(locale_path) + render(status, File.read(locale_path)) + elsif File.exist?(path) + render(status, File.read(path)) + else + render(status, '') end + rescue Exception => failsafe_error + render_failsafe(failsafe_error) + end - def render(status, body) - [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] - end - - def public_path - defined?(Rails.public_path) ? Rails.public_path : 'public_path' - end - - def log_error(env, wrapper) - logger = logger(env) - return unless logger - - exception = wrapper.exception - - ActiveSupport::Deprecation.silence do - message = "\n#{exception.class} (#{exception.message}):\n" - message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) - message << " " << wrapper.application_trace.join("\n ") - logger.fatal("#{message}\n\n") - end - end + def render_failsafe(failsafe_error) + $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" + FAILSAFE_RESPONSE + end - def logger(env) - env['action_dispatch.logger'] || stderr_logger - end + def render(status, body) + [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]] + end - def stderr_logger - @stderr_logger ||= Logger.new($stderr) - end + # TODO: Make this a middleware initialization parameter once + # we deprecated the second option + def public_path + defined?(Rails.public_path) ? Rails.public_path : 'public_path' + end end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index d191a203dd..c95b8221a1 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -175,6 +175,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase def self.build_app(routes = nil) RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| middleware.use "ActionDispatch::ShowExceptions" + middleware.use "ActionDispatch::DebugExceptions" middleware.use "ActionDispatch::Callbacks" middleware.use "ActionDispatch::ParamsParser" middleware.use "ActionDispatch::Cookies" @@ -338,16 +339,19 @@ end module ActionDispatch class ShowExceptions private - remove_method :public_path - def public_path - "#{FIXTURE_LOAD_PATH}/public" - end + remove_method :public_path + def public_path + "#{FIXTURE_LOAD_PATH}/public" + end + end - remove_method :stderr_logger - # Silence logger - def stderr_logger - nil - end + class DebugExceptions + private + remove_method :stderr_logger + # Silence logger + def stderr_logger + nil + end end end diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 74067cb895..5eff1eb09d 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -3,6 +3,7 @@ require 'abstract_unit' module ShowExceptions class ShowExceptionsController < ActionController::Base use ActionDispatch::ShowExceptions + use ActionDispatch::DebugExceptions def boom raise 'boom!' diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 90f13a3bb9..4f1140b5d3 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -29,8 +29,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end end - ProductionApp = ActionDispatch::ShowExceptions.new(Boomer.new(false)) - DevelopmentApp = ActionDispatch::ShowExceptions.new(Boomer.new(true)) + ProductionApp = ActionDispatch::ShowExceptions.new(ActionDispatch::DebugExceptions.new(Boomer.new(false))) + DevelopmentApp = ActionDispatch::ShowExceptions.new(ActionDispatch::DebugExceptions.new(Boomer.new(true))) test "rescue with error page when show_exceptions is false" do @app = ProductionApp diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 2a62446a04..f5f47acfbc 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -195,10 +195,13 @@ module Rails middleware.use ::ActionDispatch::RequestId middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods middleware.use ::ActionDispatch::ShowExceptions + middleware.use ::ActionDispatch::DebugExceptions middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies + if config.action_dispatch.x_sendfile_header.present? middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header end + middleware.use ::ActionDispatch::Reloader unless config.cache_classes middleware.use ::ActionDispatch::Callbacks middleware.use ::ActionDispatch::Cookies diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb new file mode 100644 index 0000000000..0174352900 --- /dev/null +++ b/railties/test/application/middleware/exceptions_test.rb @@ -0,0 +1,89 @@ +# encoding: utf-8 +require 'isolation/abstract_unit' +require 'rack/test' + +module ApplicationTests + class MiddlewareExceptionsTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + def setup + build_app + boot_rails + end + + def teardown + teardown_app + end + + test "show exceptions middleware filter backtrace before logging" do + my_middleware = Struct.new(:app) do + def call(env) + raise "Failure" + end + end + + app.config.middleware.use my_middleware + + stringio = StringIO.new + Rails.logger = Logger.new(stringio) + + get "/" + assert_no_match(/action_dispatch/, stringio.string) + end + + test "renders active record exceptions as 404" do + my_middleware = Struct.new(:app) do + def call(env) + raise ActiveRecord::RecordNotFound + end + end + + app.config.middleware.use my_middleware + + get "/" + assert_equal 404, last_response.status + end + + test "unspecified route when set action_dispatch.show_exceptions to false" do + app.config.action_dispatch.show_exceptions = false + + assert_raise(ActionController::RoutingError) do + get '/foo' + end + end + + test "unspecified route when set action_dispatch.show_exceptions to true" do + app.config.action_dispatch.show_exceptions = true + + assert_nothing_raised(ActionController::RoutingError) do + get '/foo' + end + end + + test "displays diagnostics message when exception raised in template that contains UTF-8" do + app.config.action_dispatch.show_exceptions = true + + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + end + end + RUBY + + app_file 'app/views/foo/index.html.erb', <<-ERB + <% raise 'boooom' %> + ✓ + ERB + + app_file 'config/routes.rb', <<-RUBY + AppTemplate::Application.routes.draw do + match ':controller(/:action)' + end + RUBY + + post '/foo', :utf8 => '✓' + assert_match(/boooom/, last_response.body) + end + end +end diff --git a/railties/test/application/middleware/show_exceptions_test.rb b/railties/test/application/middleware/show_exceptions_test.rb deleted file mode 100644 index f3cae6a11e..0000000000 --- a/railties/test/application/middleware/show_exceptions_test.rb +++ /dev/null @@ -1,89 +0,0 @@ -# encoding: utf-8 -require 'isolation/abstract_unit' -require 'rack/test' - -module ApplicationTests - class ShowExceptionsTest < Test::Unit::TestCase - include ActiveSupport::Testing::Isolation - include Rack::Test::Methods - - def setup - build_app - boot_rails - end - - def teardown - teardown_app - end - - test "show exceptions middleware filter backtrace before logging" do - my_middleware = Struct.new(:app) do - def call(env) - raise "Failure" - end - end - - app.config.middleware.use my_middleware - - stringio = StringIO.new - Rails.logger = Logger.new(stringio) - - get "/" - assert_no_match(/action_dispatch/, stringio.string) - end - - test "renders active record exceptions as 404" do - my_middleware = Struct.new(:app) do - def call(env) - raise ActiveRecord::RecordNotFound - end - end - - app.config.middleware.use my_middleware - - get "/" - assert_equal 404, last_response.status - end - - test "unspecified route when set action_dispatch.show_exceptions to false" do - app.config.action_dispatch.show_exceptions = false - - assert_raise(ActionController::RoutingError) do - get '/foo' - end - end - - test "unspecified route when set action_dispatch.show_exceptions to true" do - app.config.action_dispatch.show_exceptions = true - - assert_nothing_raised(ActionController::RoutingError) do - get '/foo' - end - end - - test "displays diagnostics message when exception raised in template that contains UTF-8" do - app.config.action_dispatch.show_exceptions = true - - controller :foo, <<-RUBY - class FooController < ActionController::Base - def index - end - end - RUBY - - app_file 'app/views/foo/index.html.erb', <<-ERB - <% raise 'boooom' %> - ✓ - ERB - - app_file 'config/routes.rb', <<-RUBY - AppTemplate::Application.routes.draw do - match ':controller(/:action)' - end - RUBY - - post '/foo', :utf8 => '✓' - assert_match(/boooom/, last_response.body) - end - end -end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index dba79bfd96..578370cfca 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -33,6 +33,7 @@ module ApplicationTests "ActionDispatch::RequestId", "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods "ActionDispatch::ShowExceptions", + "ActionDispatch::DebugExceptions", "ActionDispatch::RemoteIp", "Rack::Sendfile", "ActionDispatch::Reloader", @@ -104,10 +105,11 @@ module ApplicationTests assert !middleware.include?("ActionDispatch::Static") end - test "includes show exceptions even if action_dispatch.show_exceptions is disabled" do + test "includes exceptions middlewares even if action_dispatch.show_exceptions is disabled" do add_to_config "config.action_dispatch.show_exceptions = false" boot! assert middleware.include?("ActionDispatch::ShowExceptions") + assert middleware.include?("ActionDispatch::DebugExceptions") end test "removes ActionDispatch::Reloader if cache_classes is true" do -- cgit v1.2.3 From f9edc079e030a5d2b0f21b80d6382caf10751057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 21:15:42 +0100 Subject: Split and improve show and debug exceptions middlewares. --- .../action_dispatch/middleware/show_exceptions.rb | 22 ++-- actionpack/test/dispatch/debug_exceptions_test.rb | 116 +++++++++++++++++++++ actionpack/test/dispatch/show_exceptions_test.rb | 90 +++------------- railties/CHANGELOG.md | 6 +- 4 files changed, 145 insertions(+), 89 deletions(-) create mode 100644 actionpack/test/dispatch/debug_exceptions_test.rb diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 346770acb7..c801bcf2ef 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -3,8 +3,8 @@ require 'action_dispatch/middleware/exception_wrapper' require 'active_support/deprecation' module ActionDispatch - # This middleware rescues any exception returned by the application and renders - # nice exception pages if it's being rescued locally. + # This middleware rescues any exception returned by the application + # and wraps them in a format for the end user. class ShowExceptions RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') @@ -40,11 +40,18 @@ module ActionDispatch raise exception if env['action_dispatch.show_exceptions'] == false end - response ? response : render_exception(env, exception) + response ? response : render_exception_with_failsafe(env, exception) end private + def render_exception_with_failsafe(env, exception) + render_exception(env, exception) + rescue Exception => failsafe_error + $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" + FAILSAFE_RESPONSE + end + def render_exception(env, exception) wrapper = ExceptionWrapper.new(env, exception) @@ -59,13 +66,6 @@ module ActionDispatch else render(status, '') end - rescue Exception => failsafe_error - render_failsafe(failsafe_error) - end - - def render_failsafe(failsafe_error) - $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" - FAILSAFE_RESPONSE end def render(status, body) @@ -73,7 +73,7 @@ module ActionDispatch end # TODO: Make this a middleware initialization parameter once - # we deprecated the second option + # we removed the second option (which is deprecated) def public_path defined?(Rails.public_path) ? Rails.public_path : 'public_path' end diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb new file mode 100644 index 0000000000..e6c0a06878 --- /dev/null +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -0,0 +1,116 @@ +require 'abstract_unit' + +class DebugExceptionsTest < ActionDispatch::IntegrationTest + + class Boomer + def initialize(detailed = false) + @detailed = detailed + end + + def call(env) + env['action_dispatch.show_detailed_exceptions'] = @detailed + req = ActionDispatch::Request.new(env) + case req.path + when "/not_found" + raise ActionController::UnknownAction + when "/runtime_error" + raise RuntimeError + when "/method_not_allowed" + raise ActionController::MethodNotAllowed + when "/not_implemented" + raise ActionController::NotImplemented + when "/unprocessable_entity" + raise ActionController::InvalidAuthenticityToken + when "/not_found_original_exception" + raise ActionView::Template::Error.new('template', {}, AbstractController::ActionNotFound.new) + else + raise "puke!" + end + end + end + + ProductionApp = ActionDispatch::DebugExceptions.new((Boomer.new(false))) + DevelopmentApp = ActionDispatch::DebugExceptions.new((Boomer.new(true))) + + test 'skip diagnosis if not showing detailed exceptions' do + @app = ProductionApp + assert_raise RuntimeError do + get "/", {}, {'action_dispatch.show_exceptions' => true} + end + end + + test 'skip diagnosis if not showing exceptions' do + @app = DevelopmentApp + assert_raise RuntimeError do + get "/", {}, {'action_dispatch.show_exceptions' => false} + end + end + + test "rescue with diagnostics message" do + @app = DevelopmentApp + + get "/", {}, {'action_dispatch.show_exceptions' => true} + assert_response 500 + assert_match(/puke/, body) + + get "/not_found", {}, {'action_dispatch.show_exceptions' => true} + assert_response 404 + assert_match(/#{ActionController::UnknownAction.name}/, body) + + get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true} + assert_response 405 + assert_match(/ActionController::MethodNotAllowed/, body) + end + + test "does not show filtered parameters" do + @app = DevelopmentApp + + get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true, + 'action_dispatch.parameter_filter' => [:foo]} + assert_response 500 + assert_match(""foo"=>"[FILTERED]"", body) + end + + test "show registered original exception for wrapped exceptions" do + @app = DevelopmentApp + + get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true} + assert_response 404 + assert_match(/AbstractController::ActionNotFound/, body) + end + + test "show the controller name in the diagnostics template when controller name is present" do + @app = DevelopmentApp + get("/runtime_error", {}, { + 'action_dispatch.show_exceptions' => true, + 'action_dispatch.request.parameters' => { + 'action' => 'show', + 'id' => 'unknown', + 'controller' => 'featured_tile' + } + }) + assert_response 500 + assert_match(/RuntimeError\n in FeaturedTileController/, body) + end + + test "sets the HTTP charset parameter" do + @app = DevelopmentApp + + get "/", {}, {'action_dispatch.show_exceptions' => true} + assert_equal "text/html; charset=utf-8", response.headers["Content-Type"] + end + + test 'uses logger from env' do + @app = DevelopmentApp + output = StringIO.new + get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)} + assert_match(/puke/, output.rewind && output.read) + end + + test 'uses backtrace cleaner from env' do + @app = DevelopmentApp + cleaner = stub(:clean => ['passed backtrace cleaner']) + get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.backtrace_cleaner' => cleaner} + assert_match(/passed backtrace cleaner/, body) + end +end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 4f1140b5d3..a34f6f1888 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -13,14 +13,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest case req.path when "/not_found" raise ActionController::UnknownAction - when "/runtime_error" - raise RuntimeError when "/method_not_allowed" raise ActionController::MethodNotAllowed - when "/not_implemented" - raise ActionController::NotImplemented - when "/unprocessable_entity" - raise ActionController::InvalidAuthenticityToken when "/not_found_original_exception" raise ActionView::Template::Error.new('template', {}, AbstractController::ActionNotFound.new) else @@ -29,10 +23,16 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end end - ProductionApp = ActionDispatch::ShowExceptions.new(ActionDispatch::DebugExceptions.new(Boomer.new(false))) - DevelopmentApp = ActionDispatch::ShowExceptions.new(ActionDispatch::DebugExceptions.new(Boomer.new(true))) + ProductionApp = ActionDispatch::ShowExceptions.new((Boomer.new(false))) - test "rescue with error page when show_exceptions is false" do + test 'skip diagnosis if not showing exceptions' do + @app = ProductionApp + assert_raise RuntimeError do + get "/", {}, {'action_dispatch.show_exceptions' => false} + end + end + + test "rescue with error page" do @app = ProductionApp get "/", {}, {'action_dispatch.show_exceptions' => true} @@ -48,24 +48,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest assert_equal "", body end - test "rescue with diagnostics message when show_exceptions is true" do - @app = DevelopmentApp - - get "/", {}, {'action_dispatch.show_exceptions' => true} - assert_response 500 - assert_match(/puke/, body) - - get "/not_found", {}, {'action_dispatch.show_exceptions' => true} - assert_response 404 - assert_match(/#{ActionController::UnknownAction.name}/, body) - - get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true} - assert_response 405 - assert_match(/ActionController::MethodNotAllowed/, body) - end - test "localize rescue error page" do - # Change locale old_locale, I18n.locale = I18n.locale, :da begin @@ -83,63 +66,18 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end end - test "does not show filtered parameters" do - @app = DevelopmentApp - - get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true, - 'action_dispatch.parameter_filter' => [:foo]} - assert_response 500 - assert_match(""foo"=>"[FILTERED]"", body) - end - - test "show registered original exception for wrapped exceptions when show_exceptions is false" do - @app = ProductionApp - - get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true} - assert_response 404 - assert_match(/404 error/, body) - end - - test "show registered original exception for wrapped exceptions when show_exceptions is true" do - @app = DevelopmentApp - - get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true} - assert_response 404 - assert_match(/AbstractController::ActionNotFound/, body) - end - - test "show the controller name in the diagnostics template when controller name is present" do - @app = DevelopmentApp - get("/runtime_error", {}, { - 'action_dispatch.show_exceptions' => true, - 'action_dispatch.request.parameters' => { - 'action' => 'show', - 'id' => 'unknown', - 'controller' => 'featured_tile' - } - }) - assert_response 500 - assert_match(/RuntimeError\n in FeaturedTileController/, body) - end - test "sets the HTTP charset parameter" do - @app = DevelopmentApp + @app = ProductionApp get "/", {}, {'action_dispatch.show_exceptions' => true} assert_equal "text/html; charset=utf-8", response.headers["Content-Type"] end - test 'uses logger from env' do + test "show registered original exception for wrapped exceptions" do @app = ProductionApp - output = StringIO.new - get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)} - assert_match(/puke/, output.rewind && output.read) - end - test 'uses backtrace cleaner from env' do - @app = DevelopmentApp - cleaner = stub(:clean => ['passed backtrace cleaner']) - get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.backtrace_cleaner' => cleaner} - assert_match(/passed backtrace cleaner/, body) + get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true} + assert_response 404 + assert_match(/404 error/, body) end end diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 6b0be4c096..d158fdd46f 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,13 +1,15 @@ ## Rails 3.2.0 (unreleased) ## -* Display mounted engine's routes in `rake routes`. *Piotr Sarnacki* +* Add DebugExceptions middleware which contains features extracted from ShowExceptions middleware *José Valim* + +* Display mounted engine's routes in `rake routes` *Piotr Sarnacki* * Allow to change the loading order of railties with `config.railties_order=` *Piotr Sarnacki* Example: config.railties_order = [Blog::Engine, :main_app, :all] -* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. *José Valim* +* Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box *José Valim* * Update Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH* -- cgit v1.2.3 From 6a4606d3a64e60189ea4ba5243830dcd97e6de14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Dec 2011 21:17:11 +0100 Subject: Remove unnecessary test setup. --- actionpack/test/dispatch/show_exceptions_test.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index a34f6f1888..020cc80f3f 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -3,12 +3,7 @@ require 'abstract_unit' class ShowExceptionsTest < ActionDispatch::IntegrationTest class Boomer - def initialize(detailed = false) - @detailed = detailed - end - def call(env) - env['action_dispatch.show_detailed_exceptions'] = @detailed req = ActionDispatch::Request.new(env) case req.path when "/not_found" @@ -23,7 +18,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest end end - ProductionApp = ActionDispatch::ShowExceptions.new((Boomer.new(false))) + ProductionApp = ActionDispatch::ShowExceptions.new(Boomer.new) test 'skip diagnosis if not showing exceptions' do @app = ProductionApp -- cgit v1.2.3