Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a clean way to avoid calling a method on nil in a nested params hash? [duplicate]

I'm interested in getting the nested 'name' parameter of a params hash. Calling something like

params[:subject][:name]

throws an error when params[:subject] is empty. To avoid this error I usually write something like this:

if params[:subject] && params[:subject][:name]

Is there a cleaner way to implement this?

like image 648
Jack Kinsella Avatar asked Mar 25 '11 07:03

Jack Kinsella


4 Answers

Check Ick's maybe. You don't need to significantly refactor your code, just intersperse maybe proxies when necessary:

params[:subject].maybe[:name]

The same author (raganwald) also wrote andand, with the same idea.

like image 178
tokland Avatar answered Sep 23 '22 15:09

tokland


  1. You can use #try, but I don't think it's much better:

    params[:subject].try(:[], :name)
    
  2. Or use #fetch with default parameter:

    params.fetch(:subject, {}).fetch(:name, nil)
    
  3. Or you can set #default= to new empty hash, but then don't try to modify values returned from this:

    params.default = {}
    params[:subject][:name]
    

    It also breaks all simple tests for existence, so you can't write:

    if params[:subject]
    

    because it will return empty hash, now you have to add #present? call to every test.

    Also this always returns hash when there is no value for key, even when you expect string.

But from what I see, you try to extract nested parameter, instead of assigning it to model and there placing your logic. If you have Subject model, then simply assigning:

@subject = Subject.new(params[:subject])

shuld extract all your parameters user filled in form. Then you try to save them, to see if user passed valid values.

If you're worrying about accessing fields which user should not set, then add attr_accessible whitelist for fields whoich should be allowed to set with mass assignment (as in my example, of with @subject.attributes = params[:subject] for update)

like image 35
MBO Avatar answered Sep 21 '22 15:09

MBO


Ruby 2.3.0 makes this very easy to do with #dig

h = {foo: {bar: {baz: 1}}}

h.dig(:foo, :bar, :baz)           #=> 1
h.dig(:foo, :zot, :baz)           #=> nil
like image 9
steel Avatar answered Sep 23 '22 15:09

steel


params[:subject].try(:[], :name) is the cleanest way

like image 5
lebreeze Avatar answered Sep 21 '22 15:09

lebreeze