diff options
Diffstat (limited to 'actionpack')
22 files changed, 227 insertions, 54 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 115ad54190..838380ff71 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,34 @@ +* Ensure `append_info_to_payload` is called even if an exception is raised. + + Fixes an issue where when an exception is raised in the request the additonal + payload data is not available. + + See: + * #14903 + * https://github.com/roidrage/lograge/issues/37 + + *Dieter Komendera*, *Margus Pärt* + +* Correctly rely on the response's status code to handle calls to `head`. + + *Robin Dupret* + +* Using `head` method returns empty response_body instead + of returning a single space " ". + + The old behavior was added as a workaround for a bug in an early + version of Safari, where the HTTP headers are not returned correctly + if the response body has a 0-length. This is been fixed since and + the workaround is no longer necessary. + + Fixes #18253. + + *Prathamesh Sonpatki* + +* Fix how polymorphic routes works with objects that implement `to_model`. + + *Travis Grathwell* + * Stop converting empty arrays in `params` to `nil` This behaviour was introduced in response to CVE-2012-2660, CVE-2012-2694 @@ -9,11 +40,11 @@ *Chris Sinjakli* -* Fixed usage of optional scopes in URL helpers. +* Fixed usage of optional scopes in url helpers. *Alex Robbin* -* Fixed handling of positional url helper arguments when `format: false`. +* Fixed handling of positional url helper arguments when `format: false`. Fixes #17819. diff --git a/actionpack/MIT-LICENSE b/actionpack/MIT-LICENSE index d58dd9ed9b..3ec7a617cf 100644 --- a/actionpack/MIT-LICENSE +++ b/actionpack/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2014 David Heinemeier Hansson +Copyright (c) 2004-2015 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index c0040ec28e..f83823dd75 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).' s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.' - s.required_ruby_version = '>= 2.1.0' + s.required_ruby_version = '>= 2.2.0' s.license = 'MIT' diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index df7382f02d..109eff10eb 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -184,7 +184,7 @@ module AbstractController module_name = name.sub(/Controller$/, '') module_path = module_name.underscore helper module_path - rescue MissingSourceFile => e + rescue LoadError => e raise e unless e.is_missing? "helpers/#{module_path}_helper" rescue NameError => e raise e unless e.missing_name? "#{module_name}Helper" diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 6dd213b2f7..993f8e150d 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -173,6 +173,7 @@ module ActionController def status @_status end + alias :response_code :status # :nodoc: def status=(status) @_status = Rack::Utils.status_code(status) @@ -236,9 +237,5 @@ module ActionController lambda { |env| new.dispatch(name, klass.new(env)) } end end - - def _status_code #:nodoc: - @_status - end end end diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 3d2badf9c2..0d93e2f7aa 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -29,14 +29,14 @@ module ActionController self.status = status self.location = url_for(location) if location - if include_content?(self._status_code) + self.response_body = "" + + if include_content?(self.response_code) self.content_type = content_type || (Mime[formats.first] if formats) self.response.charset = false if self.response - self.response_body = " " else headers.delete('Content-Type') headers.delete('Content-Length') - self.response_body = "" end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index fd578d60ca..a219d35b25 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -53,10 +53,8 @@ module ActionController # In your integration tests, you can do something like this: # # def test_access_granted_from_xml - # get( - # "/notes/1.xml", nil, - # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) - # ) + # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) + # get "/notes/1.xml" # # assert_equal 200, status # end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index bef7545e71..a3e1a71b0a 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -28,10 +28,13 @@ module ActionController ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup) ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload| - result = super - payload[:status] = response.status - append_info_to_payload(payload) - result + begin + result = super + payload[:status] = response.status + result + ensure + append_info_to_payload(payload) + end end end diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 0f2fa5fb08..572d1770f7 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -30,9 +30,9 @@ module ActionController :_recall => request.path_parameters }.merge!(super).freeze - if (same_origin = _routes.equal?(env["action_dispatch.routes".freeze])) || - (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) || - (original_script_name = env['ORIGINAL_SCRIPT_NAME'.freeze]) + if (same_origin = _routes.equal?(request.routes)) || + (script_name = request.engine_script_name(_routes)) || + (original_script_name = request.original_script_name) options = @_url_options.dup if original_script_name diff --git a/actionpack/lib/action_controller/model_naming.rb b/actionpack/lib/action_controller/model_naming.rb deleted file mode 100644 index 2b33f67263..0000000000 --- a/actionpack/lib/action_controller/model_naming.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ActionController - module ModelNaming - # Converts the given object to an ActiveModel compliant one. - def convert_to_model(object) - object.respond_to?(:to_model) ? object.to_model : object - end - - def model_name_from_record_or_class(record_or_class) - convert_to_model(record_or_class).model_name - end - end -end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index b9172f8fa3..9a77f179d3 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -67,7 +67,8 @@ module ActionController def reset_template_assertion RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable| - instance_variable_get("@_#{instance_variable}").clear + ivar = instance_variable_get("@_#{instance_variable}") + ivar.clear if ivar end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 11b5e6be33..dcd3ee0644 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2014 David Heinemeier Hansson +# Copyright (c) 2004-2015 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 2a7bb374a5..d211ea2b77 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -105,6 +105,18 @@ module ActionDispatch @request_method ||= check_method(env["REQUEST_METHOD"]) end + def routes # :nodoc: + env["action_dispatch.routes".freeze] + end + + def original_script_name # :nodoc: + env['ORIGINAL_SCRIPT_NAME'.freeze] + end + + def engine_script_name(_routes) # :nodoc: + env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"] + end + def request_method=(request_method) #:nodoc: if check_method(request_method) @request_method = env["REQUEST_METHOD"] = request_method diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 93d1d33f78..8d3ce24612 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -410,7 +410,7 @@ module ActionDispatch @options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE) end - def serialize(name, value) + def serialize(value) serializer.dump(value) end @@ -463,9 +463,9 @@ module ActionDispatch def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! - options[:value] = @verifier.generate(serialize(name, options[:value])) + options[:value] = @verifier.generate(serialize(options[:value])) else - options = { :value => @verifier.generate(serialize(name, options)) } + options = { :value => @verifier.generate(serialize(options)) } end raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE @@ -524,7 +524,7 @@ module ActionDispatch options = { :value => options } end - options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value])) + options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value])) raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE @parent_jar[name] = options diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 0847842fa2..2e116ea9cd 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -1,5 +1,3 @@ -require 'action_controller/model_naming' - module ActionDispatch module Routing # Polymorphic URL helpers are methods for smart resolution to a named route call when @@ -55,8 +53,6 @@ module ActionDispatch # form_for([blog, @post]) # => "/blog/posts/1" # module PolymorphicRoutes - include ActionController::ModelNaming - # Constructs a call to a named RESTful route for the given record and returns the # resulting URL string. For example: # @@ -251,7 +247,7 @@ module ActionDispatch args = [] model = record.to_model - name = if record.persisted? + name = if model.persisted? args << model model.model_name.singular_route_key else @@ -294,11 +290,12 @@ module ActionDispatch when Class @key_strategy.call record.model_name else - if record.persisted? - args << record.to_model - record.to_model.model_name.singular_route_key + model = record.to_model + if model.persisted? + args << model + model.model_name.singular_route_key else - @key_strategy.call record.to_model.model_name + @key_strategy.call model.model_name end end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index a9a1576fed..f0e2c5becc 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -480,6 +480,84 @@ module ActionDispatch # end # end # end + # + # Another longer example would be: + # + # A simple integration test that exercises multiple controllers: + # + # require 'test_helper' + # + # class UserFlowsTest < ActionDispatch::IntegrationTest + # test "login and browse site" do + # # login via https + # https! + # get "/login" + # assert_response :success + # + # post_via_redirect "/login", username: users(:david).username, password: users(:david).password + # assert_equal '/welcome', path + # assert_equal 'Welcome david!', flash[:notice] + # + # https!(false) + # get "/articles/all" + # assert_response :success + # assert assigns(:articles) + # end + # end + # + # As you can see the integration test involves multiple controllers and + # exercises the entire stack from database to dispatcher. In addition you can + # have multiple session instances open simultaneously in a test and extend + # those instances with assertion methods to create a very powerful testing + # DSL (domain-specific language) just for your application. + # + # Here's an example of multiple sessions and custom DSL in an integration test + # + # require 'test_helper' + # + # class UserFlowsTest < ActionDispatch::IntegrationTest + # test "login and browse site" do + # # User david logs in + # david = login(:david) + # # User guest logs in + # guest = login(:guest) + # + # # Both are now available in different sessions + # assert_equal 'Welcome david!', david.flash[:notice] + # assert_equal 'Welcome guest!', guest.flash[:notice] + # + # # User david can browse site + # david.browses_site + # # User guest can browse site as well + # guest.browses_site + # + # # Continue with other assertions + # end + # + # private + # + # module CustomDsl + # def browses_site + # get "/products/all" + # assert_response :success + # assert assigns(:products) + # end + # end + # + # def login(user) + # open_session do |sess| + # sess.extend(CustomDsl) + # u = users(user) + # sess.https! + # sess.post "/login", username: u.username, password: u.password + # assert_equal '/welcome', sess.path + # sess.https!(false) + # end + # end + # end + # + # Consult the Rails Testing Guide for more. + class IntegrationTest < ActiveSupport::TestCase include Integration::Runner include ActionController::TemplateAssertions diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb index 77f656d6f1..f664dab620 100644 --- a/actionpack/lib/action_pack.rb +++ b/actionpack/lib/action_pack.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2014 David Heinemeier Hansson +# Copyright (c) 2004-2015 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 829729eb1b..2e08a6af9f 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -1003,21 +1003,21 @@ class YieldingAroundFiltersTest < ActionController::TestCase def test_first_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_1') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 1, controller.instance_variable_get(:@try) end def test_second_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_2') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 2, controller.instance_variable_get(:@try) end def test_last_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_3') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 3, controller.instance_variable_get(:@try) end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index d6219b7626..5535c7ae78 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -850,3 +850,27 @@ class IntegrationWithRoutingTest < ActionDispatch::IntegrationTest end end end + +# to work in contexts like rspec before(:all) +class IntegrationRequestsWithoutSetup < ActionDispatch::IntegrationTest + self._setup_callbacks = [] + self._teardown_callbacks = [] + + class FooController < ActionController::Base + def ok + cookies[:key] = 'ok' + render plain: 'ok' + end + end + + def test_request + with_routing do |routes| + routes.draw { get ':action' => FooController } + get '/ok' + + assert_response 200 + assert_equal 'ok', response.body + assert_equal 'ok', cookies['key'] + end + end +end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 49be7caf38..864c6ee130 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -73,6 +73,16 @@ module Another def with_action_not_found raise AbstractController::ActionNotFound end + + def append_info_to_payload(payload) + super + payload[:test_key] = "test_value" + @last_payload = payload + end + + def last_payload + @last_payload + end end end @@ -163,6 +173,16 @@ class ACLogSubscriberTest < ActionController::TestCase assert_match(/\(Views: [\d.]+ms\)/, logs[1]) end + def test_append_info_to_payload_is_called_even_with_exception + begin + get :with_exception + wait + rescue Exception + end + + assert_equal "test_value", @controller.last_payload[:test_key] + end + def test_process_action_with_filter_parameters @request.env["action_dispatch.parameter_filter"] = [:lifo, :amount] diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index b036b6c08e..929b161eb6 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -217,6 +217,15 @@ class TestController < ActionController::Base head :forbidden, :x_custom_header => "something" end + def head_with_no_content + # Fill in the headers with dummy data to make + # sure they get removed during the testing + response.headers["Content-Type"] = "dummy" + response.headers["Content-Length"] = 42 + + head 204 + end + private def set_variable_for_layout @@ -545,6 +554,14 @@ class HeadRenderTest < ActionController::TestCase end end + def test_head_with_no_content + get :head_with_no_content + + assert_equal 204, @response.status + assert_nil @response.headers["Content-Type"] + assert_nil @response.headers["Content-Length"] + end + def test_head_with_string_status get :head_with_string_status, :status => "404 Eat Dirt" assert_equal 404, @response.response_code diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 19a98a4054..6223a52a76 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -987,6 +987,13 @@ class CookiesTest < ActionController::TestCase assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/" end + def test_cookie_with_all_domain_option_using_a_non_standard_2_letter_tld + @request.host = "admin.lvh.me" + get :set_cookie_with_domain_and_tld + assert_response :success + assert_cookie_header "user_name=rizwanreza; domain=.lvh.me; path=/" + end + def test_cookie_with_all_domain_option_using_host_with_port_and_tld_length @request.host = "nextangle.local:3000" get :set_cookie_with_domain_and_tld |