Skip to content
This repository
Newer
Older
100644 233 lines (207 sloc) 8.456 kb
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
1 require 'active_support/core_ext/class/attribute'
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
2 require 'active_support/core_ext/hash/slice'
35d0d82a »
2011-05-03 More performance optimizations.
3 require 'active_support/core_ext/hash/except'
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
4 require 'active_support/core_ext/array/wrap'
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
5 require 'action_dispatch/http/mime_types'
6
7 module ActionController
8 # Wraps parameters hash into nested hash. This will allow client to submit
9 # POST request without having to specify a root element in it.
10 #
c894fff6 »
2011-05-02 Fix ParamsWrapper docs errors
11 # By default this functionality won't be enabled. You can enable
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
12 # it globally by setting +ActionController::Base.wrap_parameters+:
13 #
14 # ActionController::Base.wrap_parameters = [:json]
15 #
16 # You could also turn it on per controller by setting the format array to
17 # non-empty array:
18 #
19 # class UsersController < ApplicationController
20 # wrap_parameters :format => [:json, :xml]
21 # end
22 #
23 # If you enable +ParamsWrapper+ for +:json+ format. Instead of having to
24 # send JSON parameters like this:
25 #
26 # {"user": {"name": "Konata"}}
27 #
28 # You can now just send a parameters like this:
29 #
30 # {"name": "Konata"}
31 #
32 # And it will be wrapped into a nested hash with the key name matching
33 # controller's name. For example, if you're posting to +UsersController+,
34 # your new +params+ hash will look like this:
35 #
36 # {"name" => "Konata", "user" => {"name" => "Konata"}}
37 #
38 # You can also specify the key in which the parameters should be wrapped to,
39 # and also the list of attributes it should wrap by using either +:only+ or
40 # +:except+ options like this:
41 #
42 # class UsersController < ApplicationController
43 # wrap_parameters :person, :only => [:username, :password]
44 # end
45 #
46 # If you're going to pass the parameters to an +ActiveModel+ object (such as
47 # +User.new(params[:user])+), you might consider passing the model class to
48 # the method instead. The +ParamsWrapper+ will actually try to determine the
49 # list of attribute names from the model and only wrap those attributes:
50 #
51 # class UsersController < ApplicationController
52 # wrap_parameters Person
53 # end
54 #
55 # You still could pass +:only+ and +:except+ to set the list of attributes
56 # you want to wrap.
57 #
58 # By default, if you don't specify the key in which the parameters would be
59 # wrapped to, +ParamsWrapper+ will actually try to determine if there's
60 # a model related to it or not. This controller, for example:
61 #
62 # class Admin::UsersController < ApplicationController
63 # end
64 #
65 # will try to check if +Admin::User+ or +User+ model exists, and use it to
66 # determine the wrapper key respectively. If both of the model doesn't exists,
67 # it will then fallback to use +user+ as the key.
68 module ParamsWrapper
69 extend ActiveSupport::Concern
70
71 EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
72
73 included do
74 class_attribute :_wrapper_options
75 self._wrapper_options = {:format => []}
76 end
77
78 module ClassMethods
79 # Sets the name of the wrapper key, or the model which +ParamsWrapper+
80 # would use to determine the attribute names from.
81 #
82 # ==== Examples
83 # wrap_parameters :format => :xml
c894fff6 »
2011-05-02 Fix ParamsWrapper docs errors
84 # # enables the parmeter wrapper for XML format
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
85 #
86 # wrap_parameters :person
87 # # wraps parameters into +params[:person]+ hash
88 #
89 # wrap_parameters Person
90 # # wraps parameters by determine the wrapper key from Person class
91 # (+person+, in this case) and the list of attribute names
92 #
93 # wrap_parameters :only => [:username, :title]
94 # # wraps only +:username+ and +:title+ attributes from parameters.
95 #
96 # wrap_parameters false
97 # # disable parameters wrapping for this controller altogether.
98 #
99 # ==== Options
100 # * <tt>:format</tt> - The list of formats in which the parameters wrapper
101 # will be enabled.
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
102 # * <tt>:only</tt> - The list of attribute names which parameters wrapper
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
103 # will wrap into a nested hash.
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
104 # * <tt>:except</tt> - The list of attribute names which parameters wrapper
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
105 # will exclude from a nested hash.
106 def wrap_parameters(name_or_model_or_options, options = {})
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
107 model = nil
108
109 case name_or_model_or_options
110 when Hash
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
111 options = name_or_model_or_options
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
112 when false
113 options = options.merge(:format => [])
114 when Symbol, String
115 options = options.merge(:name => name_or_model_or_options)
116 else
117 model = name_or_model_or_options
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
118 end
119
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
120 _set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
121 end
122
123 # Sets the default wrapper key or model which will be used to determine
124 # wrapper key and attribute names. Will be called automatically when the
125 # module is inherited.
126 def inherited(klass)
127 if klass._wrapper_options[:format].present?
785ee65d »
2011-05-06 Ensure params wrapper settings are not inherited and calculated each …
128 klass._set_wrapper_defaults(klass._wrapper_options.slice(:format))
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
129 end
130 super
131 end
132
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
133 protected
134
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
135 # Determine the wrapper model from the controller's name. By convention,
136 # this could be done by trying to find the defined model that has the
137 # same singularize name as the controller. For example, +UsersController+
138 # will try to find if the +User+ model exists.
a87894ae »
2011-05-11 Get around weird missing constant error caused by AS instead of simpl…
139 #
140 # This method also does namespace lookup. Foo::Bar::UsersController will
141 # try to find Foo::Bar::User, Foo::User and finally User.
142 def _default_wrap_model #:nodoc:
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
143 model_name = self.name.sub(/Controller$/, '').singularize
144
145 begin
146 model_klass = model_name.constantize
a87894ae »
2011-05-11 Get around weird missing constant error caused by AS instead of simpl…
147 rescue NameError, ArgumentError => e
d77b306b »
2011-05-15 Make ParamsWrapper calling newly introduced `Model.attribute_names` i…
148 if e.message =~ /is not missing constant|uninitialized constant #{model_name}/
a87894ae »
2011-05-11 Get around weird missing constant error caused by AS instead of simpl…
149 namespaces = model_name.split("::")
150 namespaces.delete_at(-2)
151 break if namespaces.last == model_name
152 model_name = namespaces.join("::")
153 else
154 raise
155 end
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
156 end until model_klass
157
158 model_klass
159 end
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
160
161 def _set_wrapper_defaults(options, model=nil)
162 options = options.dup
163
164 unless options[:only] || options[:except]
165 model ||= _default_wrap_model
d77b306b »
2011-05-15 Make ParamsWrapper calling newly introduced `Model.attribute_names` i…
166 if model.respond_to?(:attribute_names) && model.attribute_names.present?
167 options[:only] = model.attribute_names
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
168 end
169 end
170
171 unless options[:name]
172 model ||= _default_wrap_model
173 options[:name] = model ? model.to_s.demodulize.underscore :
174 controller_name.singularize
175 end
176
177 options[:only] = Array.wrap(options[:only]).collect(&:to_s) if options[:only]
178 options[:except] = Array.wrap(options[:except]).collect(&:to_s) if options[:except]
179 options[:format] = Array.wrap(options[:format])
180
181 self._wrapper_options = options
182 end
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
183 end
184
185 # Performs parameters wrapping upon the request. Will be called automatically
186 # by the metal call stack.
187 def process_action(*args)
188 if _wrapper_enabled?
35d0d82a »
2011-05-03 More performance optimizations.
189 wrapped_hash = _wrap_parameters request.request_parameters
190 wrapped_filtered_hash = _wrap_parameters request.filtered_parameters
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
191
192 # This will make the wrapped hash accessible from controller and view
193 request.parameters.merge! wrapped_hash
194 request.request_parameters.merge! wrapped_hash
195
196 # This will make the wrapped hash displayed in the log file
35d0d82a »
2011-05-03 More performance optimizations.
197 request.filtered_parameters.merge! wrapped_filtered_hash
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
198 end
199 super
200 end
201
202 private
35d0d82a »
2011-05-03 More performance optimizations.
203
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
204 # Returns the wrapper key which will use to stored wrapped parameters.
205 def _wrapper_key
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
206 _wrapper_options[:name]
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
207 end
208
209 # Returns the list of enabled formats.
210 def _wrapper_formats
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
211 _wrapper_options[:format]
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
212 end
213
35d0d82a »
2011-05-03 More performance optimizations.
214 # Returns the list of parameters which will be selected for wrapped.
215 def _wrap_parameters(parameters)
216 value = if only = _wrapper_options[:only]
217 parameters.slice(*only)
218 else
219 except = _wrapper_options[:except] || []
220 parameters.except(*(except + EXCLUDE_PARAMETERS))
221 end
222
223 { _wrapper_key => value }
224 end
225
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
226 # Checks if we should perform parameters wrapping.
227 def _wrapper_enabled?
4bddc06e »
2011-05-03 Move most processing to load time for performance and improve test su…
228 ref = request.content_mime_type.try(:ref)
a55f2de0 »
2011-05-03 Improve performance for filtered parameters and add tests.
229 _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key]
8c9e4d52 »
2011-04-28 Add `ActionController::ParamsWrapper` to wrap parameters into a neste…
230 end
231 end
232 end
Something went wrong with that request. Please try again.