Skip to content

Commit 68eaf30

Browse files
committed
Merge branch 'master' into new-font-guidelines-for-the-api
Conflicts: actionpack/lib/action_controller/metal/strong_parameters.rb
2 parents 837bbf8 + 2c79122 commit 68eaf30

File tree

61 files changed

+431
-179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+431
-179
lines changed

.travis.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,10 @@ env:
2323
rvm:
2424
- 2.2.2
2525
- ruby-head
26-
- rbx-2
27-
- jruby-head
2826
matrix:
2927
allow_failures:
3028
- env: "GEM=ar:mysql"
3129
- rvm: ruby-head
32-
- rvm: rbx-2
33-
- rvm: jruby-head
3430
fast_finish: true
3531
notifications:
3632
email: false

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ PATH
8585
activesupport (= 5.0.0.alpha)
8686
arel (= 7.0.0.alpha)
8787
activesupport (5.0.0.alpha)
88+
concurrent-ruby (~> 0.9.0)
8889
i18n (~> 0.7)
8990
json (~> 1.7, >= 1.7.7)
9091
minitest (~> 5.1)
@@ -135,6 +136,7 @@ GEM
135136
execjs
136137
coffee-script-source (1.9.0)
137138
columnize (0.9.0)
139+
concurrent-ruby (0.9.0)
138140
connection_pool (2.1.1)
139141
dalli (2.7.2)
140142
dante (0.1.5)

actionpack/CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
* `ActionController::Parameters` no longer inherits from
2+
`HashWithIndifferentAccess`
3+
4+
Inheriting from `HashWithIndifferentAccess` allowed users to call any
5+
enumerable methods on `Parameters` object, resulting in a risk of losing the
6+
`permitted?` status or even getting back a pure `Hash` object instead of
7+
a `Parameters` object with proper sanitization.
8+
9+
By not inheriting from `HashWithIndifferentAccess`, we are able to make
10+
sure that all methods that are defined in `Parameters` object will return
11+
a proper `Parameters` object with a correct `permitted?` flag.
12+
13+
*Prem Sichanugrist*
14+
15+
* Replaced `ActiveSupport::Concurrency::Latch` with `Concurrent::CountDownLatch`
16+
from the concurrent-ruby gem.
17+
18+
*Jerry D'Antonio*
19+
120
* Add ability to filter parameters based on parent keys.
221

322
# matches {credit_card: {code: "xxxx"}}

actionpack/lib/action_controller/caching.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module ActionController
88
#
99
# You can read more about each approach by clicking the modules below.
1010
#
11-
# Note: To turn off all caching, set
11+
# Note: To turn off all caching provided by Action Controller, set
1212
# config.action_controller.perform_caching = false
1313
#
1414
# == \Caching stores

actionpack/lib/action_controller/metal/data_streaming.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ def initialize(path)
8585
@to_path = path
8686
end
8787

88+
def body
89+
File.binread(to_path)
90+
end
91+
8892
# Stream the file's contents if Rack::Sendfile isn't present.
8993
def each
9094
File.open(to_path, 'rb') do |file|

actionpack/lib/action_controller/metal/strong_parameters.rb

Lines changed: 132 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ def initialize(params) # :nodoc:
104104
# params = ActionController::Parameters.new(key: 'value')
105105
# params[:key] # => "value"
106106
# params["key"] # => "value"
107-
class Parameters < ActiveSupport::HashWithIndifferentAccess
107+
class Parameters
108108
cattr_accessor :permit_all_parameters, instance_accessor: false
109109
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
110110

111+
delegate :keys, :key?, :has_key?, :empty?, :inspect, to: :@parameters
112+
111113
# By default, never raise an UnpermittedParameters exception if these
112114
# params are present. The default includes both 'controller' and 'action'
113115
# because they are added by Rails and should be of no concern. One way
@@ -144,11 +146,22 @@ def self.const_missing(const_name)
144146
# params = ActionController::Parameters.new(name: 'Francesco')
145147
# params.permitted? # => true
146148
# Person.new(params) # => #<Person id: nil, name: "Francesco">
147-
def initialize(attributes = nil)
148-
super(attributes)
149+
def initialize(parameters = {})
150+
@parameters = parameters.with_indifferent_access
149151
@permitted = self.class.permit_all_parameters
150152
end
151153

