diff options
Diffstat (limited to 'actionpack/lib/action_controller')
31 files changed, 583 insertions, 486 deletions
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb index 6bbebb7b4c..5cd8d77ddb 100644 --- a/actionpack/lib/action_controller/api.rb +++ b/actionpack/lib/action_controller/api.rb @@ -1,6 +1,6 @@ -require 'action_view' -require 'action_controller' -require 'action_controller/log_subscriber' +require "action_view" +require "action_controller" +require "action_controller/log_subscriber" module ActionController # API Controller is a lightweight version of <tt>ActionController::Base</tt>, diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 04e5922ce8..ca8066cd82 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,4 +1,4 @@ -require 'action_view' +require "action_view" require "action_controller/log_subscriber" require "action_controller/metal/params_wrapper" @@ -32,7 +32,7 @@ module ActionController # new post), it initiates a redirect instead. This redirect works by returning an external # "302 Moved" HTTP response that takes the user to the index action. # - # These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. + # These two methods represent the two basic action archetypes used in Action Controllers: Get-and-show and do-and-redirect. # Most actions are variations on these themes. # # == Requests @@ -51,8 +51,8 @@ module ActionController # == Parameters # # All request parameters, whether they come from a query string in the URL or form data submitted through a POST request are - # available through the params method which returns a hash. For example, an action that was performed through - # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in params. + # available through the <tt>params</tt> method which returns a hash. For example, an action that was performed through + # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in <tt>params</tt>. # # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as: # @@ -60,7 +60,7 @@ module ActionController # <input type="text" name="post[address]" value="hyacintvej"> # # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>. - # If the address input had been named <tt>post[address][street]</tt>, the params would have included + # If the address input had been named <tt>post[address][street]</tt>, the <tt>params</tt> would have included # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting. # # == Sessions @@ -213,11 +213,12 @@ module ActionController Renderers::All, ConditionalGet, EtagWithTemplateDigest, + EtagWithFlash, Caching, MimeResponds, ImplicitRender, StrongParameters, - + ParameterEncoding, Cookies, Flash, FormBuilder, @@ -229,7 +230,7 @@ module ActionController HttpAuthentication::Digest::ControllerMethods, HttpAuthentication::Token::ControllerMethods, - # Before callbacks should also be executed the earliest as possible, so + # Before callbacks should also be executed as early as possible, so # also include them at the bottom. AbstractController::Callbacks, @@ -251,9 +252,10 @@ module ActionController setup_renderer! # Define some internal variables that should not be propagated to the view. - PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [ - :@_params, :@_response, :@_request, - :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ] + PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i( + @_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class + @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy + ) def _protected_ivars # :nodoc: PROTECTED_IVARS diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index a0917b4fdb..d29a5fe68f 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -51,7 +51,7 @@ module ActionController def unpermitted_parameters(event) debug do unpermitted_keys = event.payload[:keys] - "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}" + "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}" end end @@ -59,7 +59,7 @@ module ActionController expire_fragment expire_page write_page).each do |method| class_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{method}(event) - return unless logger.info? + return unless logger.info? && ActionController::Base.enable_fragment_cache_logging key_or_path = event.payload[:key] || event.payload[:path] human_name = #{method.to_s.humanize.inspect} info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)") diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index f6e67b02d7..075e4504c2 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,7 +1,7 @@ -require 'active_support/core_ext/array/extract_options' -require 'action_dispatch/middleware/stack' -require 'action_dispatch/http/request' -require 'action_dispatch/http/response' +require "active_support/core_ext/array/extract_options" +require "action_dispatch/middleware/stack" +require "action_dispatch/http/request" +require "action_dispatch/http/response" module ActionController # Extend ActionDispatch middleware stack to make it aware of options @@ -34,29 +34,29 @@ module ActionController private - INCLUDE = ->(list, action) { list.include? action } - EXCLUDE = ->(list, action) { !list.include? action } - NULL = ->(list, action) { true } - - def build_middleware(klass, args, block) - options = args.extract_options! - only = Array(options.delete(:only)).map(&:to_s) - except = Array(options.delete(:except)).map(&:to_s) - args << options unless options.empty? - - strategy = NULL - list = nil - - if only.any? - strategy = INCLUDE - list = only - elsif except.any? - strategy = EXCLUDE - list = except - end + INCLUDE = ->(list, action) { list.include? action } + EXCLUDE = ->(list, action) { !list.include? action } + NULL = ->(list, action) { true } + + def build_middleware(klass, args, block) + options = args.extract_options! + only = Array(options.delete(:only)).map(&:to_s) + except = Array(options.delete(:except)).map(&:to_s) + args << options unless options.empty? + + strategy = NULL + list = nil + + if only.any? + strategy = INCLUDE + list = only + elsif except.any? + strategy = EXCLUDE + list = except + end - Middleware.new(get_class(klass), args, list, strategy, block) - end + Middleware.new(get_class(klass), args, list, strategy, block) + end end # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a @@ -130,7 +130,7 @@ module ActionController # ==== Returns # * <tt>string</tt> def self.controller_name - @controller_name ||= name.demodulize.sub(/Controller$/, '').underscore + @controller_name ||= name.demodulize.sub(/Controller$/, "").underscore end def self.make_response!(request) @@ -139,15 +139,19 @@ module ActionController end end + def self.encoding_for_param(action, param) # :nodoc: + ::Encoding::UTF_8 + end + # Delegates to the class' <tt>controller_name</tt> def controller_name self.class.controller_name end attr_internal :response, :request - delegate :session, :to => "@_request" + delegate :session, to: "@_request" delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, :to => "@_response" + :status, :location, :content_type, to: "@_response" def initialize @_request = nil diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 480e265e44..89bf60a0bb 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/hash/keys' +require "active_support/core_ext/hash/keys" module ActionController module ConditionalGet @@ -129,7 +129,7 @@ module ActionController # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the # +:weak_etag+ option. # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response. - # requests that set If-None-Match header may return a 304 Not Modified + # Requests that set If-None-Match header may return a 304 Not Modified # response if it matches the ETag exactly. A weak ETag indicates semantic # equivalence, not byte-for-byte equality, so they're good for caching # HTML pages in browser caches. They can't be used for responses that @@ -232,20 +232,21 @@ module ActionController # The method will also ensure an HTTP Date header for client compatibility. def expires_in(seconds, options = {}) response.cache_control.merge!( - :max_age => seconds, - :public => options.delete(:public), - :must_revalidate => options.delete(:must_revalidate) + max_age: seconds, + public: options.delete(:public), + must_revalidate: options.delete(:must_revalidate) ) options.delete(:private) - response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"} + response.cache_control[:extras] = options.map { |k,v| "#{k}=#{v}" } response.date = Time.now unless response.date? end - # Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should - # occur by the browser or intermediate caches (like caching proxy servers). + # Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the + # resource will be marked as stale, so clients must always revalidate. + # Intermediate/browser caches may still store the asset. def expires_now - response.cache_control.replace(:no_cache => true) + response.cache_control.replace(no_cache: true) end # Cache or yield the block. The cache is supposed to never expire. diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 6cd6130032..f089c8423b 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -1,4 +1,4 @@ -require 'action_controller/metal/exceptions' +require "action_controller/metal/exceptions" module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, @@ -8,8 +8,8 @@ module ActionController #:nodoc: include ActionController::Rendering - DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc: - DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc: + DEFAULT_SEND_FILE_TYPE = "application/octet-stream".freeze #:nodoc: + DEFAULT_SEND_FILE_DISPOSITION = "attachment".freeze #:nodoc: protected # Sends the file. This uses a server-appropriate method (such as X-Sendfile) @@ -64,12 +64,13 @@ module ActionController #:nodoc: # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 # for the Cache-Control header spec. def send_file(path, options = {}) #:doc: - raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) + raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path) options[:filename] ||= File.basename(path) unless options[:url_based_filename] send_file_headers! options self.status = options[:status] || 200 + self.content_type = options[:type] if options.key?(:type) self.content_type = options[:content_type] if options.key?(:content_type) response.send_file path end @@ -122,7 +123,7 @@ module ActionController #:nodoc: else if !type_provided && options[:filename] # If type wasn't provided, try guessing from file extension. - content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type + content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete(".")) || content_type end self.content_type = content_type end @@ -131,10 +132,10 @@ module ActionController #:nodoc: unless disposition.nil? disposition = disposition.to_s disposition += %(; filename="#{options[:filename]}") if options[:filename] - headers['Content-Disposition'] = disposition + headers["Content-Disposition"] = disposition end - headers['Content-Transfer-Encoding'] = 'binary' + headers["Content-Transfer-Encoding"] = "binary" response.sending_file = true diff --git a/actionpack/lib/action_controller/metal/etag_with_flash.rb b/actionpack/lib/action_controller/metal/etag_with_flash.rb new file mode 100644 index 0000000000..474d75f02e --- /dev/null +++ b/actionpack/lib/action_controller/metal/etag_with_flash.rb @@ -0,0 +1,16 @@ +module ActionController + # When you're using the flash, it's generally used as a conditional on the view. + # This means the content of the view depends on the flash. Which in turn means + # that the etag for a response should be computed with the content of the flash + # in mind. This does that by including the content of the flash as a component + # in the etag that's generated for a response. + module EtagWithFlash + extend ActiveSupport::Concern + + include ActionController::ConditionalGet + + included do + etag { flash unless flash.empty? } + end + end +end diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb index 669cf55bca..6c103bb042 100644 --- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb +++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb @@ -33,18 +33,24 @@ module ActionController end private - def determine_template_etag(options) - if template = pick_template_for_etag(options) - lookup_and_digest_template(template) + def determine_template_etag(options) + if template = pick_template_for_etag(options) + lookup_and_digest_template(template) + end end - end - def pick_template_for_etag(options) - options.fetch(:template) { "#{controller_name}/#{action_name}" } - end + # Pick the template digest to include in the ETag. If the +:template+ option + # is present, use the named template. If +:template+ is nil or absent, use + # the default controller/action template. If +:template+ is false, omit the + # template digest from the ETag. + def pick_template_for_etag(options) + unless options[:template] == false + options[:template] || "#{controller_path}/#{action_name}" + end + end - def lookup_and_digest_template(template) - ActionView::Digestor.digest name: template, finder: lookup_context - end + def lookup_and_digest_template(template) + ActionView::Digestor.digest name: template, finder: lookup_context + end end end diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index 5c0ada37be..56a4b085e2 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -35,7 +35,7 @@ module ActionController class MethodNotAllowed < ActionControllerError #:nodoc: def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") + super("Only #{allowed_methods.to_sentence(locale: :en)} requests are allowed.") end end @@ -49,7 +49,7 @@ module ActionController end class SessionOverflowError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' + DEFAULT_MESSAGE = "Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data." def initialize(message = nil) super(message || DEFAULT_MESSAGE) diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index ea8e91ce24..b8976497a4 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -1,5 +1,5 @@ -require 'active_support/core_ext/hash/except' -require 'active_support/core_ext/hash/slice' +require "active_support/core_ext/hash/except" +require "active_support/core_ext/hash/slice" module ActionController # This module provides a method which will redirect the browser to use HTTPS @@ -76,10 +76,10 @@ module ActionController def force_ssl_redirect(host_or_options = nil) unless request.ssl? options = { - :protocol => 'https://', - :host => request.host, - :path => request.fullpath, - :status => :moved_permanently + protocol: "https://", + host: request.host, + path: request.fullpath, + status: :moved_permanently } if host_or_options.is_a?(Hash) diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 5e9832fd4e..86b5eb20d7 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -18,7 +18,7 @@ module ActionController # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols. def head(status, options = {}) if status.is_a?(Hash) - msg = status[:status] ? 'The :status option' : 'The implicit :ok status' + msg = status[:status] ? "The :status option" : "The implicit :ok status" options, status = status, status.delete(:status) ActiveSupport::Deprecation.warn(<<-MSG.squish) @@ -33,7 +33,7 @@ module ActionController content_type = options.delete(:content_type) options.each do |key, value| - headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s + headers[key.to_s.dasherize.split("-").each { |v| v[0] = v[0].chr.upcase }.join("-")] = value.to_s end self.status = status @@ -41,7 +41,7 @@ module ActionController self.response_body = "" - if include_content?(self.response_code) + if include_content?(response_code) self.content_type = content_type || (Mime[formats.first] if formats) self.response.charset = false end @@ -50,15 +50,15 @@ module ActionController end private - def include_content?(status) - case status - when 100..199 - false - when 204, 205, 304 - false - else - true + def include_content?(status) + case status + when 100..199 + false + when 204, 205, 304 + false + else + true + end end - end end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index d3853e2e83..476d081239 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -5,7 +5,7 @@ module ActionController # # In addition to using the standard template helpers provided, creating custom helpers to # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller - # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt> + # will include all helpers. These helpers are only accessible on the controller through <tt>#helpers</tt> # # In previous versions of \Rails the controller will include a helper which # matches the name of the controller, e.g., <tt>MyController</tt> will automatically @@ -71,7 +71,7 @@ module ActionController attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } end - # Provides a proxy to access helpers methods from outside the view. + # Provides a proxy to access helper methods from outside the view. def helpers @helper_proxy ||= begin proxy = ActionView::Base.new @@ -108,10 +108,15 @@ module ActionController end private - # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt> - def all_application_helpers - all_helpers_from_path(helpers_path) - end + # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt> + def all_application_helpers + all_helpers_from_path(helpers_path) + end + end + + # Provides a proxy to access helper methods from outside the view. + def helpers + @_helper_proxy ||= view_context end end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 4639348509..a335bf109e 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -1,5 +1,5 @@ -require 'base64' -require 'active_support/security_utils' +require "base64" +require "active_support/security_utils" module ActionController # Makes it dead easy to do HTTP Basic, Digest and Token authentication. @@ -99,23 +99,23 @@ module ActionController end def has_basic_credentials?(request) - request.authorization.present? && (auth_scheme(request).downcase == 'basic') + request.authorization.present? && (auth_scheme(request).downcase == "basic") end def user_name_and_password(request) - decode_credentials(request).split(':', 2) + decode_credentials(request).split(":", 2) end def decode_credentials(request) - ::Base64.decode64(auth_param(request) || '') + ::Base64.decode64(auth_param(request) || "") end def auth_scheme(request) - request.authorization.to_s.split(' ', 2).first + request.authorization.to_s.split(" ", 2).first end def auth_param(request) - request.authorization.to_s.split(' ', 2).second + request.authorization.to_s.split(" ", 2).second end def encode_credentials(user_name, password) @@ -208,7 +208,7 @@ module ActionController password = password_procedure.call(credentials[:username]) return false unless password - method = request.get_header('rack.methodoverride.original_method') || request.get_header('REQUEST_METHOD') + method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD") uri = credentials[:uri] [true, false].any? do |trailing_question_mark| @@ -226,17 +226,17 @@ module ActionController # of a plain-text password. def expected_response(http_method, uri, credentials, password, password_is_ha1=true) ha1 = password_is_ha1 ? password : ha1(credentials, password) - ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) - ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) + ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":")) + ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":")) end def ha1(credentials, password) - ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":")) end def encode_credentials(http_method, credentials, password, password_is_ha1) credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) - "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ') + "Digest " + credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}='#{v[1]}'" }.join(", ") end def decode_credentials_header(request) @@ -244,9 +244,9 @@ module ActionController end def decode_credentials(header) - ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair| - key, value = pair.split('=', 2) - [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')] + ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair| + key, value = pair.split("=", 2) + [key.strip, value.to_s.gsub(/^"|"$/,"").delete('\'')] end] end @@ -324,7 +324,6 @@ module ActionController def opaque(secret_key) ::Digest::MD5.hexdigest(secret_key) end - end # Makes it dead easy to do HTTP Token authentication. @@ -406,7 +405,7 @@ module ActionController # # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] module Token - TOKEN_KEY = 'token=' + TOKEN_KEY = "token=" TOKEN_REGEX = /^(Token|Bearer)\s+/ AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/ extend self @@ -476,14 +475,14 @@ module ActionController # This removes the <tt>"</tt> characters wrapping the value. def rewrite_param_values(array_params) - array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' } + array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, "" } end # This method takes an authorization body and splits up the key-value # pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt> # delimiters defined in +AUTHN_PAIR_DELIMITERS+. def raw_params(auth) - _raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/) + _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/) if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}}) _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}" diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb index 6192fc0f9c..8615c16c6f 100644 --- a/actionpack/lib/action_controller/metal/implicit_render.rb +++ b/actionpack/lib/action_controller/metal/implicit_render.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/string/strip' +require "active_support/core_ext/string/strip" module ActionController # Handles implicit rendering for a controller action that does not @@ -27,7 +27,6 @@ module ActionController # Finally, if we DON'T find a template AND the request isn't a browser page # load, then we implicitly respond with 204 No Content. module ImplicitRender - # :stopdoc: include BasicImplicitRender @@ -49,7 +48,7 @@ module ActionController "NOTE! For XHR/Ajax or API requests, this action would normally " \ "respond with 204 No Content: an empty white screen. Since you're " \ "loading it in a web browser, we assume that you expected to " \ - "actually render a template, not… nothing, so we're showing an " \ + "actually render a template, not nothing, so we're showing an " \ "error to be extra-clear. If you expect 204 No Content, carry on. " \ "That's what you'll get from an XHR or API request. Give it a shot." @@ -62,8 +61,8 @@ module ActionController def method_for_action(action_name) super || if template_exists?(action_name.to_s, _prefixes) - "default_render" - end + "default_render" + end end private diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 624a6d5b76..2ede96c667 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -1,5 +1,5 @@ -require 'benchmark' -require 'abstract_controller/logger' +require "benchmark" +require "abstract_controller/logger" module ActionController # Adds instrumentation to several ends in ActionController::Base. It also provides @@ -16,13 +16,13 @@ module ActionController def process_action(*args) raw_payload = { - :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 + controller: self.class.name, + action: action_name, + params: request.filtered_parameters, + headers: request.headers, + format: request.format.ref, + method: request.request_method, + path: request.fullpath } ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup) @@ -48,7 +48,7 @@ module ActionController def send_file(path, options={}) ActiveSupport::Notifications.instrument("send_file.action_controller", - options.merge(:path => path)) do + options.merge(path: path)) do super end end @@ -72,7 +72,7 @@ module ActionController # A hook invoked every time a before callback is halted. def halted_callback_hook(filter) - ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter) + ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter) end # A hook which allows you to clean up any time, wrongly taken into account in diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index 6055fde4f7..26a16104db 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -1,6 +1,6 @@ -require 'action_dispatch/http/response' -require 'delegate' -require 'active_support/json' +require "action_dispatch/http/response" +require "delegate" +require "active_support/json" module ActionController # Mix this module into your controller, and all actions in that controller @@ -84,7 +84,6 @@ module ActionController # Note: SSEs are not currently supported by IE. However, they are supported # by Chrome, Firefox, Opera, and Safari. class SSE - WHITELISTED_OPTIONS = %w( retry event id ) def initialize(stream, options = {}) @@ -163,14 +162,6 @@ module ActionController end end - def each - @response.sending! - while str = @buf.pop - yield str - end - @response.sent! - end - # Write a 'close' event to the buffer; the producer/writing thread # uses this to notify us that it's finished supplying content. # @@ -210,23 +201,36 @@ module ActionController def call_on_error @error_callback.call end + + private + + def each_chunk(&block) + loop do + str = nil + ActiveSupport::Dependencies.interlock.permit_concurrent_loads do + str = @buf.pop + end + break unless str + yield str + end + end end class Response < ActionDispatch::Response #:nodoc: all private - def before_committed - super - jar = request.cookie_jar - # The response can be committed multiple times - jar.write self unless committed? - end + def before_committed + super + jar = request.cookie_jar + # The response can be committed multiple times + jar.write self unless committed? + end - def build_buffer(response, body) - buf = Live::Buffer.new response - body.each { |part| buf.write part } - buf - end + def build_buffer(response, body) + buf = Live::Buffer.new response + body.each { |part| buf.write part } + buf + end end def process(name) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 2e89af1a5e..f6aabcb102 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -1,4 +1,4 @@ -require 'abstract_controller/collector' +require "abstract_controller/collector" module ActionController #:nodoc: module MimeResponds @@ -280,8 +280,8 @@ module ActionController #:nodoc: def any(*args, &block) if block_given? - if args.any? && args.none?{ |a| a == @variant } - args.each{ |v| @variants[v] = block } + if args.any? && args.none? { |a| a == @variant } + args.each { |v| @variants[v] = block } else @variants[:any] = block end diff --git a/actionpack/lib/action_controller/metal/parameter_encoding.rb b/actionpack/lib/action_controller/metal/parameter_encoding.rb new file mode 100644 index 0000000000..c457fd0d06 --- /dev/null +++ b/actionpack/lib/action_controller/metal/parameter_encoding.rb @@ -0,0 +1,30 @@ +module ActionController + # Allows encoding to be specified per parameter per action. + module ParameterEncoding + extend ActiveSupport::Concern + + module ClassMethods + def inherited(klass) # :nodoc: + super + klass.setup_param_encode + end + + def setup_param_encode # :nodoc: + @_parameter_encodings = {} + end + + def encoding_for_param(action, param) # :nodoc: + if @_parameter_encodings[action.to_s] && @_parameter_encodings[action.to_s][param.to_s] + @_parameter_encodings[action.to_s][param.to_s] + else + super + end + end + + def parameter_encoding(action, param_name, encoding) + @_parameter_encodings[action.to_s] ||= {} + @_parameter_encodings[action.to_s][param_name.to_s] = encoding + end + end + end +end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index c38fc40b81..9d1b740025 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,7 +1,7 @@ -require 'active_support/core_ext/hash/slice' -require 'active_support/core_ext/hash/except' -require 'active_support/core_ext/module/anonymous' -require 'action_dispatch/http/mime_type' +require "active_support/core_ext/hash/slice" +require "active_support/core_ext/hash/except" +require "active_support/core_ext/module/anonymous" +require "action_dispatch/http/mime_type" module ActionController # Wraps the parameters hash into a nested hash. This will allow clients to @@ -71,7 +71,7 @@ module ActionController EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8) - require 'mutex_m' + require "mutex_m" class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc: include Mutex_m @@ -128,30 +128,30 @@ module ActionController end private - # Determine the wrapper model from the controller's name. By convention, - # this could be done by trying to find the defined model that has the - # same singular name as the controller. For example, +UsersController+ - # will try to find if the +User+ model exists. - # - # This method also does namespace lookup. Foo::Bar::UsersController will - # try to find Foo::Bar::User, Foo::User and finally User. - def _default_wrap_model #:nodoc: - return nil if klass.anonymous? - model_name = klass.name.sub(/Controller$/, '').classify - - begin - if model_klass = model_name.safe_constantize - model_klass - else - namespaces = model_name.split("::") - namespaces.delete_at(-2) - break if namespaces.last == model_name - model_name = namespaces.join("::") - end - end until model_klass + # Determine the wrapper model from the controller's name. By convention, + # this could be done by trying to find the defined model that has the + # same singular name as the controller. For example, +UsersController+ + # will try to find if the +User+ model exists. + # + # This method also does namespace lookup. Foo::Bar::UsersController will + # try to find Foo::Bar::User, Foo::User and finally User. + def _default_wrap_model #:nodoc: + return nil if klass.anonymous? + model_name = klass.name.sub(/Controller$/, "").classify + + begin + if model_klass = model_name.safe_constantize + model_klass + else + namespaces = model_name.split("::") + namespaces.delete_at(-2) + break if namespaces.last == model_name + model_name = namespaces.join("::") + end + end until model_klass - model_klass - end + model_klass + end end included do @@ -198,9 +198,9 @@ module ActionController when Hash options = name_or_model_or_options when false - options = options.merge(:format => []) + options = options.merge(format: []) when Symbol, String - options = options.merge(:name => name_or_model_or_options) + options = options.merge(name: name_or_model_or_options) else model = name_or_model_or_options end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 3c7cc15627..2bd4296aff 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -111,7 +111,7 @@ module ActionController `fallback_location` represents the location to use if the request has no HTTP referer information. MESSAGE - request.headers["Referer"] or raise RedirectBackError + request.headers["Referer"] || raise(RedirectBackError) when Proc _compute_redirect_to_location request, options.call else diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 90fb34e386..f8a037189c 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -1,4 +1,4 @@ -require 'set' +require "set" module ActionController # See <tt>Renderers.add</tt> @@ -71,8 +71,6 @@ module ActionController # format.csv { render csv: @csvable, filename: @csvable.name } # end # end - # To use renderers and their mime types in more concise ways, see - # <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> def self.add(key, &block) define_method(_render_with_renderer_method_name(key), &block) RENDERERS << key.to_sym @@ -94,7 +92,6 @@ module ActionController end module ClassMethods - # Adds, by name, a renderer or renderers to the +_renderers+ available # to call within controller actions. # @@ -103,7 +100,7 @@ module ActionController # # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt> # include <tt>ActionController::Renderers::All</tt>, making all renderers - # avaialable in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>. + # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>. # # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>, diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index cce6fe7787..f8f91ed41c 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/string/filters' +require "active_support/core_ext/string/filters" module ActionController module Rendering @@ -32,7 +32,7 @@ module ActionController # Check for double render errors and set the content_type after rendering. def render(*args) #:nodoc: - raise ::AbstractController::DoubleRenderError if self.response_body + raise ::AbstractController::DoubleRenderError if response_body super end @@ -49,84 +49,84 @@ module ActionController end def render_to_body(options = {}) - super || _render_in_priorities(options) || ' ' + super || _render_in_priorities(options) || " " end private - def _render_in_priorities(options) - RENDER_FORMATS_IN_PRIORITY.each do |format| - return options[format] if options.key?(format) - end + def _render_in_priorities(options) + RENDER_FORMATS_IN_PRIORITY.each do |format| + return options[format] if options.key?(format) + end - nil - end + nil + end - def _set_html_content_type - self.content_type = Mime[:html].to_s - end + def _set_html_content_type + self.content_type = Mime[:html].to_s + end - def _set_rendered_content_type(format) - unless response.content_type - self.content_type = format.to_s + def _set_rendered_content_type(format) + unless response.content_type + self.content_type = format.to_s + end end - end - # Normalize arguments by catching blocks and setting them on :update. - def _normalize_args(action=nil, options={}, &blk) #:nodoc: - options = super - options[:update] = blk if block_given? - options - end + # Normalize arguments by catching blocks and setting them on :update. + def _normalize_args(action=nil, options={}, &blk) #:nodoc: + options = super + options[:update] = blk if block_given? + options + end - # Normalize both text and status options. - def _normalize_options(options) #:nodoc: - _normalize_text(options) + # Normalize both text and status options. + def _normalize_options(options) #:nodoc: + _normalize_text(options) - if options[:text] - ActiveSupport::Deprecation.warn <<-WARNING.squish + if options[:text] + ActiveSupport::Deprecation.warn <<-WARNING.squish `render :text` is deprecated because it does not actually render a `text/plain` response. Switch to `render plain: 'plain text'` to render as `text/plain`, `render html: '<strong>HTML</strong>'` to render as `text/html`, or `render body: 'raw'` to match the deprecated behavior and render with the default Content-Type, which is - `text/plain`. + `text/html`. WARNING - end + end - if options[:html] - options[:html] = ERB::Util.html_escape(options[:html]) - end + if options[:html] + options[:html] = ERB::Util.html_escape(options[:html]) + end - if options.delete(:nothing) - ActiveSupport::Deprecation.warn("`:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.") - options[:body] = nil - end + if options.delete(:nothing) + ActiveSupport::Deprecation.warn("`:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.") + options[:body] = nil + end - if options[:status] - options[:status] = Rack::Utils.status_code(options[:status]) - end + if options[:status] + options[:status] = Rack::Utils.status_code(options[:status]) + end - super - end + super + end - def _normalize_text(options) - RENDER_FORMATS_IN_PRIORITY.each do |format| - if options.key?(format) && options[format].respond_to?(:to_text) - options[format] = options[format].to_text + def _normalize_text(options) + RENDER_FORMATS_IN_PRIORITY.each do |format| + if options.key?(format) && options[format].respond_to?(:to_text) + options[format] = options[format].to_text + end end end - end - # Process controller specific options, as status, content-type and location. - def _process_options(options) #:nodoc: - status, content_type, location = options.values_at(:status, :content_type, :location) + # Process controller specific options, as status, content-type and location. + def _process_options(options) #:nodoc: + status, content_type, location = options.values_at(:status, :content_type, :location) - self.status = status if status - self.content_type = content_type if content_type - self.headers["Location"] = url_for(location) if location + self.status = status if status + self.content_type = content_type if content_type + self.headers["Location"] = url_for(location) if location - super - end + super + end end end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index f7e8d06f10..3d3c121280 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -1,6 +1,6 @@ -require 'rack/session/abstract/id' -require 'action_controller/metal/exceptions' -require 'active_support/security_utils' +require "rack/session/abstract/id" +require "action_controller/metal/exceptions" +require "active_support/security_utils" module ActionController #:nodoc: class InvalidAuthenticityToken < ActionControllerError #:nodoc: @@ -109,10 +109,10 @@ module ActionController #:nodoc: # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>. # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference. # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the - # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful - # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth). + # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful + # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth). # - # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>. + # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>. # * <tt>:with</tt> - Set the method to handle unverified request. # # Valid unverified request handling methods are: @@ -130,11 +130,11 @@ module ActionController #:nodoc: private - def protection_method_class(name) - ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify) - rescue NameError - raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session' - end + def protection_method_class(name) + ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify) + rescue NameError + raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session" + end end module ProtectionMethods @@ -154,26 +154,26 @@ module ActionController #:nodoc: protected - class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: - def initialize(req) - super(nil, req) - @data = {} - @loaded = true - end + class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: + def initialize(req) + super(nil, req) + @data = {} + @loaded = true + end - # no-op - def destroy; end + # no-op + def destroy; end - def exists? - true + def exists? + true + end end - end - class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc: - def write(*) - # nothing + class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc: + def write(*) + # nothing + end end - end end class ResetSession @@ -235,7 +235,9 @@ module ActionController #:nodoc: # we aren't serving an unauthorized cross-origin response. def verify_same_origin_request if marked_for_same_origin_verification? && non_xhr_javascript_response? - logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger + if logger && log_warning_on_csrf_failure + logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING + end raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING end end @@ -380,7 +382,7 @@ module ActionController #:nodoc: def xor_byte_strings(s1, s2) s2_bytes = s2.bytes s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 } - s2_bytes.pack('C*') + s2_bytes.pack("C*") end # The form's authenticity parameter. Override to provide your own. @@ -406,7 +408,7 @@ module ActionController #:nodoc: def normalize_action_path(action_path) uri = URI.parse(action_path) - uri.path.chomp('/') + uri.path.chomp("/") end end end diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index 0621a7368c..2d99e4045b 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -1,22 +1,11 @@ module ActionController #:nodoc: - # This module is responsible to provide `rescue_from` helpers - # to controllers and configure when detailed exceptions must be + # This module is responsible for providing `rescue_from` helpers + # to controllers and configuring when detailed exceptions must be # shown. module Rescue extend ActiveSupport::Concern include ActiveSupport::Rescuable - def rescue_with_handler(exception) - 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 - # Override this method if you want to customize when detailed # exceptions must be shown. This method is only called when # consider_all_requests_local is false. By default, it returns @@ -30,8 +19,8 @@ module ActionController #:nodoc: def process_action(*args) super rescue Exception => exception - request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions? - rescue_with_handler(exception) || raise(exception) + request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions? + rescue_with_handler(exception) || raise end end end diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index a6115674aa..481f19f1ef 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -1,4 +1,4 @@ -require 'rack/chunked' +require "rack/chunked" module ActionController #:nodoc: # Allows views to be streamed back to the client as they are rendered. diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index f9b80dd805..387c2aa0b9 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -1,12 +1,14 @@ -require 'active_support/core_ext/hash/indifferent_access' -require 'active_support/core_ext/hash/transform_values' -require 'active_support/core_ext/array/wrap' -require 'active_support/core_ext/string/filters' -require 'active_support/rescuable' -require 'action_dispatch/http/upload' -require 'rack/test' -require 'stringio' -require 'set' +require "active_support/core_ext/hash/indifferent_access" +require "active_support/core_ext/hash/transform_values" +require "active_support/core_ext/array/wrap" +require "active_support/core_ext/string/filters" +require "active_support/core_ext/object/to_query" +require "active_support/rescuable" +require "action_dispatch/http/upload" +require "rack/test" +require "stringio" +require "set" +require "yaml" module ActionController # Raised when a required parameter is missing. @@ -31,19 +33,19 @@ module ActionController # # params = ActionController::Parameters.new(a: "123", b: "456") # params.permit(:c) - # # => ActionController::UnpermittedParameters: found unpermitted parameters: a, b + # # => ActionController::UnpermittedParameters: found unpermitted parameters: :a, :b class UnpermittedParameters < IndexError attr_reader :params # :nodoc: def initialize(params) # :nodoc: @params = params - super("found unpermitted parameter#{'s' if params.size > 1 }: #{params.join(", ")}") + super("found unpermitted parameter#{'s' if params.size > 1 }: #{params.map { |e| ":#{e}" }.join(", ")}") end end # == Action Controller \Parameters # - # Allows to choose which attributes should be whitelisted for mass updating + # Allows you to choose which attributes should be whitelisted for mass updating # and thus prevent accidentally exposing that which shouldn't be exposed. # Provides two methods for this purpose: #require and #permit. The former is # used to mark parameters as required. The latter is used to set the parameter @@ -58,8 +60,7 @@ module ActionController # }) # # permitted = params.require(:person).permit(:name, :age) - # permitted # => {"name"=>"Francesco", "age"=>22} - # permitted.class # => ActionController::Parameters + # permitted # => <ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true> # permitted.permitted? # => true # # Person.first.update!(permitted) @@ -87,7 +88,7 @@ module ActionController # # params = ActionController::Parameters.new(a: "123", b: "456") # params.permit(:c) - # # => {} + # # => <ActionController::Parameters {} permitted: true> # # ActionController::Parameters.action_on_unpermitted_parameters = :raise # @@ -107,6 +108,8 @@ module ActionController # params["key"] # => "value" class Parameters cattr_accessor :permit_all_parameters, instance_accessor: false + self.permit_all_parameters = false + cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?, @@ -196,7 +199,7 @@ module ActionController end alias_method :to_unsafe_hash, :to_unsafe_h - # Convert all hashes in values into parameters, then yield each pair like + # Convert all hashes in values into parameters, then yield each pair in # the same way as <tt>Hash#each_pair</tt> def each_pair(&block) @parameters.each_pair do |key, value| @@ -255,7 +258,7 @@ module ActionController # either present or the singleton +false+, returns said value: # # ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person) - # # => {"name"=>"Francesco"} + # # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false> # # Otherwise raises <tt>ActionController::ParameterMissing</tt>: # @@ -276,12 +279,12 @@ module ActionController # returned: # # params = ActionController::Parameters.new(user: { ... }, profile: { ... }) - # user_params, profile_params = params.require(:user, :profile) + # user_params, profile_params = params.require([:user, :profile]) # - # Otherwise, the method reraises the first exception found: + # Otherwise, the method re-raises the first exception found: # # params = ActionController::Parameters.new(user: {}, profile: {}) - # user_params, profile_params = params.require(:user, :profile) + # user_params, profile_params = params.require([:user, :profile]) # # ActionController::ParameterMissing: param is missing or the value is empty: user # # Technically this method can be used to fetch terminal values: @@ -374,13 +377,13 @@ module ActionController # }) # # params.require(:person).permit(:contact) - # # => {} + # # => <ActionController::Parameters {} permitted: true> # # params.require(:person).permit(contact: :phone) - # # => {"contact"=>{"phone"=>"555-1234"}} + # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true> # # params.require(:person).permit(contact: [ :email, :phone ]) - # # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}} + # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true> def permit(*filters) params = self.class.new @@ -402,7 +405,7 @@ module ActionController # returns +nil+. # # params = ActionController::Parameters.new(person: { name: 'Francesco' }) - # params[:person] # => {"name"=>"Francesco"} + # params[:person] # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false> # params[:none] # => nil def [](key) convert_hashes_to_parameters(key, @parameters[key]) @@ -421,7 +424,7 @@ module ActionController # is given, then that will be run and its result returned. # # params = ActionController::Parameters.new(person: { name: 'Francesco' }) - # params.fetch(:person) # => {"name"=>"Francesco"} + # params.fetch(:person) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false> # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none # params.fetch(:none, 'Francesco') # => "Francesco" # params.fetch(:none) { 'Francesco' } # => "Francesco" @@ -441,12 +444,12 @@ module ActionController # 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 + # 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 + # params2 = ActionController::Parameters.new(foo: [10, 11, 12]) + # params2.dig(:foo, 1) # => 11 def dig(*keys) convert_value_to_parameters(@parameters.dig(*keys)) end @@ -457,8 +460,8 @@ module ActionController # don't exist, returns an empty hash. # # params = ActionController::Parameters.new(a: 1, b: 2, c: 3) - # params.slice(:a, :b) # => {"a"=>1, "b"=>2} - # params.slice(:d) # => {} + # params.slice(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false> + # params.slice(:d) # => <ActionController::Parameters {} permitted: false> def slice(*keys) new_instance_with_inherited_permitted_status(@parameters.slice(*keys)) end @@ -474,8 +477,8 @@ module ActionController # filters out the given +keys+. # # params = ActionController::Parameters.new(a: 1, b: 2, c: 3) - # params.except(:a, :b) # => {"c"=>3} - # params.except(:d) # => {"a"=>1,"b"=>2,"c"=>3} + # params.except(:a, :b) # => <ActionController::Parameters {"c"=>3} permitted: false> + # params.except(:d) # => <ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false> def except(*keys) new_instance_with_inherited_permitted_status(@parameters.except(*keys)) end @@ -483,8 +486,8 @@ module ActionController # Removes and returns the key/value pairs matching the given keys. # # params = ActionController::Parameters.new(a: 1, b: 2, c: 3) - # params.extract!(:a, :b) # => {"a"=>1, "b"=>2} - # params # => {"c"=>3} + # params.extract!(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false> + # params # => <ActionController::Parameters {"c"=>3} permitted: false> def extract!(*keys) new_instance_with_inherited_permitted_status(@parameters.extract!(*keys)) end @@ -494,7 +497,7 @@ module ActionController # # params = ActionController::Parameters.new(a: 1, b: 2, c: 3) # params.transform_values { |x| x * 2 } - # # => {"a"=>2, "b"=>4, "c"=>6} + # # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false> def transform_values(&block) if block new_instance_with_inherited_permitted_status( @@ -571,25 +574,11 @@ module ActionController convert_value_to_parameters(@parameters.values_at(*keys)) end - # Returns an exact copy of the <tt>ActionController::Parameters</tt> - # instance. +permitted+ state is kept on the duped object. - # - # params = ActionController::Parameters.new(a: 1) - # params.permit! - # params.permitted? # => true - # copy_params = params.dup # => {"a"=>1} - # copy_params.permitted? # => true - def dup - super.tap do |duplicate| - duplicate.permitted = @permitted - end - end - # Returns a new <tt>ActionController::Parameters</tt> with all keys from # +other_hash+ merges into current hash. def merge(other_hash) new_instance_with_inherited_permitted_status( - @parameters.merge(other_hash) + @parameters.merge(other_hash.to_h) ) end @@ -604,6 +593,37 @@ module ActionController "<#{self.class} #{@parameters} permitted: #{@permitted}>" end + def self.hook_into_yaml_loading # :nodoc: + # Wire up YAML format compatibility with Rails 4.2 and Psych 2.0.8 and 2.0.9+. + # Makes the YAML parser call `init_with` when it encounters the keys below + # instead of trying its own parsing routines. + YAML.load_tags["!ruby/hash-with-ivars:ActionController::Parameters"] = name + YAML.load_tags["!ruby/hash:ActionController::Parameters"] = name + end + hook_into_yaml_loading + + def init_with(coder) # :nodoc: + case coder.tag + when "!ruby/hash:ActionController::Parameters" + # YAML 2.0.8's format where hash instance variables weren't stored. + @parameters = coder.map.with_indifferent_access + @permitted = false + when "!ruby/hash-with-ivars:ActionController::Parameters" + # YAML 2.0.9's Hash subclass format where keys and values + # were stored under an elements hash and `permitted` within an ivars hash. + @parameters = coder.map["elements"].with_indifferent_access + @permitted = coder.map["ivars"][:@permitted] + when "!ruby/object:ActionController::Parameters" + # YAML's Object format. Only needed because of the format + # backwardscompability above, otherwise equivalent to YAML's initialization. + @parameters, @permitted = coder.map["parameters"], coder.map["permitted"] + end + end + + # Undefine `to_param` such that it gets caught in the `method_missing` + # deprecation cycle below. + undef_method :to_param + def method_missing(method_sym, *args, &block) if @parameters.respond_to?(method_sym) message = <<-DEPRECATE.squish @@ -704,7 +724,7 @@ module ActionController end def unpermitted_keys(params) - self.keys - params.keys - self.always_permitted_parameters + keys - params.keys - always_permitted_parameters end # @@ -735,7 +755,7 @@ module ActionController ] def permitted_scalar?(value) - PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)} + PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) } end def permitted_scalar_filter(params, key) @@ -751,7 +771,7 @@ module ActionController end def array_of_permitted_scalars?(value) - if value.is_a?(Array) && value.all? {|element| permitted_scalar?(element)} + if value.is_a?(Array) && value.all? { |element| permitted_scalar?(element) } yield value end end @@ -782,6 +802,11 @@ module ActionController end end end + + def initialize_copy(source) + super + @parameters = @parameters.dup + end end # == Strong \Parameters @@ -797,7 +822,7 @@ module ActionController # # class PeopleController < ActionController::Base # # Using "Person.create(params[:person])" would raise an - # # ActiveModel::ForbiddenAttributes exception because it'd + # # ActiveModel::ForbiddenAttributesError exception because it'd # # be using mass assignment without an explicit permit step. # # This is the recommended form: # def create diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb index ac37b00010..9bb416178a 100644 --- a/actionpack/lib/action_controller/metal/testing.rb +++ b/actionpack/lib/action_controller/metal/testing.rb @@ -13,7 +13,7 @@ module ActionController module ClassMethods def before_filters - _process_action_callbacks.find_all{|x| x.kind == :before}.map(&:name) + _process_action_callbacks.find_all { |x| x.kind == :before }.map(&:name) end end end diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index dbf7241a14..9f3cc099d6 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -27,10 +27,10 @@ module ActionController def url_options @_url_options ||= { - :host => request.host, - :port => request.optional_port, - :protocol => request.protocol, - :_recall => request.path_parameters + host: request.host, + port: request.optional_port, + protocol: request.protocol, + _recall: request.path_parameters }.merge!(super).freeze if (same_origin = _routes.equal?(request.routes)) || diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 28b20052b5..6513a556ee 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -11,7 +11,7 @@ module ActionController config.eager_load_namespaces << ActionController - initializer "action_controller.assets_config", :group => :all do |app| + initializer "action_controller.assets_config", group: :all do |app| app.config.action_controller.assets_dir ||= app.config.paths["public"].first end diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb index 2775a24e56..0739f16965 100644 --- a/actionpack/lib/action_controller/renderer.rb +++ b/actionpack/lib/action_controller/renderer.rb @@ -1,7 +1,7 @@ -require 'active_support/core_ext/hash/keys' +require "active_support/core_ext/hash/keys" module ActionController - # ActionController::Renderer allows to render arbitrary templates + # ActionController::Renderer allows you to render arbitrary templates # without requirement of being in controller actions. # # You get a concrete renderer class by invoking ActionController::Base#renderer. @@ -13,11 +13,11 @@ module ActionController # # ApplicationController.renderer.render template: '...' # - # You can use a shortcut on controller to replace previous example with: + # You can use this shortcut in a controller, instead of the previous example: # # ApplicationController.render template: '...' # - # #render method allows you to use any options as when rendering in controller. + # #render allows you to use the same options that you can use when rendering in a controller. # For example, # # FooController.render :action, locals: { ... }, assigns: { ... } @@ -37,11 +37,11 @@ module ActionController attr_reader :defaults, :controller DEFAULTS = { - http_host: 'example.org', + http_host: "example.org", https: false, - method: 'get', - script_name: '', - input: '' + method: "get", + script_name: "", + input: "" }.freeze # Create a new renderer instance for a specific controller class. @@ -69,7 +69,7 @@ module ActionController # Render templates with any options from ActionController::Base#render_to_string. def render(*args) - raise 'missing controller' unless controller + raise "missing controller" unless controller request = ActionDispatch::Request.new @env request.routes = controller._routes @@ -88,17 +88,17 @@ module ActionController end RACK_KEY_TRANSLATION = { - http_host: 'HTTP_HOST', - https: 'HTTPS', - method: 'REQUEST_METHOD', - script_name: 'SCRIPT_NAME', - input: 'rack.input' + http_host: "HTTP_HOST", + https: "HTTPS", + method: "REQUEST_METHOD", + script_name: "SCRIPT_NAME", + input: "rack.input" } IDENTITY = ->(_) { _ } RACK_VALUE_TRANSLATION = { - https: ->(v) { v ? 'on' : 'off' }, + https: ->(v) { v ? "on" : "off" }, method: ->(v) { v.upcase }, } diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index ecd21f29ce..f4ec13c831 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,13 +1,12 @@ -require 'rack/session/abstract/id' -require 'active_support/core_ext/hash/conversions' -require 'active_support/core_ext/object/to_query' -require 'active_support/core_ext/module/anonymous' -require 'active_support/core_ext/hash/keys' -require 'action_controller/template_assertions' -require 'rails-dom-testing' +require "rack/session/abstract/id" +require "active_support/core_ext/hash/conversions" +require "active_support/core_ext/object/to_query" +require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/hash/keys" +require "action_controller/template_assertions" +require "rails-dom-testing" module ActionController - # :stopdoc: class Metal include Testing::Functional end @@ -27,18 +26,20 @@ module ActionController # Please use ActionDispatch::IntegrationTest going forward. class TestRequest < ActionDispatch::TestRequest #:nodoc: DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup - DEFAULT_ENV.delete 'PATH_INFO' + DEFAULT_ENV.delete "PATH_INFO" def self.new_session TestSession.new end + attr_reader :controller_class + # Create a new test request with default `env` values - def self.create + def self.create(controller_class) env = {} env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application env["rack.request.cookie_hash"] = {}.with_indifferent_access - new(default_env.merge(env), new_session) + new(default_env.merge(env), new_session, controller_class) end def self.default_env @@ -46,13 +47,14 @@ module ActionController end private_class_method :default_env - def initialize(env, session) + def initialize(env, session, controller_class) super(env) self.session = session - self.session_options = TestSession::DEFAULT_OPTIONS + self.session_options = TestSession::DEFAULT_OPTIONS.dup + @controller_class = controller_class @custom_param_parsers = { - xml: lambda { |raw_post| Hash.from_xml(raw_post)['hash'] } + xml: lambda { |raw_post| Hash.from_xml(raw_post)["hash"] } } end @@ -61,7 +63,7 @@ module ActionController end def content_type=(type) - set_header 'CONTENT_TYPE', type + set_header "CONTENT_TYPE", type end def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys) @@ -83,7 +85,7 @@ module ActionController end if get? - if self.query_string.blank? + if query_string.blank? self.query_string = non_path_parameters.to_query end else @@ -91,8 +93,8 @@ module ActionController self.content_type = ENCODER.content_type data = ENCODER.build_multipart non_path_parameters else - fetch_header('CONTENT_TYPE') do |k| - set_header k, 'application/x-www-form-urlencoded' + fetch_header("CONTENT_TYPE") do |k| + set_header k, "application/x-www-form-urlencoded" end case content_mime_type.to_sym @@ -110,8 +112,8 @@ module ActionController end end - set_header 'CONTENT_LENGTH', data.length.to_s - set_header 'rack.input', StringIO.new(data) + set_header "CONTENT_LENGTH", data.length.to_s + set_header "rack.input", StringIO.new(data) end fetch_header("PATH_INFO") do |k| @@ -152,9 +154,9 @@ module ActionController private - def params_parsers - super.merge @custom_param_parsers - end + def params_parsers + super.merge @custom_param_parsers + end end class LiveTestResponse < Live::Response @@ -208,10 +210,18 @@ module ActionController end # Superclass for ActionController functional tests. Functional tests allow you to - # test a single controller action per test method. This should not be confused with - # integration tests (see ActionDispatch::IntegrationTest), which are more like - # "stories" that can involve multiple controllers and multiple actions (i.e. multiple - # different HTTP requests). + # test a single controller action per test method. + # + # == Use integration style controller tests over functional style controller tests. + # + # Rails discourages the use of functional tests in favor of integration tests + # (use ActionDispatch::IntegrationTest). + # + # New Rails applications no longer generate functional style controller tests and they should + # only be used for backward compatibility. Integration style controller tests perform actual + # requests, whereas functional style controller tests merely simulate a request. Besides, + # integration tests are as fast as functional tests and provide lot of helpers such as +as+, + # +parsed_body+ for effective testing of controller actions including even API endpoints. # # == Basic example # @@ -320,7 +330,6 @@ module ActionController attr_reader :response, :request module ClassMethods - # Sets the controller class name. Useful if the name can't be inferred from test class. # Normalizes +controller_class+ before using. # @@ -419,11 +428,11 @@ module ActionController `get :index, xhr: true` and `post :create, xhr: true` MSG - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ') + @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" + @request.env["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ") __send__(*args).tap do - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' + @request.env.delete "HTTP_X_REQUESTED_WITH" + @request.env.delete "HTTP_ACCEPT" end end alias xhr :xml_http_request @@ -440,6 +449,8 @@ module ActionController # - +session+: A hash of parameters to store in the session. This may be +nil+. # - +flash+: A hash of parameters to store in the flash. This may be +nil+. # - +format+: Request format. Defaults to +nil+. Can be string or symbol. + # - +as+: Content type. Defaults to +nil+. Must be a symbol that corresponds + # to a mime type. # # Example calling +create+ action and sending two params: # @@ -460,12 +471,12 @@ module ActionController check_required_ivars if kwarg_request?(args) - parameters, session, body, flash, http_method, format, xhr = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr) + parameters, session, body, flash, http_method, format, xhr, as = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr, :as) else http_method, parameters, session, flash = args format = nil - if parameters.is_a?(String) && http_method != 'HEAD' + if parameters.is_a?(String) && http_method != "HEAD" body = parameters parameters = nil end @@ -476,7 +487,7 @@ module ActionController end if body - @request.set_header 'RAW_POST_DATA', body + @request.set_header "RAW_POST_DATA", body end if http_method @@ -487,23 +498,28 @@ module ActionController parameters ||= {} - if format - parameters[:format] = format - end - @html_document = nil - self.cookies.update @request.cookies - self.cookies.update_cookies_from_jar - @request.set_header 'HTTP_COOKIE', cookies.to_header - @request.delete_header 'action_dispatch.cookies' + cookies.update(@request.cookies) + cookies.update_cookies_from_jar + @request.set_header "HTTP_COOKIE", cookies.to_header + @request.delete_header "action_dispatch.cookies" - @request = TestRequest.new scrub_env!(@request.env), @request.session + @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class @response = build_response @response_klass @response.request = @request @controller.recycle! - @request.set_header 'REQUEST_METHOD', http_method + @request.set_header "REQUEST_METHOD", http_method + + if as + @request.content_type = Mime[as].to_s + format ||= as + end + + if format + parameters[:format] = format + end parameters = parameters.symbolize_keys @@ -517,9 +533,9 @@ module ActionController @request.flash.update(flash || {}) if xhr - @request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest' - @request.fetch_header('HTTP_ACCEPT') do |k| - @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ') + @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest" + @request.fetch_header("HTTP_ACCEPT") do |k| + @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ") end end @@ -527,32 +543,37 @@ module ActionController @request.set_header k, @controller.config.relative_url_root end - @controller.recycle! - @controller.dispatch(action, @request, @response) - @request = @controller.request - @response = @controller.response + begin + @controller.recycle! + @controller.dispatch(action, @request, @response) + ensure + @request = @controller.request + @response = @controller.response - @request.delete_header 'HTTP_COOKIE' + @request.delete_header "HTTP_COOKIE" - if @request.have_cookie_jar? - unless @request.cookie_jar.committed? - @request.cookie_jar.write(@response) - self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) + if @request.have_cookie_jar? + unless @request.cookie_jar.committed? + @request.cookie_jar.write(@response) + cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) + end end - end - @response.prepare! + @response.prepare! - if flash_value = @request.flash.to_session_value - @request.session['flash'] = flash_value - else - @request.session.delete('flash') - end + if flash_value = @request.flash.to_session_value + @request.session["flash"] = flash_value + else + @request.session.delete("flash") + end - if xhr - @request.delete_header 'HTTP_X_REQUESTED_WITH' - @request.delete_header 'HTTP_ACCEPT' + if xhr + @request.delete_header "HTTP_X_REQUESTED_WITH" + @request.delete_header "HTTP_ACCEPT" + end + @request.query_string = "" + + @response.sent! end - @request.query_string = '' @response end @@ -587,7 +608,7 @@ module ActionController end end - @request = TestRequest.create + @request = TestRequest.create(@controller.class) @response = build_response @response_klass @response.request = @request @@ -606,71 +627,67 @@ module ActionController include ActionDispatch::Assertions class_attribute :_controller_class setup :setup_controller_request_and_response + ActiveSupport.run_load_hooks(:action_controller_test_case, self) end private - def scrub_env!(env) - env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } - env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } - env.delete 'action_dispatch.request.query_parameters' - env.delete 'action_dispatch.request.request_parameters' - env - end + def scrub_env!(env) + env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } + env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } + env.delete "action_dispatch.request.query_parameters" + env.delete "action_dispatch.request.request_parameters" + env["rack.input"] = StringIO.new + env + end - def process_with_kwargs(http_method, action, *args) - if kwarg_request?(args) - args.first.merge!(method: http_method) - process(action, *args) - else - non_kwarg_request_warning if args.any? + def process_with_kwargs(http_method, action, *args) + if kwarg_request?(args) + args.first.merge!(method: http_method) + process(action, *args) + else + non_kwarg_request_warning if args.any? - args = args.unshift(http_method) - process(action, *args) + args = args.unshift(http_method) + process(action, *args) + end end - end - REQUEST_KWARGS = %i(params session flash method body xhr) - def kwarg_request?(args) - args[0].respond_to?(:keys) && ( - (args[0].key?(:format) && args[0].keys.size == 1) || - args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) } - ) - end + REQUEST_KWARGS = %i(params session flash method body xhr) + def kwarg_request?(args) + args[0].respond_to?(:keys) && ( + (args[0].key?(:format) && args[0].keys.size == 1) || + args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) } + ) + end - def non_kwarg_request_warning - ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc) - ActionController::TestCase HTTP request methods will accept only - keyword arguments in future Rails versions. + def non_kwarg_request_warning + ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc) + ActionController::TestCase HTTP request methods will accept only + keyword arguments in future Rails versions. - Examples: + Examples: - get :show, params: { id: 1 }, session: { user_id: 1 } - process :update, method: :post, params: { id: 1 } - MSG - end + get :show, params: { id: 1 }, session: { user_id: 1 } + process :update, method: :post, params: { id: 1 } + MSG + end - def document_root_element - html_document.root - end + def document_root_element + html_document.root + end - def check_required_ivars - # Sanity check for required instance variables so we can give an - # understandable error message. - [:@routes, :@controller, :@request, :@response].each do |iv_name| - if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil? - raise "#{iv_name} is nil: make sure you set it in your test's setup method." + def check_required_ivars + # Sanity check for required instance variables so we can give an + # understandable error message. + [:@routes, :@controller, :@request, :@response].each do |iv_name| + if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil? + raise "#{iv_name} is nil: make sure you set it in your test's setup method." + end end end - end - - def html_format?(parameters) - return true unless parameters.key?(:format) - Mime.fetch(parameters[:format]) { Mime['html'] }.html? - end end include Behavior end - # :startdoc: end |