Class: StrongParameters::Parameters

Inherits:
ActiveSupport::HashWithIndifferentAccess
  • Object
show all
Defined in:
lib/strong_parameters.rb

Overview

Strong Parameters

It provides an interface for protecting attributes from end-user assignment. This makes Action Controller parameters forbidden to be used in Active Model mass assignment until they have been whitelisted.

In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.

class PeopleController < ActionController::Base
  # Using "Person.create(params[:person])" would raise an
  # ActiveModel::ForbiddenAttributes exception because it'd
  # be using mass assignment without an explicit permit step.
  # This is the recommended form:
  def create
    Person.create(person_params)
  end

  # This will pass with flying colors as long as there's a person key in the
  # parameters, otherwise it'll raise an ActionController::MissingParameter
  # exception, which will get caught by ActionController::Base and turned
  # into a 400 Bad Request reply.
  def update
    redirect_to .people.find(params[:id]).tap { |person|
      person.update!(person_params)
    }
  end

  private
    # Using a private method to encapsulate the permissible parameters is
    # just a good pattern since you'll be able to reuse the same permit
    # list between create and update. Also, you can specialize this method
    # with per-user checking of permissible attributes.
    def person_params
      params.require(:person).permit(:name, :age)
    end
end

In order to use accepts_nested_attributes_for with Strong Parameters, you will need to specify which nested attributes should be whitelisted.

class Person
  has_many :pets
  accepts_nested_attributes_for :pets
end

class PeopleController < ActionController::Base
  def create
    Person.create(person_params)
  end

  ...

  private

    def person_params
      # It's mandatory to specify the nested attributes that should be whitelisted.
      # If you use `permit` with just the key that points to the nested attributes hash,
      # it will return an empty hash.
      params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
    end
end

See ActionController::Parameters.require and ActionController::Parameters.permit for more information.V

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = nil) ⇒ Parameters

Returns a new instance of ActionController::Parameters. Also, sets the permitted attribute to the default value of ActionController::Parameters.permit_all_parameters.

class Person < ActiveRecord::Base
end

params = ActionController::Parameters.new(name: 'Francesco')
params.permitted?  # => false
Person.new(params) # => ActiveModel::ForbiddenAttributesError

ActionController::Parameters.permit_all_parameters = true

params = ActionController::Parameters.new(name: 'Francesco')
params.permitted?  # => true
Person.new(params) # => #<Person id: nil, name: "Francesco">


122
123
124
125
# File 'lib/strong_parameters.rb', line 122

def initialize(attributes = nil)
  super(attributes)
  @permitted = self.class.permit_all_parameters
end

Class Method Details

.const_missing(const_name) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'lib/strong_parameters.rb', line 96

def self.const_missing(const_name)
  return super unless const_name == :NEVER_UNPERMITTED_PARAMS
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
        `ActionController::Parameters::NEVER_UNPERMITTED_PARAMS` has been deprecated.
        Use `ActionController::Parameters.always_permitted_parameters` instead.
  MSG

  always_permitted_parameters
end

Instance Method Details

#[](key) ⇒ Object

Returns a parameter for the given key. If not found, returns nil.

params = ActionController::Parameters.new(person: { name: 'Francesco' })
params[:person] # => {"name"=>"Francesco"}
params[:none]   # => nil


324
325
326
# File 'lib/strong_parameters.rb', line 324

def [](key)
  convert_hashes_to_parameters(key, super)
end

#converted_arraysObject

Attribute that keeps track of converted arrays, if any, to avoid double looping in the common use case permit + mass-assignment. Defined in a method to instantiate it only if needed.

Testing membership still loops, but it’s going to be faster than our own loop that converts values. Also, we are not going to build a new array object per fetch.



171
172
173
# File 'lib/strong_parameters.rb', line 171

def converted_arrays
  @converted_arrays ||= Set.new
end

#delete(key, &block) ⇒ Object

Deletes and returns a key-value pair from Parameters whose key is equal to key. If the key is not found, returns the default value. If the optional code block is given and the key is not found, pass in the key and return the result of block.



394
395
396
# File 'lib/strong_parameters.rb', line 394

