@@ -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