actionpack 4.2.10 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +553 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +52 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +10 -13
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +11 -32
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +10 -10
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +66 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +8 -8
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +85 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +293 -90
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/renderer.rb +111 -0
  48. data/lib/action_controller/template_assertions.rb +9 -0
  49. data/lib/action_controller/test_case.rb +288 -368
  50. data/lib/action_controller.rb +12 -9
  51. data/lib/action_dispatch/http/cache.rb +73 -34
  52. data/lib/action_dispatch/http/filter_parameters.rb +15 -11
  53. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  54. data/lib/action_dispatch/http/headers.rb +44 -13
  55. data/lib/action_dispatch/http/mime_negotiation.rb +41 -23
  56. data/lib/action_dispatch/http/mime_type.rb +126 -90
  57. data/lib/action_dispatch/http/mime_types.rb +3 -4
  58. data/lib/action_dispatch/http/parameter_filter.rb +18 -8
  59. data/lib/action_dispatch/http/parameters.rb +54 -41
  60. data/lib/action_dispatch/http/request.rb +149 -82
  61. data/lib/action_dispatch/http/response.rb +206 -102
  62. data/lib/action_dispatch/http/url.rb +117 -8
  63. data/lib/action_dispatch/journey/formatter.rb +39 -28
  64. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  65. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  66. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  67. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  68. data/lib/action_dispatch/journey/parser_extras.rb +4 -0
  69. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  70. data/lib/action_dispatch/journey/route.rb +74 -19
  71. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  72. data/lib/action_dispatch/journey/router.rb +5 -9
  73. data/lib/action_dispatch/journey/routes.rb +14 -15
  74. data/lib/action_dispatch/journey/visitors.rb +86 -43
  75. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +189 -135
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +124 -49
  78. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  79. data/lib/action_dispatch/middleware/executor.rb +19 -0
  80. data/lib/action_dispatch/middleware/flash.rb +66 -45
  81. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  82. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  83. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  84. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  85. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  91. data/lib/action_dispatch/middleware/ssl.rb +115 -36
  92. data/lib/action_dispatch/middleware/stack.rb +44 -40
  93. data/lib/action_dispatch/middleware/static.rb +51 -35
  94. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  95. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  98. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  99. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  100. data/lib/action_dispatch/railtie.rb +2 -2
  101. data/lib/action_dispatch/request/session.rb +69 -33
  102. data/lib/action_dispatch/request/utils.rb +51 -19
  103. data/lib/action_dispatch/routing/inspector.rb +32 -43
  104. data/lib/action_dispatch/routing/mapper.rb +491 -338
  105. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  106. data/lib/action_dispatch/routing/redirection.rb +3 -3
  107. data/lib/action_dispatch/routing/route_set.rb +145 -238
  108. data/lib/action_dispatch/routing/url_for.rb +27 -10
  109. data/lib/action_dispatch/routing.rb +17 -13
  110. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  111. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  112. data/lib/action_dispatch/testing/assertions/routing.rb +11 -10
  113. data/lib/action_dispatch/testing/assertions.rb +1 -1
  114. data/lib/action_dispatch/testing/integration.rb +368 -97
  115. data/lib/action_dispatch/testing/test_process.rb +5 -6
  116. data/lib/action_dispatch/testing/test_request.rb +22 -31
  117. data/lib/action_dispatch/testing/test_response.rb +7 -4
  118. data/lib/action_dispatch.rb +3 -1
  119. data/lib/action_pack/gem_version.rb +3 -3
  120. data/lib/action_pack.rb +1 -1
  121. metadata +30 -34
  122. data/lib/action_controller/metal/hide_actions.rb +0 -40
  123. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  124. data/lib/action_controller/middleware.rb +0 -39
  125. data/lib/action_controller/model_naming.rb +0 -12
  126. data/lib/action_dispatch/journey/backwards.rb +0 -5
  127. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  128. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  129. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  130. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  131. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,9 +1,10 @@
1
1
  require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'active_support/core_ext/hash/transform_values'
2
3
  require 'active_support/core_ext/array/wrap'
3
4
  require 'active_support/core_ext/string/filters'
4
- require 'active_support/deprecation'
5
5
  require 'active_support/rescuable'
6
6
  require 'action_dispatch/http/upload'
7
+ require 'rack/test'
7
8
  require 'stringio'
8
9
  require 'set'
9
10
 
