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_type'moduleActionController# Wraps the parameters hash into a nested hash. This will allow clients to submit# POST requests without having to specify any root elements.## This functionality is enabled in +config/initializers/wrap_parameters.rb+# and can be customized. If you are upgrading to \Rails 3.1, this file will# need to be created for the functionality to be enabled.## You could also turn it on per controller by setting the format array to# a non-empty array:## class UsersController < ApplicationController# wrap_parameters format: [:json, :xml]# end## If you enable +ParamsWrapper+ for +:json+ format, instead of having to# send JSON parameters like this:## {"user": {"name": "Konata"}}## You can send parameters like this:## {"name": "Konata"}## And it will be wrapped into a nested hash with the key name matching the# controller's name. For example, if you're posting to +UsersController+,# your new +params+ hash will look like this:## {"name" => "Konata", "user" => {"name" => "Konata"}}## You can also specify the key in which the parameters should be wrapped to,# and also the list of attributes it should wrap by using either +:include+ or# +:exclude+ options like this:## class UsersController < ApplicationController# wrap_parameters :person, include: [:username, :password]# end## On ActiveRecord models with no +:include+ or +:exclude+ option set,# it will only wrap the parameters returned by the class method# <tt>attribute_names</tt>.## If you're going to pass the parameters to an +ActiveModel+ object (such as# <tt>User.new(params[:user])</tt>), you might consider passing the model class to# the method instead. The +ParamsWrapper+ will actually try to determine the# list of attribute names from the model and only wrap those attributes:## class UsersController < ApplicationController# wrap_parameters Person# end## You still could pass +:include+ and +:exclude+ to set the list of attributes# you want to wrap.## By default, if you don't specify the key in which the parameters would be# wrapped to, +ParamsWrapper+ will actually try to determine if there's# a model related to it or not. This controller, for example:## class Admin::UsersController < ApplicationController# end## will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to# determine the wrapper key respectively. If both models don't exist,# it will then fallback to use +user+ as the key.moduleParamsWrapperextendActiveSupport::ConcernEXCLUDE_PARAMETERS=%w(authenticity_token _method utf8)require'mutex_m'classOptions<Struct.new(:name,:format,:include,:exclude,:klass,:model)# :nodoc:includeMutex_mdefself.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)newname,format,include,exclude,nil,nilenddefinitialize(name,format,include,exclude,klass,model)# nodocsuper@include_set=include@name_set=nameenddefmodelsuper||synchronize{super||self.model=_default_wrap_model}enddefincludereturnsuperif@include_setm=modelsynchronizedoreturnsuperif@include_set@include_set=trueunlesssuper||excludeifm.respond_to?(:attribute_names)&&m.attribute_names.any?self.include=m.attribute_namesendendendenddefnamereturnsuperif@name_setm=modelsynchronizedoreturnsuperif@name_set@name_set=trueunlesssuper||klass.anonymous?self.name=m?m.to_s.demodulize.underscore:klass.controller_name.singularizeendendendprivate# 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:returnnilifklass.anonymous?model_name=klass.name.sub(/Controller$/,'').classifybeginifmodel_klass=model_name.safe_constantizemodel_klasselsenamespaces=model_name.split("::")namespaces.delete_at(-2)breakifnamespaces.last==model_namemodel_name=namespaces.join("::")endenduntilmodel_klassmodel_klassendendincludeddoclass_attribute:_wrapper_optionsself._wrapper_options=Options.from_hash(format: [])endmoduleClassMethodsdef_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.## ==== Examples# wrap_parameters format: :xml# # enables the parameter wrapper for XML format## wrap_parameters :person# # wraps parameters into +params[:person]+ hash## wrap_parameters Person# # wraps parameters by determining the wrapper key from Person class# (+person+, in this case) and the list of attribute names## wrap_parameters include: [:username, :title]# # wraps only +:username+ and +:title+ attributes from parameters.## wrap_parameters false# # disables parameters wrapping for this controller altogether.## ==== Options# * <tt>:format</tt> - The list of formats in which the parameters wrapper# will be enabled.# * <tt>:include</tt> - The list of attribute names which parameters wrapper# will wrap into a nested hash.# * <tt>:exclude</tt> - The list of attribute names which parameters wrapper# will exclude from a nested hash.defwrap_parameters(name_or_model_or_options,options={})model=nilcasename_or_model_or_optionswhenHashoptions=name_or_model_or_optionswhenfalseoptions=options.merge(:format=>[])whenSymbol,Stringoptions=options.merge(:name=>name_or_model_or_options)elsemodel=name_or_model_or_optionsendopts=Options.from_hash_wrapper_options.to_h.slice(:format).merge(options)opts.model=modelopts.klass=selfself._wrapper_options=optsend# 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.definherited(klass)ifklass._wrapper_options.format.any?params=klass._wrapper_options.dupparams.klass=klassklass._wrapper_options=paramsendsuperendend# Performs parameters wrapping upon the request. Will be called automatically# by the metal call stack.defprocess_action(*args)if_wrapper_enabled?wrapped_hash=_wrap_parametersrequest.request_parameterswrapped_keys=request.request_parameters.keyswrapped_filtered_hash=_wrap_parametersrequest.filtered_parameters.slice(*wrapped_keys)# This will make the wrapped hash accessible from controller and viewrequest.parameters.merge!wrapped_hashrequest.request_parameters.merge!wrapped_hash# This will make the wrapped hash displayed in the log filerequest.filtered_parameters.merge!wrapped_filtered_hashendsuperendprivate# Returns the wrapper key which will use to stored wrapped parameters.def_wrapper_key_wrapper_options.nameend# Returns the list of enabled formats.def_wrapper_formats_wrapper_options.formatend# Returns the list of parameters which will be selected for wrapped.def_wrap_parameters(parameters)value=ifinclude_only=_wrapper_options.includeparameters.slice(*include_only)elseexclude=_wrapper_options.exclude||[]parameters.except(*(exclude+EXCLUDE_PARAMETERS))end{_wrapper_key=>value}end# Checks if we should perform parameters wrapping.def_wrapper_enabled?ref=request.content_mime_type.try(:ref)_wrapper_formats.include?(ref)&&_wrapper_key&&!request.request_parameters[_wrapper_key]endendend