Method: ActionController::Parameters#expect
- Defined in:
- lib/action_controller/metal/strong_parameters.rb
#expect(*filters) ⇒ Object
expect is the preferred way to require and permit parameters. It is safer than the previous recommendation to call permit and require in sequence, which could allow user triggered 500 errors.
expect is more strict with types to avoid a number of potential pitfalls that may be encountered with the .require.permit pattern.
For example:
params = ActionController::Parameters.new(comment: { text: "hello" })
params.expect(comment: [:text])
# => #<ActionController::Parameters { text: "hello" } permitted: true>
params = ActionController::Parameters.new(comment: [{ text: "hello" }, { text: "world" }])
params.expect(comment: [:text])
# => ActionController::ParameterMissing: param is missing or the value is empty or invalid: comment
In order to permit an array of parameters, the array must be defined explicitly. Use double array brackets, an array inside an array, to declare that an array of parameters is expected.
params = ActionController::Parameters.new(comments: [{ text: "hello" }, { text: "world" }])
params.expect(comments: [[:text]])
# => [#<ActionController::Parameters { "text" => "hello" } permitted: true>,
# #<ActionController::Parameters { "text" => "world" } permitted: true>]
params = ActionController::Parameters.new(comments: { text: "hello" })
params.expect(comments: [[:text]])
# => ActionController::ParameterMissing: param is missing or the value is empty or invalid: comments
expect is intended to protect against array tampering.
params = ActionController::Parameters.new(user: "hack")
# The previous way of requiring and permitting parameters will error
params.require(:user).permit(:name, pets: [:name]) # wrong
# => NoMethodError: undefined method `permit' for an instance of String
# similarly with nested parameters
params = ActionController::Parameters.new(user: { name: "Martin", pets: { name: "hack" } })
user_params = params.require(:user).permit(:name, pets: [:name]) # wrong
# user_params[:pets] is expected to be an array but is a hash
expect solves this by being more strict with types.
params = ActionController::Parameters.new(user: "hack")
params.expect(user: [ :name, pets: [[:name]] ])
# => ActionController::ParameterMissing: param is missing or the value is empty or invalid: user
# with nested parameters
params = ActionController::Parameters.new(user: { name: "Martin", pets: { name: "hack" } })
user_params = params.expect(user: [:name, pets: [[:name]] ])
user_params[:pets] # => nil
As the examples show, expect requires the :user key, and any root keys similar to the .require.permit pattern. If multiple root keys are expected, they will all be required.
params = ActionController::Parameters.new(name: "Martin", pies: [{ type: "dessert", flavor: "pumpkin"}])
name, pies = params.expect(:name, pies: [[:type, :flavor]])
name # => "Martin"
pies # => [#<ActionController::Parameters {"type"=>"dessert", "flavor"=>"pumpkin"} permitted: true>]
When called with a hash with multiple keys, expect will permit the parameters and require the keys in the order they are given in the hash, returning an array of the permitted parameters.
params = ActionController::Parameters.new(subject: { name: "Martin" }, object: { pie: "pumpkin" })
subject, object = params.expect(subject: [:name], object: [:pie])
subject # => #<ActionController::Parameters {"name"=>"Martin"} permitted: true>
object # => #<ActionController::Parameters {"pie"=>"pumpkin"} permitted: true>
Besides being more strict about array vs hash params, expect uses permit internally, so it will behave similarly.
params = ActionController::Parameters.new({
person: {
name: "Francesco",
age: 22,
pets: [{
name: "Purplish",
category: "dogs"
}]
}
})
permitted = params.expect(person: [ :name, { pets: [[:name]] } ])
permitted.permitted? # => true
permitted[:name] # => "Francesco"
permitted[:age] # => nil
permitted[:pets][0][:name] # => "Purplish"
permitted[:pets][0][:category] # => nil
An array of permitted scalars may be expected with the following:
params = ActionController::Parameters.new(tags: ["rails", "parameters"])
permitted = params.expect(tags: [])
permitted # => ["rails", "parameters"]
permitted.is_a?(Array) # => true
permitted.size # => 2
772 773 774 775 776 777 |
# File 'lib/action_controller/metal/strong_parameters.rb', line 772 def expect(*filters) params = permit_filters(filters) keys = filters.flatten.flat_map { |f| f.is_a?(Hash) ? f.keys : f } values = params.require(keys) values.size == 1 ? values.first : values end |