154+
# Returns true if another +Parameters+ object contains the same content and
155+
# permitted flag, or other Hash-like object contains the same content. This
156+
# override is in place so you can perform a comparison with `Hash`.
157+
def ==(other_hash)
158+
if other_hash.respond_to?(:permitted?)
159+
super
160+
else
161+
@parameters == other_hash
162+
end
163+
end
164+
152165
# Returns a safe +Hash+ representation of this parameter with all
153166
# unpermitted keys removed.
154167
#
@@ -162,28 +175,25 @@ def initialize(attributes = nil)
162175
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
163176
def to_h
164177
if permitted?
165-
to_hash
178+
@parameters.to_h
166179
else
167180
slice(*self.class.always_permitted_parameters).permit!.to_h
168181
end
169182
end
170183

171184
# Returns an unsafe, unfiltered +Hash+ representation of this parameter.
172185
def to_unsafe_h
173-
to_hash
186+
@parameters.to_h
174187
end
175188
alias_method :to_unsafe_hash, :to_unsafe_h
176189

177190
# Convert all hashes in values into parameters, then yield each pair like
178191
# the same way as <tt>Hash#each_pair</tt>
179192
def each_pair(&block)
180-
super do |key, value|
181-
convert_hashes_to_parameters(key, value)
193+
@parameters.each_pair do |key, value|
194+
yield key, convert_hashes_to_parameters(key, value)
182195
end
183-
184-
super
185196
end
186-
187197
alias_method :each, :each_pair
188198

189199
# Attribute that keeps track of converted arrays, if any, to avoid double
@@ -347,7 +357,13 @@ def permit(*filters)
347357
# params[:person] # => {"name"=>"Francesco"}
348358
# params[:none] # => nil
349359
def [](key)
350-
convert_hashes_to_parameters(key, super)
360+
convert_hashes_to_parameters(key, @parameters[key])
361+
end
362+
363+
# Assigns a value to a given +key+. The given key may still get filtered out
364+
# when +permit+ is called.
365+
def []=(key, value)
366+
@parameters[key] = value
351367
end
352368

353369
# Returns a parameter for the given +key+. If the +key+
@@ -361,8 +377,12 @@ def [](key)
361377
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
362378
# params.fetch(:none, 'Francesco') # => "Francesco"
363379
# params.fetch(:none) { 'Francesco' } # => "Francesco"
364-
def fetch(key, *args)
365-
convert_hashes_to_parameters(key, super, false)
380+
def fetch(key, *args, &block)
381+
convert_hashes_to_parameters(
382+
key,
383+
@parameters.fetch(key, *args, &block),
384+
false
385+
)
366386
rescue KeyError
367387
raise ActionController::ParameterMissing.new(key)
368388
end
@@ -375,7 +395,24 @@ def fetch(key, *args)
375395
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
376396
# params.slice(:d) # => {}
377397
def slice(*keys)
378-
new_instance_with_inherited_permitted_status(super)
398+
new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
399+
end
400+
401+
# Returns current <tt>ActionController::Parameters</tt> instance which
402+
# contains only the given +keys+.
403+
def slice!(*keys)
404+
@parameters.slice!(*keys)
405+
self
406+
end
407+
408+
# Returns a new <tt>ActionController::Parameters</tt> instance that
409+
# filters out the given +keys+.
410+
#
411+
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
412+
# params.except(:a, :b) # => {"c"=>3}
413+
# params.except(:d) # => {"a"=>1,"b"=>2,"c"=>3}
414+
def except(*keys)
415+
new_instance_with_inherited_permitted_status(@parameters.except(*keys))
379416
end
380417

381418
# Removes and returns the key/value pairs matching the given keys.
@@ -384,7 +421,7 @@ def slice(*keys)
384421
# params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
385422
# params # => {"c"=>3}
386423
def extract!(*keys)
387-
new_instance_with_inherited_permitted_status(super)
424+
new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
388425
end
389426

390427
# Returns a new ActionController::Parameters with the results of
@@ -393,36 +430,80 @@ def extract!(*keys)
393430
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
394431
# params.transform_values { |x| x * 2 }
395432
# # => {"a"=>2, "b"=>4, "c"=>6}
396-
def transform_values
397-
if block_given?
398-
new_instance_with_inherited_permitted_status(super)
433+
def transform_values(&block)
434+
if block
435+
new_instance_with_inherited_permitted_status(
436+
@parameters.transform_values(&block)
437+
)
399438
else
400-
super
439+
@parameters.transform_values
401440
end
402441
end
403442

