diff options
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 1 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/params_wrapper.rb | 160 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/strong_parameters.rb | 5 |
3 files changed, 106 insertions, 60 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 9b3bf99fc3..971c4189c8 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,4 +1,5 @@ require "action_controller/log_subscriber" +require "action_controller/metal/params_wrapper" module ActionController # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 09abc999c1..a475d4bdff 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/module/anonymous' +require 'active_support/core_ext/struct' require 'action_dispatch/http/mime_types' module ActionController @@ -72,12 +73,99 @@ module ActionController EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8) + require 'mutex_m' + + class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc: + include Mutex_m + + def self.from_hash(hash) + name = hash[:name] + format = Array(hash[:format]) + include = hash[:include] && Array(hash[:include]).collect(&:to_s) + exclude = hash[:exclude] && Array(hash[:exclude]).collect(&:to_s) + new name, format, include, exclude, nil, nil + end + + def initialize(name, format, include, exclude, klass, model) # nodoc + super + @include_set = include + @name_set = name + end + + def model + super || synchronize { super || self.model = _default_wrap_model } + end + + def include + return super if @include_set + + m = model + synchronize do + return super if @include_set + + @include_set = true + + unless super || exclude + if m.respond_to?(:attribute_names) && m.attribute_names.any? + self.include = m.attribute_names + end + end + end + end + + def name + return super if @name_set + + m = model + synchronize do + return super if @name_set + + @name_set = true + + unless super || klass.anonymous? + self.name = m ? m.to_s.demodulize.underscore : + klass.controller_name.singularize + end + end + 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 singularize 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 + end + included do class_attribute :_wrapper_options - self._wrapper_options = { :format => [] } + self._wrapper_options = Options.from_hash(format: []) end module ClassMethods + def _set_wrapper_options(options) + self._wrapper_options = Options.from_hash(options) + end + # Sets the name of the wrapper key, or the model which +ParamsWrapper+ # would use to determine the attribute names from. # @@ -119,68 +207,24 @@ module ActionController model = name_or_model_or_options end - _set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model) + opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options) + opts.model = model + opts.klass = self + + self._wrapper_options = opts end # Sets the default wrapper key or model which will be used to determine # wrapper key and attribute names. Will be called automatically when the # module is inherited. def inherited(klass) - if klass._wrapper_options[:format].present? - klass._set_wrapper_defaults(klass._wrapper_options.slice(:format)) + if klass._wrapper_options.format.any? + params = klass._wrapper_options.dup + params.klass = klass + klass._wrapper_options = params end super end - - protected - - # 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 singularize 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 self.anonymous? - model_name = self.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 - - def _set_wrapper_defaults(options, model=nil) - options = options.dup - - unless options[:include] || options[:exclude] - model ||= _default_wrap_model - if model.respond_to?(:attribute_names) && model.attribute_names.present? - options[:include] = model.attribute_names - end - end - - unless options[:name] || self.anonymous? - model ||= _default_wrap_model - options[:name] = model ? model.to_s.demodulize.underscore : - controller_name.singularize - end - - options[:include] = Array(options[:include]).collect(&:to_s) if options[:include] - options[:exclude] = Array(options[:exclude]).collect(&:to_s) if options[:exclude] - options[:format] = Array(options[:format]) - - self._wrapper_options = options - end end # Performs parameters wrapping upon the request. Will be called automatically @@ -205,20 +249,20 @@ module ActionController # Returns the wrapper key which will use to stored wrapped parameters. def _wrapper_key - _wrapper_options[:name] + _wrapper_options.name end # Returns the list of enabled formats. def _wrapper_formats - _wrapper_options[:format] + _wrapper_options.format end # Returns the list of parameters which will be selected for wrapped. def _wrap_parameters(parameters) - value = if include_only = _wrapper_options[:include] + value = if include_only = _wrapper_options.include parameters.slice(*include_only) else - exclude = _wrapper_options[:exclude] || [] + exclude = _wrapper_options.exclude || [] parameters.except(*(exclude + EXCLUDE_PARAMETERS)) end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 04dc1d37f7..da640502a2 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -65,7 +65,6 @@ module ActionController # params["key"] # => "value" class Parameters < ActiveSupport::HashWithIndifferentAccess cattr_accessor :permit_all_parameters, instance_accessor: false - attr_accessor :permitted # :nodoc: # Returns a new instance of <tt>ActionController::Parameters</tt>. # Also, sets the +permitted+ attribute to the default value of @@ -260,7 +259,9 @@ module ActionController # params.slice(:a, :b) # => {"a"=>1, "b"=>2} # params.slice(:d) # => {} def slice(*keys) - self.class.new(super) + self.class.new(super).tap do |new_instance| + new_instance.instance_variable_set :@permitted, @permitted + end end # Returns an exact copy of the <tt>ActionController::Parameters</tt> |