@@ -12,9 +13,9 @@ module ActionController
12
13
  #
13
14
  # params = ActionController::Parameters.new(a: {})
14
15
  # params.fetch(:b)
15
- # # => ActionController::ParameterMissing: param not found: b
16
+ # # => ActionController::ParameterMissing: param is missing or the value is empty: b
16
17
  # params.require(:a)
17
- # # => ActionController::ParameterMissing: param not found: a
18
+ # # => ActionController::ParameterMissing: param is missing or the value is empty: a
18
19
  class ParameterMissing < KeyError
19
20
  attr_reader :param # :nodoc:
20
21
 
@@ -42,7 +43,7 @@ module ActionController
42
43
 
43
44
  # == Action Controller \Parameters
44
45
  #
45
- # Allows to choose which attributes should be whitelisted for mass updating
46
+ # Allows you to choose which attributes should be whitelisted for mass updating
46
47
  # and thus prevent accidentally exposing that which shouldn't be exposed.
47
48
  # Provides two methods for this purpose: #require and #permit. The former is
48
49
  # used to mark parameters as required. The latter is used to set the parameter
@@ -98,17 +99,19 @@ module ActionController
98
99
  # environment they should only be set once at boot-time and never mutated at
99
100
  # runtime.
100
101
  #
101
- # <tt>ActionController::Parameters</tt> inherits from
102
- # <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
103
- # that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
102
+ # You can fetch values of <tt>ActionController::Parameters</tt> using either
103
+ # <tt>:key</tt> or <tt>"key"</tt>.
104
104
  #
105
105
  # params = ActionController::Parameters.new(key: 'value')
106
106
  # params[:key] # => "value"
107
107
  # params["key"] # => "value"
108
- class Parameters < ActiveSupport::HashWithIndifferentAccess
108
+ class Parameters
109
109
  cattr_accessor :permit_all_parameters, instance_accessor: false
110
110
  cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
111
111
 
112
+ delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
113
+ :as_json, to: :@parameters
114
+
112
115
  # By default, never raise an UnpermittedParameters exception if these
113
116
  # params are present. The default includes both 'controller' and 'action'
114
117
  # because they are added by Rails and should be of no concern. One way
@@ -119,16 +122,6 @@ module ActionController
119
122
  cattr_accessor :always_permitted_parameters
120
123
  self.always_permitted_parameters = %w( controller action )
121
124
 
122
- def self.const_missing(const_name)
123
- super unless const_name == :NEVER_UNPERMITTED_PARAMS
124
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
125
- `ActionController::Parameters::NEVER_UNPERMITTED_PARAMS` has been deprecated.
126
- Use `ActionController::Parameters.always_permitted_parameters` instead.
127
- MSG
128
-
129
- always_permitted_parameters
130
- end
131
-
132
125
  # Returns a new instance of <tt>ActionController::Parameters</tt>.
133
126
  # Also, sets the +permitted+ attribute to the default value of
134
127
  # <tt>ActionController::Parameters.permit_all_parameters</tt>.
@@ -145,13 +138,32 @@ module ActionController
145
138
  # params = ActionController::Parameters.new(name: 'Francesco')
146
139
  # params.permitted? # => true
147
140
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
148
- def initialize(attributes = nil)
149
- super(attributes)
141
+ def initialize(parameters = {})
142
+ @parameters = parameters.with_indifferent_access
150
143
  @permitted = self.class.permit_all_parameters
151
144
  end
152
145
 
153
- # Returns a safe +Hash+ representation of this parameter with all
154
- # unpermitted keys removed.
146
+ # Returns true if another +Parameters+ object contains the same content and
147
+ # permitted flag.
148
+ def ==(other)
149
+ if other.respond_to?(:permitted?)
150
+ self.permitted? == other.permitted? && self.parameters == other.parameters
151
+ elsif other.is_a?(Hash)
152
+ ActiveSupport::Deprecation.warn <<-WARNING.squish
153
+ Comparing equality between `ActionController::Parameters` and a
154
+ `Hash` is deprecated and will be removed in Rails 5.1. Please only do
155
+ comparisons between instances of `ActionController::Parameters`. If
156
+ you need to compare to a hash, first convert it using
157
+ `ActionController::Parameters#new`.
158
+ WARNING
159
+ @parameters == other.with_indifferent_access
160
+ else
161
+ @parameters == other
162
+ end
163
+ end
164
+
165
+ # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
166
+ # representation of this parameter with all unpermitted keys removed.
155
167
  #