404-
# This method is here only to make sure that the returned object has the
405-
# correct permitted status. It should not matter since the parent of
406-
# this object is HashWithIndifferentAccess
407-
def transform_keys # :nodoc:
408-
if block_given?
409-
new_instance_with_inherited_permitted_status(super)
443+
# Performs values transformation and returns the altered
444+
# ActionController::Parameters instance.
445+
def transform_values!(&block)
446+
@parameters.transform_values!(&block)
447+
self
448+
end
449+
450+
# Returns a new ActionController::Parameters instance with the
451+
# results of running +block+ once for every key. The values are unchanged.
452+
def transform_keys(&block)
453+
if block
454+
new_instance_with_inherited_permitted_status(
455+
@parameters.transform_keys(&block)
456+
)
410457
else
411-
super
458+
@parameters.transform_keys
412459
end
413460
end
414461

415-
# Deletes and returns a key-value pair from Parameters whose key is equal
416-
# to key. If the key is not found, returns the default value. If the
417-
# optional code block is given and the key is not found, pass in the key
418-
# and return the result of block.
462+
# Performs keys transfomration and returns the altered
463+
# ActionController::Parameters instance.
464+
def transform_keys!(&block)
465+
@parameters.transform_keys!(&block)
466+
self
467+
end
468+
469+
# Deletes and returns a key-value pair whose key is equal to +key+. If the
470+
# key is not found, returns the default value. If the optional code block
471+
# is given and the key is not found, passes in the key and returns the
472+
# result of the block.
419473
def delete(key, &block)
420-
convert_hashes_to_parameters(key, super, false)
474+
convert_hashes_to_parameters(key, @parameters.delete(key), false)
475+
end
476+
477+
# Returns a new instance of <tt>ActionController::Parameters</tt> with only
478+
# items that the block evaluates to true.
479+
def select(&block)
480+
new_instance_with_inherited_permitted_status(@parameters.select(&block))
421481
end
422482

423483
# Equivalent to Hash#keep_if, but returns nil if no changes were made.
424484
def select!(&block)
425-
convert_value_to_parameters(super)
485+
@parameters.select!(&block)
486+
self
487+
end
488+
alias_method :keep_if, :select!
489+
490+
# Returns a new instance of <tt>ActionController::Parameters</tt> with items
491+
# that the block evaluates to true removed.
492+
def reject(&block)
493+
new_instance_with_inherited_permitted_status(@parameters.reject(&block))
494+
end
495+
496+
# Removes items that the block evaluates to true and returns self.
497+
def reject!(&block)
498+
@parameters.reject!(&block)
499+
self
500+
end
501+
alias_method :delete_if, :reject!
502+
503+
# Return values that were assigned to the given +keys+. Note that all the
504+
# +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
505+
def values_at(*keys)
506+
convert_value_to_parameters(@parameters.values_at(*keys))
426507
end
427508

428509
# Returns an exact copy of the ActionController::Parameters
@@ -439,6 +520,21 @@ def dup
439520
end
440521
end
441522

523+
# Returns a new <tt>ActionController::Parameters</tt> with all keys from
524+
# +other_hash+ merges into current hash.
525+
def merge(other_hash)
526+
new_instance_with_inherited_permitted_status(
527+
@parameters.merge(other_hash)
528+
)
529+
end
530+
531+
# This is required by ActiveModel attribute assignment, so that user can
532+
# pass +Parameters+ to a mass assignment methods in a model. It should not
533+
# matter as we are using +HashWithIndifferentAccess+ internally.
534+
def stringify_keys # :nodoc:
535+
dup
536+
end
537+
442538
protected
443539
def permitted=(new_permitted)
444540
@permitted = new_permitted
@@ -453,7 +549,7 @@ def new_instance_with_inherited_permitted_status(hash)
453549

454550
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
455551
converted = convert_value_to_parameters(value)
456-
self[key] = converted if assign_if_converted && !converted.equal?(value)
552+
@parameters[key] = converted if assign_if_converted && !converted.equal?(value)
457553
converted
458554
end
459555

@@ -482,7 +578,8 @@ def each_element(object)
482578
end
483579

484580
def fields_for_style?(object)
485-
object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
581+
(object.is_a?(Hash) || object.is_a?(Parameters)) &&
582+
object.to_unsafe_h.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
486583
end
487584

488585
def unpermitted_parameters!(params)
@@ -571,7 +668,7 @@ def hash_filter(params, filter)
571668
else
572669
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
573670
params[key] = each_element(value) do |element|
574-
if element.is_a?(Hash)
671+
if element.is_a?(Hash) || element.is_a?(Parameters)
575672
element = self.class.new(element) unless element.respond_to?(:permit)
576673
element.permit(*Array.wrap(filter[key]))
577674
end

0 commit comments

Comments
 (0)