def delete(key, &block)
  convert_hashes_to_parameters(key, super, false)
end

#dupObject

Returns an exact copy of the ActionController::Parameters instance. permitted state is kept on the duped object.

params = ActionController::Parameters.new(a: 1)
params.permit!
params.permitted?        # => true
copy_params = params.dup # => {"a"=>1}
copy_params.permitted?   # => true


411
412
413
414
415
# File 'lib/strong_parameters.rb', line 411

def dup
  super.tap do |duplicate|
    duplicate.permitted = @permitted
  end
end

#each_pair(&block) ⇒ Object Also known as: each

Convert all hashes in values into parameters, then yield each pair like the same way as Hash#each_pair



154
155
156
157
158
159
160
# File 'lib/strong_parameters.rb', line 154

def each_pair(&block)
  super do |key, value|
    convert_hashes_to_parameters(key, value)
  end

  super
end

#extract!(*keys) ⇒ Object

Removes and returns the key/value pairs matching the given keys.

params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
params                  # => {"c"=>3}


361
362
363
# File 'lib/strong_parameters.rb', line 361

def extract!(*keys)
  new_instance_with_inherited_permitted_status(super)
end

#fetch(key, *args) ⇒ Object

Returns a parameter for the given key. If the key can’t be found, there are several options: With no other arguments, it will raise an ActionController::ParameterMissing error; if more arguments are given, then that will be returned; if a block is given, then that will be run and its result returned.

params = ActionController::Parameters.new(person: { name: 'Francesco' })
params.fetch(:person)               # => {"name"=>"Francesco"}
params.fetch(:none)                 # => ActionController::ParameterMissing: param is missing or the value is empty: none
params.fetch(:none, 'Francesco')    # => "Francesco"
params.fetch(:none) { 'Francesco' } # => "Francesco"


339
340
341
342
343
# File 'lib/strong_parameters.rb', line 339

def fetch(key, *args)
  convert_hashes_to_parameters(key, super, false)
rescue KeyError
  raise StrongParameters::Error::ParameterMissing.new(key)
end

#permit(*filters) ⇒ Object

Returns a new ActionController::Parameters instance that includes only the given filters and sets the permitted attribute for the object to true. This is useful for limiting which attributes should be allowed for mass updating.

params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' })
permitted = params.require(:user).permit(:name, :age)
permitted.permitted?      # => true
permitted.has_key?(:name) # => true
permitted.has_key?(:age)  # => true
permitted.has_key?(:role) # => false

Only permitted scalars pass the filter. For example, given

params.permit(:name)

:name passes if it is a key of params whose associated value is of type String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile or Rack::Test::UploadedFile. Otherwise, the key :name is filtered out.

You may declare that the parameter should be an array of permitted scalars by mapping it to an empty array:

params = ActionController::Parameters.new(tags: ['rails', 'parameters'])
params.permit(tags: [])

You can also use permit on nested parameters, like:

params = ActionController::Parameters.new({
  person: {
    name: 'Francesco',
    age:  22,
    pets: [{
      name: 'Purplish',
      category: 'dogs'
    }]
  }
})

permitted = params.permit(person: [ :name, { pets: :name } ])
permitted.permitted?                    # => true
permitted[:person][:name]               # => "Francesco"
permitted[:person][:age]                # => nil
permitted[:person][:pets][0][:name]     # => "Purplish"
permitted[:person][:pets][0][:category] # => nil

Note that if you use permit in a key that points to a hash, it won’t allow all the hash. You also need to specify which attributes inside the hash should be whitelisted.

params = ActionController::Parameters.new({
  person: {
    contact: {
      email: '[email protected]',
      phone: '555-1234'
    }
  }
})

params.require(:person).permit(:contact)
# => {}

params.require(:person).permit(contact: :phone)
# => {"contact"=>{"phone"=>"555-1234"}}

params.require(:person).permit(contact: [ :email, :phone ])
# => {"contact"=>{"email"=>"[email protected]", "phone"=>"555-1234"}}


301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/strong_parameters.rb', line 301