156
168
  # params = ActionController::Parameters.new({
157
169
  # name: 'Senjougahara Hitagi',
@@ -163,28 +175,34 @@ module ActionController
163
175
  # safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
164
176
  def to_h
165
177
  if permitted?
166
- to_hash
178
+ convert_parameters_to_hashes(@parameters, :to_h)
167
179
  else
168
180
  slice(*self.class.always_permitted_parameters).permit!.to_h
169
181
  end
170
182
  end
171
183
 
172
- # Returns an unsafe, unfiltered +Hash+ representation of this parameter.
184
+ # Returns an unsafe, unfiltered
185
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> representation of this
186
+ # parameter.
187
+ #
188
+ # params = ActionController::Parameters.new({
189
+ # name: 'Senjougahara Hitagi',
190
+ # oddity: 'Heavy stone crab'
191
+ # })
192
+ # params.to_unsafe_h
193
+ # # => {"name"=>"Senjougahara Hitagi", "oddity" => "Heavy stone crab"}
173
194
  def to_unsafe_h
174
- to_hash
195
+ convert_parameters_to_hashes(@parameters, :to_unsafe_h)
175
196
  end
176
197
  alias_method :to_unsafe_hash, :to_unsafe_h
177
198
 
178
- # Convert all hashes in values into parameters, then yield each pair like
199
+ # Convert all hashes in values into parameters, then yield each pair in
179
200
  # the same way as <tt>Hash#each_pair</tt>
180
201
  def each_pair(&block)
181
- super do |key, value|
182
- convert_hashes_to_parameters(key, value)
202
+ @parameters.each_pair do |key, value|
203
+ yield key, convert_hashes_to_parameters(key, value)
183
204
  end
184
-
185
- super
186
205
  end
187
-
188
206
  alias_method :each, :each_pair
189
207
 
190
208
  # Attribute that keeps track of converted arrays, if any, to avoid double
@@ -231,19 +249,58 @@ module ActionController
231
249
  self
232
250
  end
233
251
 
234
- # Ensures that a parameter is present. If it's present, returns
235
- # the parameter at the given +key+, otherwise raises an
236
- # <tt>ActionController::ParameterMissing</tt> error.
252
+ # This method accepts both a single key and an array of keys.
253
+ #
254
+ # When passed a single key, if it exists and its associated value is
255
+ # either present or the singleton +false+, returns said value:
237
256
  #
238
257
  # ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
239
258
  # # => {"name"=>"Francesco"}
240
259
  #
260
+ # Otherwise raises <tt>ActionController::ParameterMissing</tt>:
261
+ #
262
+ # ActionController::Parameters.new.require(:person)
263
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
264
+ #
241
265
  # ActionController::Parameters.new(person: nil).require(:person)
242
- # # => ActionController::ParameterMissing: param not found: person
266
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
267
+ #
268
+ # ActionController::Parameters.new(person: "\t").require(:person)
269
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
243
270
  #
244
271
  # ActionController::Parameters.new(person: {}).require(:person)
245
- # # => ActionController::ParameterMissing: param not found: person
272
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
273
+ #
274
+ # When given an array of keys, the method tries to require each one of them
275
+ # in order. If it succeeds, an array with the respective return values is
276
+ # returned:
277
+ #
278
+ # params = ActionController::Parameters.new(user: { ... }, profile: { ... })
279
+ # user_params, profile_params = params.require(:user, :profile)
280
+ #
281
+ # Otherwise, the method re-raises the first exception found:
282
+ #
283
+ # params = ActionController::Parameters.new(user: {}, profile: {})
284
+ # user_params, profile_params = params.require(:user, :profile)
285
+ # # ActionController::ParameterMissing: param is missing or the value is empty: user
286
+ #
287
+ # Technically this method can be used to fetch terminal values:
288
+ #
289
+ # # CAREFUL
290
+ # params = ActionController::Parameters.new(person: { name: 'Finn' })
291
+ # name = params.require(:person).require(:name) # CAREFUL
292
+ #
293
+ # but take into account that at some point those ones have to be permitted:
294
+ #
295
+ # def person_params
296
+ # params.require(:person).permit(:name).tap do |person_params|
297
+ # person_params.require(:name) # SAFER
298
+ # end
299
+ # end
300
+ #
301
+ # for example.
246
302
  def require(key)
