expect
expect(*filters)`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.permitted? # => true permitted.is_a?(Array) # => true permitted.size # => 2