Class: ActionController::Parameters

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

Overview

Action Controller Parameters

Allows to choose which attributes should be whitelisted for mass updating and thus prevent accidentally exposing that which shouldn’t be exposed. Provides two methods for this purpose: #require and #permit. The former is used to mark parameters as required. The latter is used to set the parameter as permitted and limit which attributes should be allowed for mass updating.

params = ActionController::Parameters.new({
  person: {
    name: 'Francesco',
    age:  22,
    role: 'admin'
  }
})

permitted = params.require(:person).permit(:name, :age)
permitted            # => {"name"=>"Francesco", "age"=>22}
permitted.class      # => ActionController::Parameters
permitted.permitted? # => true

Person.first.update!(permitted)
# => #<Person id: 1, name: "Francesco", age: 22, role: "user">

It provides two options that controls the top-level behavior of new instances:

  • permit_all_parameters - If it's true, all the parameters will be permitted by default. The default is false.

  • action_on_unpermitted_parameters - Allow to control the behavior when parameters that are not explicitly permitted are found. The values can be :log to write a message on the logger or :raise to raise ActionController::UnpermittedParameters exception. The default value is :log in test and development environments, false otherwise.

Examples:

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

ActionController::Parameters.permit_all_parameters = true

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

params = ActionController::Parameters.new(a: "123", b: "456")
params.permit(:c)
# => {}

ActionController::Parameters.action_on_unpermitted_parameters = :raise

params = ActionController::Parameters.new(a: "123", b: "456")
params.permit(:c)
# => ActionController::UnpermittedParameters: found unpermitted keys: a, b

ActionController::Parameters is inherited from ActiveSupport::HashWithIndifferentAccess, this means that you can fetch values using either :key or "key".

params = ActionController::Parameters.new(key: 'value')
params[:key]  # => "value"
params["key"] # => "value"

Constant Summary collapse

NEVER_UNPERMITTED_PARAMS =

Never raise an UnpermittedParameters exception because of these params are present. They are added by Rails and it's of no concern.

%w( controller action )

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

123
124
125
126
# File 'lib/action_controller/metal/strong_parameters.rb', line 123

def initialize(attributes = nil)
  super(attributes)
  @permitted = self.class.permit_all_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

271
272
273
# File 'lib/action_controller/metal/strong_parameters.rb', line 271

def [](key)
  convert_hashes_to_parameters(key, super)
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

313
314
315
316
317
# File 'lib/action_controller/metal/strong_parameters.rb', line 313

def dup
  super.tap do |duplicate|
    duplicate.instance_variable_set :@permitted, @permitted
  end
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 not found: none
params.fetch(:none, 'Francesco')    # => "Francesco"
params.fetch(:none) { 'Francesco' } # => "Francesco"

286
287
288
289
290
# File 'lib/action_controller/metal/strong_parameters.rb', line 286

def fetch(key, *args)
  convert_hashes_to_parameters(key, super)
rescue KeyError
  raise ActionController::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 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"}}

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/action_controller/metal/strong_parameters.rb', line 248

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

150
151
152
153
154
155
156
157
158
# File 'lib/action_controller/metal/strong_parameters.rb', line 150

def permit!
  each_pair do |key, value|
    convert_hashes_to_parameters(key, value)
    self[key].permit! if self[key].respond_to? :permit!
  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)

134
135
136
# File 'lib/action_controller/metal/strong_parameters.rb', line 134

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 not found: person

ActionController::Parameters.new(person: {}).require(:person)
# => ActionController::ParameterMissing: param not found: person

172
173
174
# File 'lib/action_controller/metal/strong_parameters.rb', line 172

def require(key)
  self[key].presence || raise(ParameterMissing.new(key))
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)     # => {}

299
300
301
302
303
# File 'lib/action_controller/metal/strong_parameters.rb', line 299

def slice(*keys)
  self.class.new(super).tap do |new_instance|
    new_instance.instance_variable_set :@permitted, @permitted
  end
end