303
+ return key.map { |k| require(k) } if key.is_a?(Array)
247
304
  value = self[key]
248
305
  if value.present? || value == false
249
306
  value
@@ -271,7 +328,7 @@ module ActionController
271
328
  #
272
329
  # params.permit(:name)
273
330
  #
274
- # +:name+ passes it is a key of +params+ whose associated value is of type
331
+ # +:name+ passes if it is a key of +params+ whose associated value is of type
275
332
  # +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
276
333
  # +Date+, +Time+, +DateTime+, +StringIO+, +IO+,
277
334
  # +ActionDispatch::Http::UploadedFile+ or +Rack::Test::UploadedFile+.
@@ -348,7 +405,13 @@ module ActionController
348
405
  # params[:person] # => {"name"=>"Francesco"}
349
406
  # params[:none] # => nil
350
407
  def [](key)
351
- convert_hashes_to_parameters(key, super)
408
+ convert_hashes_to_parameters(key, @parameters[key])
409
+ end
410
+
411
+ # Assigns a value to a given +key+. The given key may still get filtered out
412
+ # when +permit+ is called.
413
+ def []=(key, value)
414
+ @parameters[key] = value
352
415
  end
353
416
 
354
417
  # Returns a parameter for the given +key+. If the +key+
@@ -359,13 +422,34 @@ module ActionController
359
422
  #
360
423
  # params = ActionController::Parameters.new(person: { name: 'Francesco' })
361
424
  # params.fetch(:person) # => {"name"=>"Francesco"}
362
- # params.fetch(:none) # => ActionController::ParameterMissing: param not found: none
425
+ # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
363
426
  # params.fetch(:none, 'Francesco') # => "Francesco"
364
427
  # params.fetch(:none) { 'Francesco' } # => "Francesco"
365
428
  def fetch(key, *args)
366
- convert_hashes_to_parameters(key, super, false)
367
- rescue KeyError
368
- raise ActionController::ParameterMissing.new(key)
429
+ convert_value_to_parameters(
430
+ @parameters.fetch(key) {
431
+ if block_given?
432
+ yield
433
+ else
434
+ args.fetch(0) { raise ActionController::ParameterMissing.new(key) }
435
+ end
436
+ }
437
+ )
438
+ end
439
+
440
+ if Hash.method_defined?(:dig)
441
+ # Extracts the nested parameter from the given +keys+ by calling +dig+
442
+ # at each step. Returns +nil+ if any intermediate step is +nil+.
443
+ #
444
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
445
+ # params.dig(:foo, :bar, :baz) # => 1
446
+ # params.dig(:foo, :zot, :xyz) # => nil
447
+ #
448
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
449
+ # params2.dig(:foo, 1) # => 11
450
+ def dig(*keys)
451
+ convert_value_to_parameters(@parameters.dig(*keys))
452
+ end
369
453
  end
370
454
 
371
455
  # Returns a new <tt>ActionController::Parameters</tt> instance that
@@ -376,7 +460,24 @@ module ActionController
376
460
  # params.slice(:a, :b) # => {"a"=>1, "b"=>2}
377
461
  # params.slice(:d) # => {}
378
462
  def slice(*keys)
379
- new_instance_with_inherited_permitted_status(super)
463
+ new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
464
+ end
465
+
466
+ # Returns current <tt>ActionController::Parameters</tt> instance which
467
+ # contains only the given +keys+.
468
+ def slice!(*keys)
469
+ @parameters.slice!(*keys)
470
+ self
471
+ end
472
+
473
+ # Returns a new <tt>ActionController::Parameters</tt> instance that
474
+ # filters out the given +keys+.
475
+ #
476
+ # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
477
+ # params.except(:a, :b) # => {"c"=>3}
478
+ # params.except(:d) # => {"a"=>1,"b"=>2,"c"=>3}
479
+ def except(*keys)
480
+ new_instance_with_inherited_permitted_status(@parameters.except(*keys))
380
481
  end
381
482
 
382
483
  # Removes and returns the key/value pairs matching the given keys.
@@ -385,7 +486,7 @@ module ActionController
385
486
  # params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
386
487
  # params # => {"c"=>3}
387
488
  def extract!(*keys)
388
- new_instance_with_inherited_permitted_status(super)
489
+ new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
389
490
  end
390
491
 
391
492
  # Returns a new <tt>ActionController::Parameters</tt> with the results of
