From 3900f4007ee6463b8936af23c04017a900673866 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 2 May 2009 14:23:44 -0500 Subject: Deprecate assert_redirect_to's partial hash matching --- actionpack/lib/action_dispatch/http/response.rb | 2 -- actionpack/lib/action_dispatch/testing/assertions/response.rb | 9 ++------- actionpack/lib/action_dispatch/testing/test_response.rb | 6 ++++++ 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 2b969323ca..2618e284fe 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -34,8 +34,6 @@ module ActionDispatch # :nodoc: DEFAULT_HEADERS = { "Cache-Control" => "no-cache" } attr_accessor :request - attr_accessor :redirected_to, :redirected_to_method_params - attr_writer :header alias_method :headers=, :header= diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index a72ce9084f..92e3da580e 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -60,14 +60,9 @@ module ActionDispatch validate_request! assert_response(:redirect, message) - return true if options == @response.redirected_to + return true if options == @response.location - # Support partial arguments for hash redirections - if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash) - return true if options.all? {|(key, value)| @response.redirected_to[key] == value} - end - - redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to) + redirected_to_after_normalisation = normalize_argument_to_redirection(@response.location) options_after_normalisation = normalize_argument_to_redirection(options) if redirected_to_after_normalisation != options_after_normalisation diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index 50c6d85828..c5464f1559 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -1,4 +1,10 @@ module ActionDispatch + # Integration test methods such as ActionController::Integration::Session#get + # and ActionController::Integration::Session#post return objects of class + # TestResponse, which represent the HTTP response results of the requested + # controller actions. + # + # See Response for more information on controller response objects. class TestResponse < Response def self.from_response(response) new.tap do |resp| -- cgit v1.2.3 From a8b75c480fc9774252f5976dcf1a614079822e56 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 2 May 2009 14:57:40 -0500 Subject: Functional test runner finalizes response just like the integration test runner. In both runners, the @response object will now behave the same. Some functional tests will need to be updated if they are relying on preprocessed data on the response. --- actionpack/lib/action_dispatch/http/response.rb | 2 +- .../lib/action_dispatch/testing/test_response.rb | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 2618e284fe..133b4f7335 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -185,7 +185,7 @@ module ActionDispatch # :nodoc: @writer = lambda { |x| callback.call(x) } @body.call(self, self) else - @body.each(&callback) + @body.each { |part| callback.call(part.to_s) } end @writer = callback diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index c5464f1559..c35982e075 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -93,6 +93,12 @@ module ActionDispatch ActiveSupport::Deprecation.warn("response.has_template_object? has been deprecated. Use tempate.assigns[name].nil? instead", caller) !template_objects[name].nil? end + + # Returns binary content (downloadable file), converted to a String + def binary_content + ActiveSupport::Deprecation.warn("response.binary_content has been deprecated. Use response.body instead", caller) + body + end end include DeprecatedHelpers @@ -121,17 +127,5 @@ module ActionDispatch def client_error? (400..499).include?(response_code) end - - # Returns binary content (downloadable file), converted to a String - def binary_content - raise "Response body is not a Proc: #{body_parts.inspect}" unless body_parts.kind_of?(Proc) - require 'stringio' - - sio = StringIO.new - body_parts.call(self, sio) - - sio.rewind - sio.read - end end end -- cgit v1.2.3 From f32cf44870549c6cc5b6e6c84cffc1facf6ec38e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 2 May 2009 15:29:18 -0500 Subject: Switch functional tests to run through the rack interface instead of process --- actionpack/lib/action_dispatch/testing/test_request.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb index 5d8cd7e619..20288aa7a5 100644 --- a/actionpack/lib/action_dispatch/testing/test_request.rb +++ b/actionpack/lib/action_dispatch/testing/test_request.rb @@ -16,6 +16,7 @@ module ActionDispatch def env write_cookies! + delete_nil_values! super end @@ -74,5 +75,9 @@ module ActionDispatch @env['HTTP_COOKIE'] = @cookies.map { |name, value| "#{name}=#{value};" }.join(' ') end end + + def delete_nil_values! + @env.delete_if { |k, v| v.nil? } + end end end -- cgit v1.2.3 From 11af089cee0a0e744e267d32becfe2c66a586d31 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 2 May 2009 23:02:22 -0500 Subject: Extract ActionController rescue templates into Rescue and ShowExceptions middleware. This commit breaks all exception catching plugins like ExceptionNotifier. These plugins should be rewritten as middleware instead overriding Controller#rescue_action_in_public. --- .../lib/action_dispatch/middleware/rescue.rb | 14 ++ .../action_dispatch/middleware/show_exceptions.rb | 142 +++++++++++++++++++++ .../templates/rescues/_request_and_response.erb | 24 ++++ .../middleware/templates/rescues/_trace.erb | 26 ++++ .../middleware/templates/rescues/diagnostics.erb | 10 ++ .../middleware/templates/rescues/layout.erb | 29 +++++ .../templates/rescues/missing_template.erb | 2 + .../middleware/templates/rescues/routing_error.erb | 10 ++ .../templates/rescues/template_error.erb | 21 +++ .../templates/rescues/unknown_action.erb | 2 + .../action_dispatch/testing/assertions/response.rb | 8 +- 11 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 actionpack/lib/action_dispatch/middleware/rescue.rb create mode 100644 actionpack/lib/action_dispatch/middleware/show_exceptions.rb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb create mode 100644 actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/middleware/rescue.rb b/actionpack/lib/action_dispatch/middleware/rescue.rb new file mode 100644 index 0000000000..1456825526 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/rescue.rb @@ -0,0 +1,14 @@ +module ActionDispatch + class Rescue + def initialize(app, rescuer) + @app, @rescuer = app, rescuer + end + + def call(env) + @app.call(env) + rescue Exception => exception + env['action_dispatch.rescue.exception'] = exception + @rescuer.call(env) + end + end +end 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..791254cdf2 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -0,0 +1,142 @@ +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 + 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 + # 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_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 diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb new file mode 100644 index 0000000000..5224403dab --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb @@ -0,0 +1,24 @@ +<% unless @exception.blamed_files.blank? %> + <% if (hide = @exception.blamed_files.length > 8) %> + Show blamed files + <% end %> +
><%=h @exception.describe_blame %>
+<% end %> + +<% + clean_params = @request.parameters.clone + clean_params.delete("action") + clean_params.delete("controller") + + request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n") +%> + +