def permit(*filters)
  params = self.class.new

  filters.flatten.each do |filter|
    case filter
      when Symbol, String
        permitted_scalar_filter(params, filter)
      when Hash then
        hash_filter(params, filter)
    end
  end

  unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters

  params.permit!
end

#permit!Object

Sets the permitted attribute to true. This can be used to pass mass assignment. Returns self.

class Person < ActiveRecord::Base
end

params = ActionController::Parameters.new(name: 'Francesco')
params.permitted?  # => false
Person.new(params) # => ActiveModel::ForbiddenAttributesError
params.permit!
params.permitted?  # => true
Person.new(params) # => #<Person id: nil, name: "Francesco">


197
198
199
200
201
202
203
204
205
206
# File 'lib/strong_parameters.rb', line 197

def permit!
  each_pair do |_key, value|
    Array.wrap(value).each do |v|
      v.permit! if v.respond_to? :permit!
    end
  end

  @permitted = true
  self
end

#permitted?Boolean

Returns true if the parameter is permitted, false otherwise.

params = ActionController::Parameters.new
params.permitted? # => false
params.permit!
params.permitted? # => true

Returns:

  • (Boolean)


181
182
183
# File 'lib/strong_parameters.rb', line 181

def permitted?
  @permitted
end

#require(key) ⇒ Object Also known as: required

Ensures that a parameter is present. If it’s present, returns the parameter at the given key, otherwise raises an ActionController::ParameterMissing error.

ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
# => {"name"=>"Francesco"}

ActionController::Parameters.new(person: nil).require(:person)
# => ActionController::ParameterMissing: param is missing or the value is empty: person

ActionController::Parameters.new(person: {}).require(:person)
# => ActionController::ParameterMissing: param is missing or the value is empty: person


220
221
222
223
224
225
226
227
# File 'lib/strong_parameters.rb', line 220

def require(key)
  value = self[key]
  if value.present? || value == false
    value
  else
    fail StrongParameters::Error::ParameterMissing.new(key)
  end
end

#select!(&block) ⇒ Object

Equivalent to Hash#keep_if, but returns nil if no changes were made.



399
400
401
# File 'lib/strong_parameters.rb', line 399

def select!(&block)
  convert_value_to_parameters(super)
end

#slice(*keys) ⇒ Object

Returns a new ActionController::Parameters instance that includes only the given keys. If the given keys don’t exist, returns an empty hash.

params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
params.slice(:a, :b) # => {"a"=>1, "b"=>2}
params.slice(:d)     # => {}


352
353
354
# File 'lib/strong_parameters.rb', line 352

def slice(*keys)
  new_instance_with_inherited_permitted_status(super)
end

#to_hObject

Returns a safe Hash representation of this parameter with all unpermitted keys removed.

params = ActionController::Parameters.new({
  name: 'Senjougahara Hitagi',
  oddity: 'Heavy stone crab'
})
params.to_h # => {}

safe_params = params.permit(:name)
safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}


138
139
140
141
142
143
144
# File 'lib/strong_parameters.rb', line 138

def to_h
  if permitted?
    to_hash
  else
    slice(*self.class.always_permitted_parameters).permit!.to_h
  end
end

#to_unsafe_hObject Also known as: to_unsafe_hash

Returns an unsafe, unfiltered Hash representation of this parameter.



147
148
149
# File 'lib/strong_parameters.rb', line 147

def to_unsafe_h
  to_hash
end

#transform_keysObject

This method is here only to make sure that the returned object has the correct permitted status. It should not matter since the parent of this object is HashWithIndifferentAccess



382
383
384
385
386
387
388
# File 'lib/strong_parameters.rb', line 382

def transform_keys # :nodoc:
  if block_given?
    new_instance_with_inherited_permitted_status(super)
  else
    super
  end
end

#transform_valuesObject

Returns a new ActionController::Parameters with the results of running block once for every value. The keys are unchanged.

params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
params.transform_values { |x| x * 2 }
# => {"a"=>2, "b"=>4, "c"=>6}


371
372
373
374
375
376
377
# File 'lib/strong_parameters.rb', line 371

def transform_values
  if block_given?
    new_instance_with_inherited_permitted_status(super)
  else
    super
  end
end