@@ -394,36 +495,80 @@ module ActionController
394
495
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
395
496
  # params.transform_values { |x| x * 2 }
396
497
  # # => {"a"=>2, "b"=>4, "c"=>6}
397
- def transform_values
398
- if block_given?
399
- new_instance_with_inherited_permitted_status(super)
498
+ def transform_values(&block)
499
+ if block
500
+ new_instance_with_inherited_permitted_status(
501
+ @parameters.transform_values(&block)
502
+ )
400
503
  else
401
- super
504
+ @parameters.transform_values
402
505
  end
403
506
  end
404
507
 
405
- # This method is here only to make sure that the returned object has the
406
- # correct +permitted+ status. It should not matter since the parent of
407
- # this object is +HashWithIndifferentAccess+
408
- def transform_keys # :nodoc:
409
- if block_given?
410
- new_instance_with_inherited_permitted_status(super)
508
+ # Performs values transformation and returns the altered
509
+ # <tt>ActionController::Parameters</tt> instance.
510
+ def transform_values!(&block)
511
+ @parameters.transform_values!(&block)
512
+ self
513
+ end
514
+
515
+ # Returns a new <tt>ActionController::Parameters</tt> instance with the
516
+ # results of running +block+ once for every key. The values are unchanged.
517
+ def transform_keys(&block)
518
+ if block
519
+ new_instance_with_inherited_permitted_status(
520
+ @parameters.transform_keys(&block)
521
+ )
411
522
  else
412
- super
523
+ @parameters.transform_keys
413
524
  end
414
525
  end
415
526
 
527
+ # Performs keys transformation and returns the altered
528
+ # <tt>ActionController::Parameters</tt> instance.
529
+ def transform_keys!(&block)
530
+ @parameters.transform_keys!(&block)
531
+ self
532
+ end
533
+
416
534
  # Deletes and returns a key-value pair from +Parameters+ whose key is equal
417
535
  # to key. If the key is not found, returns the default value. If the
418
536
  # optional code block is given and the key is not found, pass in the key
419
537
  # and return the result of block.
420
- def delete(key, &block)
421
- convert_hashes_to_parameters(key, super, false)
538
+ def delete(key)
539
+ convert_value_to_parameters(@parameters.delete(key))
540
+ end
541
+
542
+ # Returns a new instance of <tt>ActionController::Parameters</tt> with only
543
+ # items that the block evaluates to true.
544
+ def select(&block)
545
+ new_instance_with_inherited_permitted_status(@parameters.select(&block))
422
546
  end
423
547
 
424
548
  # Equivalent to Hash#keep_if, but returns nil if no changes were made.
425
549
  def select!(&block)
426
- convert_value_to_parameters(super)
550
+ @parameters.select!(&block)
551
+ self
552
+ end
553
+ alias_method :keep_if, :select!
554
+
555
+ # Returns a new instance of <tt>ActionController::Parameters</tt> with items
556
+ # that the block evaluates to true removed.
557
+ def reject(&block)
558
+ new_instance_with_inherited_permitted_status(@parameters.reject(&block))
559
+ end
560
+
561
+ # Removes items that the block evaluates to true and returns self.
562
+ def reject!(&block)
563
+ @parameters.reject!(&block)
564
+ self
565
+ end
566
+ alias_method :delete_if, :reject!
567
+
568
+ # Returns values that were assigned to the given +keys+. Note that all the
569
+ # +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
570
+ def values_at(*keys)
571
+ convert_value_to_parameters(@parameters.values_at(*keys))
427
572
  end
428
573
 
429
574
  # Returns an exact copy of the <tt>ActionController::Parameters</tt>
@@ -440,11 +585,54 @@ module ActionController
440
585
  end
441
586
  end
442
587
 
