Modify ruby hash in place( rails strong params)
permit
returns a new hash with those keys in it, so you're not modifying the real params
variable. You're also not saving a reference to the hash trip_params returns, so you get it fresh each call in save
.
Try this:
def save
tp = trip_params
tp[:name] = 'Modifying name in place'
# ... use tp later, it'll be in there
end
Or, if you really want it to be used the way you previously did, modify trip_params
like so:
def trip_params
@trip_params ||= params.require(:trip).permit(:name, :date)
end
Now that hash is lazily cached and the same one is returned on subsequent trip_params
calls.
How to permit hash with rails strong params
You haven't mentioned the version of rails you are using but,:elements => []
does not work because elements is a ruby hash and not an array
on rails 5.1+ you can use
params.require(:category).permit(:name, :body, :elements => {})
Rails 6 change params to hash of hashes
The params get his structure from the input names.
So you could add an hidden field for question, and then specify a name for both of your fields.
<%= simple_form_for :test_results, url: test_results_path do |f| %>
<% @randomize_questions.map do |q| %>
<%= q[:question] %>
<%= f.input "question_#{q[:id]}", as: :hidden, input_html: { name: "test_results[#{q[:id]}][question]", value: q[:question] } %>
<%= f.input "question_#{q[:id]}", collection: q[:answers], as: :radio_buttons, input_html: { name: "test_results[#{q[:id]}][answer]" } %>
<% end %>
<%= f.button :submit %>
<% end %>
Params should looks like this:
params => {
test_result: {
1 => {
question: "...",
answer: "..."
},
2 => {
question: "...",
answer: "..."
}
}
}
Not tested. Could you tell if that's works for you?
update params hash in rails
Something like this would do the job:
if (attributes = params.dig(:user, :friend_attributes))
%i[players leaders].each do |key|
next unless attributes.has_key?(key)
attributes[key] = attributes[key].split(',').map(&:to_i)
end
end
The problem currently lies in (friend_params.dig(:friend_attributes) || []).each
which loops though the key-value pairs, or if not present through an empty array. You then call the #[]
method on the value (which is a string). eg. "50589,50590"['players']
tries the find the substring 'players'
inside "50589,50590"
the result of this should be nil
. Followed by an "NoMethodError: undefined method `split' for nil:NilClass" exception.
You should simply use the returned collection, rather than looping through the key-value pairs.
The better question is, why do you need to transform your parameters at all?
Can't the view be arranged in such a way to provide the parameters in array format?
<input name="user[friend_attributes][players][]" type="checkbox" value="1" />
<input name="user[friend_attributes][players][]" type="checkbox" value="2" />
<input name="user[friend_attributes][players][]" type="checkbox" value="3" />
Would send an array of numbers to the controller to start with, eliminating the need to convert parameters into the correct format.
Note: If you supply the parameters in the above format you should update your permited params to:
params.require(:user).permit(:first_name, :last_name, friend_attributes: [:phone_number, :title, :role, players: [], leaders: []])
# players and leaders are now expecting an array instead of a scalar value
Strong parameters with nested hash
It's done with syntax like:
answers_attributes: [:id, :content]
The problem is the keys you are using in the answers_attributes
. They are expected to be the ids of the answers_attributes
or in the case of new records 0
.
Changing these gives your expected outcome:
json = {
id: 1,
answers_attributes: {
"1": { id: "", content: "Hi" },
"2": { id: "", content: "Ho" }
}
}
params = ActionController::Parameters.new(json)
params.permit(:id, answers_attributes:[:id, :content])
=> {"id"=>1, "answers_attributes"=>{"1"=>{"id"=>"", "content"=>"Hi"}, "2"=>{"id"=>"", "content"=>"Ho"}}}
Edit: It appears that 0 is not the only key, I mean what if you have two new
records. I use nested_form and it appears to use a very long random number.
Editing params nested hash
still keep the output as a params hash (still containing nested hashes arrays
Sure.
You'll have to manipulate the params
hash, which is done in the controller.
Whilst I don't have lots of experience with this I just spent a bunch of time testing -- you can use a blend of the ActionController::Parameters
class and then using gsub!
-- like this:
#app/controllers/your_controller.rb
class YourController < ApplicationController
before_action :set_params, only: :create
def create
# Params are passed from the browser request
@model = Model.new params_hash
end
private
def params_hash
params.require(:x).permit(:y).each do |k,v|
v.gsub!(/[regex]/, 'string')
end
end
end
I tested this on one of our test apps, and it worked perfectly:
--
There are several important points.
Firstly, when you call a strong_params
hash, params.permit
creates a new hash out of the passed params. This means you can't just modify the passed params with params[:description] =
etc. You have to do it to the permitted params.
Secondly, I could only get the .each
block working with a bang-operator (gsub!
), as this changes the value directly. I'd have to spend more time to work out how to do more elaborate changes.
--
Update
If you wanted to include nested hashes, you'd have to call another loop:
def params_hash
params.require(:x).permit(:y).each do |k,v|
if /_attributes/ ~= k
k.each do |deep_k, deep_v|
deep_v.gsub!(/[regex]/, 'string'
end
else
v.gsub!(/[regex]/, 'string')
end
end
end
Strong params on a hash or array
It's possible to use strong parameters outside of controllers per the documentation at https://github.com/rails/strong_parameters
Example:
def update_user_ex(*attrs)
params = ActionController::Parameters.new(attrs)
user.assign_attributes(params.permit(:name, :email, :something_else))
Related Topics
How to Specify a Required Switch (Not Argument) with Ruby Optionparser
Are the Date, Time, and Datetime Classes Necessary
Get Class Location from Class Object
How to Write Columns Header to a CSV File with Ruby
How to Backreference in Ruby Regular Expression (Regex) with Gsub When I Use Grouping
Replacing the 'Auto_Link' Method in Ruby on Rails 3.1
Dynamic Rails Routes Based on Database Models
Creating a Capistrano Task That Performs Different Tasks Based on Role
Rails Strip Non Numeric Values Before Save
Rbenv: Surviving Without Gemsets
Aws Elastic Beanstalk - How to Upgrade Existing Environment from Ruby 2.1 to Ruby 2.2
Ruby Methods and Optional Parameters
Put Haml Tags Inside Link_To Helper
How to Access (Devise) Current_User in a Rspec Feature Test