diff options
Diffstat (limited to 'actionpack')
21 files changed, 186 insertions, 60 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 6b73b29ace..e8a29f5182 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,25 @@ +* When a `respond_to` collector with a block doesn't have a response, then + a `:no_content` response should be rendered. This brings the default + rendering behavior introduced by https://github.com/rails/rails/issues/19036 + to controller methods employing `respond_to`. + + *Justin Coyne* + +* Add `ActionController::Parameters#dig` on Ruby 2.3 and greater, which + behaves the same as `Hash#dig`. + + *Sean Griffin* + +* Add request headers in the payload of the `start_processing.action_controller` + and `process_action.action_controller` notifications. + + *Gareth du Plooy* + +* Add `action_dispatch_integration_test` load hook. The hook can be used to + extend `ActionDispatch::IntegrationTest` once it has been loaded. + + *Yuichiro Kaneko* + * Update default rendering policies when the controller action did not explicitly indicate a response. @@ -8,10 +30,10 @@ First, if a template exists for the controller action, it is rendered. This template lookup takes into account the action name, locales, format, - variant, template handlers, etc. (see +render+ for details). + variant, template handlers, etc. (see `render` for details). Second, if other templates exist for the controller action but is not in - the right format (or variant, etc.), an <tt>ActionController::UnknownFormat</tt> + the right format (or variant, etc.), an `ActionController::UnknownFormat` is raised. The list of available templates is assumed to be a complete enumeration of all the possible formats (or variants, etc.); that is, having only HTML and JSON templates indicate that the controller action is @@ -20,7 +42,7 @@ Third, if the current request is an "interactive" browser request (the user navigated here by entering the URL in the address bar, submiting a form, clicking on a link, etc. as opposed to an XHR or non-browser API request), - <tt>ActionView::UnknownFormat</tt> is raised to display a helpful error + `ActionView::UnknownFormat` is raised to display a helpful error message. Finally, it falls back to the same "204 No Content" behavior as API controllers. @@ -29,7 +51,7 @@ ## Rails 5.0.0.beta3 (February 24, 2016) ## -* Add application/gzip as a default mime type. +* Add "application/gzip" as a default mime type. *Mehmet Emin İNAÇ* @@ -80,7 +102,7 @@ *Kasper Timm Hansen* -* Add image/svg+xml as a default mime type. +* Add "image/svg+xml" as a default mime type. *DHH* @@ -93,7 +115,7 @@ See #18902. - *Anton Davydov* & *Vipul A M* + *Anton Davydov*, *Vipul A M* * Response etags to always be weak: Prefixes 'W/' to value returned by `ActionDispatch::Http::Cache::Response#etag=`, such that etags set in @@ -125,11 +147,7 @@ * Add option for per-form CSRF tokens. - *Greg Ose & Ben Toews* - -* Add tests and documentation for `ActionController::Renderers::use_renderers`. - - *Benjamin Fleischer* + *Greg Ose*, *Ben Toews* * Fix `ActionController::Parameters#convert_parameters_to_hashes` to return filtered or unfiltered values based on from where it is called, `to_h` or `to_unsafe_h` diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 28d8bc3091..66300754e3 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.author = 'David Heinemeier Hansson' s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.homepage = 'http://rubyonrails.org' s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*'] s.require_path = 'lib' diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 8edea0f52b..16dec31938 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,13 +1,11 @@ require 'erubis' +require 'abstract_controller/error' require 'active_support/configurable' require 'active_support/descendants_tracker' require 'active_support/core_ext/module/anonymous' require 'active_support/core_ext/module/attr_internal' module AbstractController - class Error < StandardError #:nodoc: - end - # Raised when a non-existing controller action is triggered. class ActionNotFound < StandardError end diff --git a/actionpack/lib/abstract_controller/error.rb b/actionpack/lib/abstract_controller/error.rb new file mode 100644 index 0000000000..7fafce4dd4 --- /dev/null +++ b/actionpack/lib/abstract_controller/error.rb @@ -0,0 +1,4 @@ +module AbstractController + class Error < StandardError #:nodoc: + end +end diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index d84c238a62..ab4355296b 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -38,7 +38,8 @@ module AbstractController end # Declare a controller method as a helper. For example, the following - # makes the +current_user+ controller method available to the view: + # makes the +current_user+ and +logged_in?+ controller methods available + # to the view: # class ApplicationController < ActionController::Base # helper_method :current_user, :logged_in? # diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index e765d73ce4..9f192c54f7 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -1,3 +1,4 @@ +require 'abstract_controller/error' require 'active_support/concern' require 'active_support/core_ext/class/attribute' require 'action_view' diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index e2535d024c..35befc05e1 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -222,12 +222,10 @@ module ActionController # * +public+: By default, HTTP responses are private, cached only on the # user's web browser. To allow proxies to cache the response, set +true+ to # indicate that they can serve the cached response to all users. - # - # * +version+: the version passed as a key for the cache. - def http_cache_forever(public: false, version: 'v1') + def http_cache_forever(public: false) expires_in 100.years, public: public - yield if stale?(etag: "#{version}-#{request.fullpath}", + yield if stale?(etag: request.fullpath, last_modified: Time.new(2011, 1, 1).utc, public: public) end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index bf74b39ac4..885ea3fefd 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -19,6 +19,7 @@ module ActionController :controller => self.class.name, :action => self.action_name, :params => request.filtered_parameters, + :headers => request.headers, :format => request.format.ref, :method => request.request_method, :path => request.fullpath diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 173a14a1d2..2e89af1a5e 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -198,7 +198,7 @@ module ActionController #:nodoc: _process_format(format) _set_rendered_content_type format response = collector.response - response ? response.call : render({}) + response.call if response else raise ActionController::UnknownFormat end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index b13ba06962..3c7cc15627 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -84,7 +84,7 @@ module ActionController # redirect_back fallback_location: proc { edit_post_url(@post) } # # All options that can be passed to <tt>redirect_to</tt> are accepted as - # options and the behavior is indetical. + # options and the behavior is identical. def redirect_back(fallback_location:, **args) if referer = request.headers["Referer"] redirect_to referer, **args diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index 81b9a7b9ed..0621a7368c 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -7,8 +7,12 @@ module ActionController #:nodoc: include ActiveSupport::Rescuable def rescue_with_handler(exception) - if exception.cause && handler_for_rescue(exception.cause) - exception = exception.cause + if exception.cause + handler_index = index_of_handler_for_rescue(exception) || Float::INFINITY + cause_handler_index = index_of_handler_for_rescue(exception.cause) + if cause_handler_index && cause_handler_index <= handler_index + exception = exception.cause + end end super(exception) end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index a01110d474..76e3b4d25a 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -430,6 +430,21 @@ module ActionController ) end + if Hash.method_defined?(:dig) + # Extracts the nested parameter from the given +keys+ by calling +dig+ + # at each step. Returns +nil+ if any intermediate step is +nil+. + # + # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } }) + # params.dig(:foo, :bar, :baz) # => 1 + # params.dig(:foo, :zot, :xyz) # => nil + # + # params2 = ActionController::Parameters.new(foo: [10, 11, 12]) + # params2.dig(:foo, 1) # => 11 + def dig(*keys) + convert_value_to_parameters(@parameters.dig(*keys)) + end + end + # Returns a new <tt>ActionController::Parameters</tt> instance that # includes only the given +keys+. If the given +keys+ # don't exist, returns an empty hash. @@ -799,7 +814,8 @@ module ActionController # end # # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you - # will need to specify which nested attributes should be whitelisted. + # will need to specify which nested attributes should be whitelisted. You might want + # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information. # # class Person # has_many :pets @@ -819,7 +835,7 @@ module ActionController # # It's mandatory to specify the nested attributes that should be whitelisted. # # If you use `permit` with just the key that points to the nested attributes hash, # # it will return an empty hash. - # params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ]) + # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ]) # end # end # diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 316a9f08b7..b0ed681623 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -337,7 +337,6 @@ module ActionDispatch else self.session = {} end - self.flash = nil end def session=(session) #:nodoc: diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index c51dcd542a..06038af571 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -70,6 +70,11 @@ module ActionDispatch session.delete('flash') end end + + def reset_session # :nodoc + super + self.flash = nil + end end class FlashNow #:nodoc: diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index dcf800b215..79d2f1f13c 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -73,14 +73,14 @@ module ActionDispatch # get 'post/:id' => 'posts#show' # post 'post/:id' => 'posts#create_comment' # + # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same + # URL will route to the <tt>show</tt> action. + # # If your route needs to respond to more than one HTTP method (or all methods) then using the # <tt>:via</tt> option on <tt>match</tt> is preferable. # # match 'post/:id' => 'posts#show', via: [:get, :post] # - # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same - # URL will route to the <tt>show</tt> action. - # # == Named routes # # Routes can be named by passing an <tt>:as</tt> option, diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index f4534b4173..60c562d7cd 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -735,34 +735,49 @@ module ActionDispatch # Consult the Rails Testing Guide for more. class IntegrationTest < ActiveSupport::TestCase - include Integration::Runner - include ActionController::TemplateAssertions - include ActionDispatch::Routing::UrlFor + module UrlOptions + extend ActiveSupport::Concern + def url_options + integration_session.url_options + end + end - @@app = nil + module Behavior + extend ActiveSupport::Concern - def self.app - @@app || ActionDispatch.test_app - end + include Integration::Runner + include ActionController::TemplateAssertions - def self.app=(app) - @@app = app - end + included do + include ActionDispatch::Routing::UrlFor + include UrlOptions # don't let UrlFor override the url_options method + ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self) + @@app = nil + end - def app - super || self.class.app - end + module ClassMethods + def app + defined?(@@app) ? @@app : ActionDispatch.test_app + end - def url_options - integration_session.url_options - end + def app=(app) + @@app = app + end - def document_root_element - html_document.root - end + def register_encoder(*args) + Integration::Session::RequestEncoder.register_encoder(*args) + end + end - def self.register_encoder(*args) - Integration::Session::RequestEncoder.register_encoder(*args) + def app + super || self.class.app + end + + def document_root_element + html_document.root + end end + + include Behavior end end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 6ae33be3c8..57cf2dafdf 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -183,6 +183,12 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal "test_value", @controller.last_payload[:test_key] end + def test_process_action_headers + get :show + wait + assert_equal "Rails Testing", @controller.last_payload[:headers]['User-Agent'] + end + def test_process_action_with_filter_parameters @request.env["action_dispatch.parameter_filter"] = [:lifo, :amount] diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index d0c7b2e06a..993f4001de 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -74,6 +74,14 @@ class RespondToController < ActionController::Base end end + def missing_templates + respond_to do |type| + # This test requires a block that is empty + type.json { } + type.xml + end + end + def using_defaults_with_type_list respond_to(:html, :xml) end @@ -624,6 +632,13 @@ class RespondToControllerTest < ActionController::TestCase end end + def test_missing_templates + get :missing_templates, format: :json + assert_response :no_content + get :missing_templates, format: :xml + assert_response :no_content + end + def test_invalid_variant assert_raises(ActionController::UnknownFormat) do get :variant_with_implicit_template_rendering, params: { v: :invalid } diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb index cea265f9ab..17c62dc3fe 100644 --- a/actionpack/test/controller/parameters/accessors_test.rb +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -194,4 +194,24 @@ class ParametersAccessorsTest < ActiveSupport::TestCase assert_match(/permitted: true/, @params.inspect) end + + if Hash.method_defined?(:dig) + test "#dig delegates the dig method to its values" do + assert_equal "David", @params.dig(:person, :name, :first) + assert_equal "Chicago", @params.dig(:person, :addresses, 0, :city) + end + + test "#dig converts hashes to parameters" do + assert_kind_of ActionController::Parameters, @params.dig(:person) + assert_kind_of ActionController::Parameters, @params.dig(:person, :addresses, 0) + assert @params.dig(:person, :addresses).all? do |value| + value.is_a?(ActionController::Parameters) + end + end + else + test "ActionController::Parameters does not respond to #dig on Ruby 2.2" do + assert_not ActionController::Parameters.method_defined?(:dig) + assert_not @params.respond_to?(:dig) + end + end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index b7f3e121fd..82fc8b0f8a 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -703,7 +703,7 @@ end class HttpCacheForeverTest < ActionController::TestCase class HttpCacheForeverController < ActionController::Base def cache_me_forever - http_cache_forever(public: params[:public], version: params[:version] || 'v1') do + http_cache_forever(public: params[:public]) do render plain: 'hello' end end @@ -742,13 +742,5 @@ class HttpCacheForeverTest < ActionController::TestCase assert_response :not_modified @request.if_modified_since = @response.headers['Last-Modified'] @request.if_none_match = @response.etag - - get :cache_me_forever, params: {version: 'v2'} - assert_response :success - @request.if_modified_since = @response.headers['Last-Modified'] - @request.if_none_match = @response.etag - - get :cache_me_forever, params: {version: 'v2'} - assert_response :not_modified end end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index f42bef883f..ed78f859ce 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -147,6 +147,24 @@ class RescueController < ActionController::Base end end + def exception_with_more_specific_handler_for_wrapper + raise RecordInvalid + rescue + raise NotAuthorized + end + + def exception_with_more_specific_handler_for_cause + raise NotAuthorized + rescue + raise RecordInvalid + end + + def exception_with_no_handler_for_wrapper + raise RecordInvalid + rescue + raise RangeError + end + protected def deny_access head :forbidden @@ -301,6 +319,21 @@ class RescueControllerTest < ActionController::TestCase get :resource_unavailable_raise_as_string assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body end + + test 'rescue when wrapper has more specific handler than cause' do + get :exception_with_more_specific_handler_for_wrapper + assert_response :unprocessable_entity + end + + test 'rescue when cause has more specific handler than wrapper' do + get :exception_with_more_specific_handler_for_cause + assert_response :unprocessable_entity + end + + test 'rescue when cause has handler, but wrapper doesnt' do + get :exception_with_no_handler_for_wrapper + assert_response :unprocessable_entity + end end class RescueTest < ActionDispatch::IntegrationTest |