588
+ # Returns a new <tt>ActionController::Parameters</tt> with all keys from
589
+ # +other_hash+ merges into current hash.
590
+ def merge(other_hash)
591
+ new_instance_with_inherited_permitted_status(
592
+ @parameters.merge(other_hash)
593
+ )
594
+ end
595
+
596
+ # This is required by ActiveModel attribute assignment, so that user can
597
+ # pass +Parameters+ to a mass assignment methods in a model. It should not
598
+ # matter as we are using +HashWithIndifferentAccess+ internally.
599
+ def stringify_keys # :nodoc:
600
+ dup
601
+ end
602
+
603
+ def inspect
604
+ "<#{self.class} #{@parameters} permitted: #{@permitted}>"
605
+ end
606
+
607
+ def method_missing(method_sym, *args, &block)
608
+ if @parameters.respond_to?(method_sym)
609
+ message = <<-DEPRECATE.squish
610
+ Method #{method_sym} is deprecated and will be removed in Rails 5.1,
611
+ as `ActionController::Parameters` no longer inherits from
612
+ hash. Using this deprecated behavior exposes potential security
613
+ problems. If you continue to use this method you may be creating
614
+ a security vulnerability in your app that can be exploited. Instead,
615
+ consider using one of these documented methods which are not
616
+ deprecated: http://api.rubyonrails.org/v#{ActionPack.version}/classes/ActionController/Parameters.html
617
+ DEPRECATE
618
+ ActiveSupport::Deprecation.warn(message)
619
+ @parameters.public_send(method_sym, *args, &block)
620
+ else
621
+ super
622
+ end
623
+ end
624
+
443
625
  protected
626
+ attr_reader :parameters
627
+
444
628
  def permitted=(new_permitted)
445
629
  @permitted = new_permitted
446
630
  end
447
631
 
632
+ def fields_for_style?
633
+ @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
634
+ end
635
+
448
636
  private
449
637
  def new_instance_with_inherited_permitted_status(hash)
450
638
  self.class.new(hash).tap do |new_instance|
@@ -452,40 +640,56 @@ module ActionController
452
640
  end
453
641
  end
454
642
 
455
- def convert_hashes_to_parameters(key, value, assign_if_converted=true)
643
+ def convert_parameters_to_hashes(value, using)
644
+ case value
645
+ when Array
646
+ value.map { |v| convert_parameters_to_hashes(v, using) }
647
+ when Hash
648
+ value.transform_values do |v|
649
+ convert_parameters_to_hashes(v, using)
650
+ end.with_indifferent_access
651
+ when Parameters
652
+ value.send(using)
653
+ else
654
+ value
655
+ end
656
+ end
657
+
658
+ def convert_hashes_to_parameters(key, value)
456
659
  converted = convert_value_to_parameters(value)
457
- self[key] = converted if assign_if_converted && !converted.equal?(value)
660
+ @parameters[key] = converted unless converted.equal?(value)
458
661
  converted
459
662
  end
460
663
 
461
664
  def convert_value_to_parameters(value)
462
- if value.is_a?(Array) && !converted_arrays.member?(value)
665
+ case value
666
+ when Array
667
+ return value if converted_arrays.member?(value)
463
668
  converted = value.map { |_| convert_value_to_parameters(_) }
464
669
  converted_arrays << converted
465
670
  converted
466
- elsif value.is_a?(Parameters) || !value.is_a?(Hash)
467
- value
468
- else
671
+ when Hash
469
672
  self.class.new(value)
673
+ else
674
+ value
470
675
  end
471
676
  end
472
677
 
473
678
  def each_element(object)
474
- if object.is_a?(Array)
475
- object.map { |el| yield el }.compact
476
- elsif fields_for_style?(object)
477
- hash = object.class.new
478
- object.each { |k,v| hash[k] = yield v }
479
- hash
480
- else
481
- yield object
679
+ case object
680
+ when Array
681
+ object.grep(Parameters).map { |el| yield el }.compact
682
+ when Parameters
683
+ if object.fields_for_style?
684
+ hash = object.class.new
685
+ object.each { |k,v| hash[k] = yield v }
686
+ hash
687
+ else
688
+ yield object
689
+ end
482
690
  end
483
691
  end
484
692
 
485
- def fields_for_style?(object)
486
- object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
487
- end
488
-
489
693
  def unpermitted_parameters!(params)
490
694
  unpermitted_keys = unpermitted_keys(params)
491
695
  if unpermitted_keys.any?
@@ -547,15 +751,13 @@ module ActionController
547
751
  end
548
752
 
549
753
  def array_of_permitted_scalars?(value)
550
- if value.is_a?(Array)
551
- value.all? {|element| permitted_scalar?(element)}
754
+ if value.is_a?(Array) && value.all? {|element| permitted_scalar?(element)}
755
+ yield value
552
756
  end
553
757
  end
554
758
 
