Skip to content
This repository

Documentation unclear #110

Open
denisemauldin opened this Issue February 27, 2013 · 3 comments

2 participants

denisemauldin Adam Spiers
denisemauldin

It's not clear what the intention of #require is. Are all parameters that are 'required' to be in the parameters list for a model supposed to be in the #require? I read :

   params.require(:person).permit(:name, :age)

to mean that in the params hash, there MUST be a :person key and there may also be a name and age key. ie:

params = { :person => 'person', :name => 'John', :age => "23" }

Whereas it seems that the code means that there must be a person key and inside of that there may be a name and age key. ie:

 params => { :person => { :name => "John", :age => "23" } }

It would be useful to clarify the documentation as to what 'require' and 'permit' mean. I thought that because I have a validation for name being required, that I needed to have name as a required parameter, not as a permitted parameter. Alternately, I thought I could force name to be required by adding it in the require (and have another way of doing a validation). ie: This would work:

params => { :person => 'blah', :name => "John" }

and this would fail (because :name was also required):

params => { :person => "blah", :age => "23" }

It's also unclear from the documentation that #permit only works after a require and why it works that way.

Adam Spiers

I agree this is unclear; furthermore, once you realise how the require -> permit chain is expected to be used, it still leaves unanswered questions, e.g.

  • Are name and age both required nested parameters, or would { :person => { :name => "John" } } be accepted?
  • If the latter, how do I make them required nested parameters?
  • How would I make name required but age optional (but still permitted via the whitelist) ?
  • Is it enough to call these methods on params, or does the result have to be used in resulting calls to the model's #create / #update actions? It seems the answer is the latter, but this is not explicitly mentioned, and it's extra confusing given that in the case where multiple parameters are required, all but the penultimate return value are discarded.
Adam Spiers

Just found another apparently undocumented ambiguity:

  • .permit(nested: []) allows { nested: [ 'anything', 'goes', 'here' ] }, whereas
  • .permit(nested: [ :foo ]) allows { nested: { foo: 'bar' } }
Adam Spiers

And another weakness:

http://guides.rubyonrails.org/action_controller_overview.html#more-examples explains that in order to allow a nested hash with arbitrary keys, you need to jump through ugly hoops:

def product_params
  params.require(:product).permit(:data).tap do |whitelisted|
    whitelisted[:data] = params[:product][:data]
  end
end

(Actually that's not quite what it says, but I already submitted rails/rails#12151 to fix the existing typo.)

However this recommended code is hard to understand, and also emits a highly misleading error message: Unpermitted parameters: data. Furthermore, it's not clear why this is better than simply writing:

pa.require(:product)[:data].permit!

Even if the recommended code is deliberately more complicated than it needs to be in order to suggest a way of doing more complicated validations, it still seems unnecessary. For example, if you wanted to filter certain parts of params[:product][:data] (e.g. via regular expressions), you could do something like:

def product_params
  params.require(:product).fetch(:data, {}).inject({}) do |whitelisted, (k, v)|
    whitelisted[k] = v if k =~ allowed_key_regexp
    whitelisted
  end
end

since presumably there is no hard requirement to return an ActionController::Parameters object when a vanilla Hash will suffice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.