Request

+

Parameters:

<%=h request_dump %>

+ +

Show session dump

+ + + +

Response

+

Headers:

<%=h @response ? @response.headers.inspect.gsub(',', ",\n") : 'None' %>

diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb new file mode 100644 index 0000000000..bb2d8375bd --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb @@ -0,0 +1,26 @@ +<% + traces = [ + ["Application Trace", @exception.application_backtrace], + ["Framework Trace", @exception.framework_backtrace], + ["Full Trace", @exception.clean_backtrace] + ] + names = traces.collect {|name, trace| name} +%> + +

RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %>

+ +
+ <% names.each do |name| %> + <% + show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';" + hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"} + %> + <%= name %> <%= '|' unless names.last == name %> + <% end %> + + <% traces.each do |name, trace| %> +
;"> +
<%= trace.join "\n" %>
+
+ <% end %> +
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb new file mode 100644 index 0000000000..693e56270a --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb @@ -0,0 +1,10 @@ +

+ <%=h @exception.class.to_s %> + <% if @request.parameters['controller'] %> + in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %> + <% end %> +

+
<%=h @exception.clean_message %>
+ +<%= render :file => "rescues/_trace.erb" %> +<%= render :file => "rescues/_request_and_response.erb" %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb new file mode 100644 index 0000000000..6c32fb17b8 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb @@ -0,0 +1,29 @@ + + + Action Controller: Exception caught + + + + +<%= yield %> + + + diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb new file mode 100644 index 0000000000..dbfdf76947 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb @@ -0,0 +1,2 @@ +

Template is missing

+

<%=h @exception.message %>

diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb new file mode 100644 index 0000000000..ccfa858cce --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb @@ -0,0 +1,10 @@ +

Routing Error

+

<%=h @exception.message %>

+<% unless @exception.failures.empty? %>

+

Failure reasons:

+
    + <% @exception.failures.each do |route, reason| %> +
  1. <%=h route.inspect.gsub('\\', '') %> failed because <%=h reason.downcase %>
  2. + <% end %> +
+

<% end %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb new file mode 100644 index 0000000000..02fa18211d --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb @@ -0,0 +1,21 @@ +

+ <%=h @exception.original_exception.class.to_s %> in + <%=h @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%=h @request.parameters["action"] %> +

+ +

+ Showing <%=h @exception.file_name %> where line #<%=h @exception.line_number %> raised: +

<%=h @exception.message %>
+

+ +

Extracted source (around line #<%=h @exception.line_number %>): +

<%=h @exception.source_extract %>

+ +

<%=h @exception.sub_template_message %>

+ +<% @real_exception = @exception + @exception = @exception.original_exception || @exception %> +<%= render :file => "rescues/_trace.erb" %> +<% @exception = @real_exception %> + +<%= render :file => "rescues/_request_and_response.erb" %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb new file mode 100644 index 0000000000..683379da10 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb @@ -0,0 +1,2 @@ +

Unknown action

+

<%=h @exception.message %>

diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 92e3da580e..501a7c4dfb 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -31,13 +31,7 @@ module ActionDispatch elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type] assert_block("") { true } # to count the assertion else - if @controller && @response.error? - exception = @controller.template.instance_variable_get(:@exception) - exception_message = exception && exception.message - assert_block(build_message(message, "Expected response to be a , but was \n", type, @response.response_code, exception_message.to_s)) { false } - else - assert_block(build_message(message, "Expected response to be a , but was ", type, @response.response_code)) { false } - end + assert_block(build_message(message, "Expected response to be a , but was ", type, @response.response_code)) { false } end end -- cgit v1.2.3 From e0660192806fc1ec8b75654e69211f85c9f1256b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 3 May 2009 09:24:32 -0500 Subject: Show lazy middleware args in pretty print --- actionpack/lib/action_dispatch/middleware/stack.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index ee5f28d5cb..ec8a9ca76e 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -61,7 +61,7 @@ module ActionDispatch def inspect str = klass.to_s - args.each { |arg| str += ", #{arg.inspect}" } + args.each { |arg| str += ", #{build_args.inspect}" } str end @@ -74,7 +74,6 @@ module ActionDispatch end private - def build_args Array(args).map { |arg| arg.respond_to?(:call) ? arg.call : arg } end -- cgit v1.2.3 From bcc4537f2a0d37fc02d67e9564fa5c9c5555b3d5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 3 May 2009 09:41:40 -0500 Subject: Wrap dispatcher callbacks around the whole middleware chain. Kill unnecessary Reloader middleware. --- actionpack/lib/action_dispatch/middleware/reloader.rb | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 actionpack/lib/action_dispatch/middleware/reloader.rb (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb deleted file mode 100644 index 67313e30e4..0000000000 --- a/actionpack/lib/action_dispatch/middleware/reloader.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ActionDispatch - class Reloader - def initialize(app) - @app = app - end - - def call(env) - ActionController::Dispatcher.reload_application - @app.call(env) - ensure - ActionController::Dispatcher.cleanup_application - end - end -end -- cgit v1.2.3