555
- def array_of_permitted_scalars_filter(params, key)
556
- if has_key?(key) && array_of_permitted_scalars?(self[key])
557
- params[key] = self[key]
558
- end
759
+ def non_scalar?(value)
760
+ value.is_a?(Array) || value.is_a?(Parameters)
559
761
  end
560
762
 
561
763
  EMPTY_ARRAY = []
@@ -565,17 +767,17 @@ module ActionController
565
767
  # Slicing filters out non-declared keys.
566
768
  slice(*filter.keys).each do |key, value|
567
769
  next unless value
770
+ next unless has_key? key
568
771
 
569
772
  if filter[key] == EMPTY_ARRAY
570
773
  # Declaration { comment_ids: [] }.
571
- array_of_permitted_scalars_filter(params, key)
572
- else
774
+ array_of_permitted_scalars?(self[key]) do |val|
775
+ params[key] = val
776
+ end
777
+ elsif non_scalar?(value)
573
778
  # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
574
779
  params[key] = each_element(value) do |element|
575
- if element.is_a?(Hash)
576
- element = self.class.new(element) unless element.respond_to?(:permit)
577
- element.permit(*Array.wrap(filter[key]))
578
- end
780
+ element.permit(*Array.wrap(filter[key]))
579
781
  end
580
782
  end
581
783
  end
@@ -623,7 +825,8 @@ module ActionController
623
825
  # end
624
826
  #
625
827
  # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
626
- # will need to specify which nested attributes should be whitelisted.
828
+ # will need to specify which nested attributes should be whitelisted. You might want
829
+ # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
627
830
  #
628
831
  # class Person
629
832
  # has_many :pets
@@ -643,7 +846,7 @@ module ActionController
643
846
  # # It's mandatory to specify the nested attributes that should be whitelisted.
644
847
  # # If you use `permit` with just the key that points to the nested attributes hash,
645
848
  # # it will return an empty hash.
646
- # params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
849
+ # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
647
850
  # end
648
851
  # end
649
852
  #
@@ -2,19 +2,8 @@ module ActionController
2
2
  module Testing
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include RackDelegation
6
-
7
- # TODO : Rewrite tests using controller.headers= to use Rack env
8
- def headers=(new_headers)
9
- @_response ||= ActionDispatch::Response.new
10
- @_response.headers.replace(new_headers)
11
- end
12
-
13
5
  # Behavior specific to functional tests
14
6
  module Functional # :nodoc:
15
- def set_response!(request)
16
- end
17
-
18
7
  def recycle!
19
8
  @_url_options = nil
20
9
  self.formats = nil
@@ -24,7 +13,7 @@ module ActionController
24
13
 
25
14
  module ClassMethods
26
15
  def before_filters
27
- _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
16
+ _process_action_callbacks.find_all{|x| x.kind == :before}.map(&:name)
28
17
  end
29
18
  end
30
19
  end
@@ -4,7 +4,10 @@ module ActionController
4
4
  #
5
5
  # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
6
6
  # url options like the +host+. In order to do so, this module requires the host class
7
- # to implement +env+ and +request+, which need to be a Rack-compatible.
7
+ # to implement +env+ which needs to be Rack-compatible and +request+
8
+ # which is either an instance of +ActionDispatch::Request+ or an object
9
+ # that responds to the +host+, +optional_port+, +protocol+ and
10
+ # +symbolized_path_parameter+ methods.
8
11
  #
9
12
  # class RootUrl
10
13
  # include ActionController::UrlFor
@@ -30,15 +33,19 @@ module ActionController
30
33
  :_recall => request.path_parameters
31
34
  }.merge!(super).freeze
32
35
 
33
- if (same_origin = _routes.equal?(env["action_dispatch.routes".freeze])) ||
34
- (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
35
- (original_script_name = env['ORIGINAL_SCRIPT_NAME'.freeze])
36
+ if (same_origin = _routes.equal?(request.routes)) ||
37
+ (script_name = request.engine_script_name(_routes)) ||
38
+ (original_script_name = request.original_script_name)
36
39
 
37
40
  options = @_url_options.dup
38
41
  if original_script_name
39
42
  options[:original_script_name] = original_script_name
40
43
  else
41
- options[:script_name] = same_origin ? request.script_name.dup : script_name
44
+ if same_origin
45
+ options[:script_name] = request.script_name.empty? ? "".freeze : request.script_name.dup
46
+ else
47
+ options[:script_name] = script_name
48
+ end
42
49
  end
43
50
  options.freeze
44
51
  else