8

In Rails 4 here is the question about HOW to do this. What I would like to know is, although this works, WHY does the log still complain?

In Rails 5.1.3 I have a JSON column (letterhead) as one of my model attributes (and inside that json is a hash with various attributes that I don't care about whitelisting). I just want to permit/whitelist the column itself.

Note on Rails 5.1.4

There is a Rails way to do this in 5.1.4, see this commit. There is a rather long discussion here on github about this. In Rails 5.1.4 it is simply this:

def account_params
  params.require(:account).permit(:id, :name, :plan_id, letterhead: {})
end

The :letterhead parameter is permitted, no error shows in the log and the model saves. But obviously it allows arbitrary input inside that parameter, so use with care.

If you did want to restrict which hash keys were allowed inside such a parameter, then you can also whitelist those, like this for example:

def account_params
  params.require(:account).permit(:id, :name, :plan_id, letterhead: [:address, :logo, :contact_info])
end

This now prevents any other arbitrary keys inside :letterhead since I have explicitly only permitted these 3 - :address, :logo, :contact_info

Rails 5.1.3 (and earlier)

I can permit this column using either of the following (see the linked discussion for other possible options also):

Option 1

def account_params
  params.require(:account).permit(:id, :name, :plan_id, :letterhead).tap do |whitelisted|
    whitelisted[:letterhead] = params[:account].fetch(:letterhead, ActionController::Parameters.new).permit!
  end
end

Option 2

def account_params
  params.require(:account).permit(:id, :name, :plan_id, :letterhead).tap do |whitelisted|
    whitelisted[:letterhead] = params[:account][:letterhead].permit!
  end
end

In both cases the model saves but in the log it still says "unpermitted parameters :letterhead"

  1. Why is still saying that when I have explicitly permitted it?

  2. Also, is there any real difference between Option 1 and Option 2?

EDIT

Data is like this:

{"id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", 
"plan_id"=>"1dc49acf-3111-4030-aea1-7db259b53a51", 
"name"=>"Test Account 1", 
"is_active"=>true, 
"letterhead"=>{"left"=>"", "center"=>"", "right"=>""}, 
"created_by"=>nil, 
"updated_by"=>nil, 
"created_at"=>"2017-10-14T19:05:40.197Z", 
"updated_at"=>"2017-10-20T15:14:08.194Z"}
rmcsharry
  • 5,363
  • 6
  • 65
  • 108

1 Answers1

1

Why is still saying that when I have explicitly permitted it?

The log comes from #unpermitted_parameters! which is called by #permit. All this happens before the call to #tap.

Is there any real difference between Option 1 and Option 2?

The difference boils down to

params[:account].fetch(:letterhead, ActionController::Parameters.new).permit!

vs

params[:account][:letterhead].permit!

The latter will result in NoMethodError if :letterhead isn't passed because params[:account][:letterhead] will return nil. The former returns an empty hash of parameters instead.

Greg Navis
  • 2,818
  • 10
  • 10
  • Thank you for the comprehensive answer to both questions. However the last paragraph does not make sense. You say the latter will result in NoMethodError...will return nil. It cannot return both? Also is the final sentence finished - it ends with a square bracket? – rmcsharry Nov 29 '17 at 15:37
  • 1
    @rmcsharry, the last paragraph was a type due to my Kinesis ;-) What I had in mind is the difference between `#[]` and `#fetch`. If `:letterhead` isn't passed then `params[:account][:letterhead]` is `nil` and `nil` doesn't define `#permit!`. – Greg Navis Nov 29 '17 at 16:32
  • I see now, thanks for clarifying. You deserve the bonus :) – rmcsharry Nov 29 '17 at 16:52
  • Thank you, @rmcsharry. I appreciate it. – Greg Navis Nov 29 '17 at 17:29