forked from rails/rails
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documentation and implementation for ActionController::Parameters#man…
…date
- Loading branch information
1 parent
a5ffec5
commit 96041b5
Showing
1 changed file
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -531,6 +531,140 @@ def require(key) | |
|
||
alias :required :require | ||
|
||
# `mandate` is the preferred way to require and permit parameters. | ||
# It is similar to calling `permit` and `require` in sequence, but it avoids some | ||
# potential pitfalls, such as NoMethodErrors that can be triggered by external | ||
# users passing unexpected scalar or non-scalar types. | ||
# | ||
# `mandate` can be thought of as a shortcut for `params.permit(filters).require(filters)` | ||
# with the additional special behavior of requiring `filetrs.keys` if `filters` is a hash. | ||
# This can be convenient for the standard format of params from rails forms. | ||
# | ||
# # given a usual format of params from a form | ||
# params = ActionController::Parameters.new(user: { name: "Martin", age: 40, role: "admin" }) | ||
# # instead of this (not recommended) | ||
# permitted = params.require(:user).permit(:name, :age) | ||
# # do this (recommended) | ||
# permitted = params.mandate(user: [:name, :age]) | ||
# | ||
# In the example, `mandate` will require the `:user` key and permit the | ||
# `:name` and `:age` keys, and return the params `{ name: "Martin", age: 40 }`. | ||
# It is functionally the same as the following: | ||
# | ||
# permitted = params.permit(user: [:name, :age]).require(:user) | ||
# | ||
# `mandate` aims to correct a potential error that can be triggered when an end user | ||
# alters the params. In the following example, the params are altered by the user in | ||
# order to cause a 500 error. | ||
# | ||
# params = ActionController::Parameters.new(user: "Hax0r") | ||
# params.require(:user).permit(:name, :age) # causes a 500 error | ||
# # => NoMethodError: undefined method `permit' for an instance of String | ||
# params.mandate(user: [:name, :age]) # expected behavior, handled correctly by rails | ||
# # => ActionController::ParameterMissing: param is missing or the value is empty: user | ||
# | ||
# Like `permit`, `mandate` only allows permitted scalars to pass the filter. | ||
# For example, | ||
# | ||
# params.mandate(:name) | ||
# | ||
# If `:name` is a `Hash` or `Array`, ActionController::ParameterMissing is raised. | ||
# | ||
# `:name` is permitted 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`. | ||
# | ||
# You can also `mandate` nested parameters just like permit. The result will | ||
# `require(filters.keys)` on the permitted parameters. | ||
# | ||
# params = ActionController::Parameters.new({ | ||
# person: { | ||
# name: "Francesco", | ||
# age: 22, | ||
# pets: [{ | ||
# name: "Purplish", | ||
# category: "dogs" | ||
# }] | ||
# } | ||
# }) | ||
# | ||
# permitted = params.mandate(person: [ :name, { pets: :name } ]) | ||
# permitted.permitted? # => true | ||
# permitted[:name] # => "Francesco" | ||
# permitted[:age] # => nil | ||
# permitted[:pets][0][:name] # => "Purplish" | ||
# permitted[:pets][0][:category] # => nil | ||
# | ||
# 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"]) | ||
# permitted = params.mandate(tags: []) | ||
# permitted.permitted? # => true | ||
# permitted.is_a?(Array) # => true | ||
# permitted.size # => 2 | ||
# | ||
# Like `permit`, `mandate` can be used to permit nested parameters. | ||
# Be careful because this opens the door to arbitrary input. In this case, | ||
# `permit` ensures values in the returned structure are permitted scalars and | ||
# filters out anything else. For example: | ||
# | ||
# params = ActionController::Parameters.new(user: { name: "Martin", age: 40, hax: [] }) | ||
# permitted = params.mandate(user: {}) | ||
# permitted.permitted? # => true | ||
# permitted.has_key?(:name) # => true | ||
# permitted.has_key?(:age) # => true | ||
# permitted.has_key?(:hax) # => false | ||
# | ||
# Note that if you use `mandate` on a key that points to a hash, it will not | ||
# allow the hash. You also need to specify which attributes inside the hash | ||
# should be permitted. | ||
# | ||
# params = ActionController::Parameters.new({ | ||
# person: { | ||
# contact: { | ||
# email: "[email protected]", | ||
# phone: "555-1234" | ||
# } | ||
# } | ||
# }) | ||
# | ||
# params.mandate(person: :contact) # wrong | ||
# # => ActionController::ParameterMissing: param is missing or the value is empty: person | ||
# | ||
# params.mandate(person: { contact: :phone }) # correct | ||
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true> | ||
# | ||
# params.mandate(person: { contact: [ :email, :phone ] }) # correct | ||
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"[email protected]", "phone"=>"555-1234"} permitted: true>} permitted: true> | ||
# | ||
# Use `mandate` to require multiple top level scalar parameters. | ||
# The keys must be passed as an array, similar to using `require`. | ||
# Note that this is different than `permit` which allows multiple keys | ||
# to be passed as arguments instead of as an array. | ||
# | ||
# params = ActionController::Parameters.new(name: "Martin", age: 40) | ||
# name, age = params.mandate(:name, :age) # wrong | ||
# # => ArgumentError: wrong number of arguments (given 2, expected 1) | ||
# name, age = params.mandate([:name, :age]) # correct | ||
# name # => "Martin" | ||
# age # => 40 | ||
# | ||
# When called with a hash with multiple keys, `mandate` will behave as | ||
# described above, requiring all the keys of the hash and returning the value | ||
# at each key in the order they are given in the filters argument. | ||
# | ||
# params = ActionController::Parameters.new(subject: { name: "Martin" }, object: { pie: "pumpkin" }) | ||
# subject, object = params.mandate(subject: [:name], object: [:pie]) | ||
# subject # => #<ActionController::Parameters {"name"=>"Martin"} permitted: true> | ||
# object # => #<ActionController::Parameters {"pie"=>"pumpkin"} permitted: true> | ||
# | ||
def mandate(filters) | ||
keys = filters.respond_to?(:keys) ? filters.keys : filters | ||
permit(filters).require(keys) | ||
end | ||
|
||
# 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 | ||
|