52

This may be more a ruby question then rails question but I'm pretty sure I was able to do this in a vanilla ruby application.

I have strong params defined.

def trip_params
  params.require(:trip).permit(:name, :date)
end

Now I get those params in a controller method. I want to do this.

def save
  trip_params[:name] = 'Modifying name in place'
  #trip_params[:name] still equals original value passed
end

This never works. Name never changes. BTW: The type of trip_params is ActionController::Parameters

If I do a standard ruby script, it works.

test = {}    
test[:name] = "blah"    
test[:name] = "ok"    
puts test #{:name=>"ok"}
2
  • Is Trip an active record? You may need to explicitly save it in the database: @trip.save Commented Aug 22, 2013 at 0:14
  • I took the Trip.new part. Don't let that distract. I'm trying to modify the ActionController::Parameters hash in place. Commented Aug 22, 2013 at 0:42

5 Answers 5

73

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Default values and formatting should be done on the model, though.
30

If you really want to change params in controller you can do it on this way:

def save
  params[:trip][:name] = 'Modifying name in place'
  # Now trip_params[:name] is 'Modifying name in place'
end

1 Comment

I think this is the best and simplest answer here. Just modify the actual params first, then call your strong parameters method. You can even do it with before_action :modify_params, only [:create, :update] then create a private method to keep it out of your create and update methods.
6

You could also do

def save
  data = trip_params.merge(name: 'new name')
  # use data later
end

If a new hash is merged into an old hash and if there are duplicate keys, the new hash's keys overwrite the old hash's matching keys.

Comments

3

This is because there's no method such as trip_params[]=(arg, val).

I mean, when you call trip_params you are returning the value of params.require(:trip).permit(:name, :date), so every time you call trip_params you are getting the params again.

So, if I were you, I'd define the trip_params method as follow:

def trip_params
  @trip_params ||= params.require(:trip).permit(:name, :date)
end

And would also define a method to change trip_params

def trip_params[]= (key,val)
  trip_params # Ensure that trip_params is called once or you will get an error
  @trip_params[key] = val
end

So now when you call trip_params you would actually return @trip_params, and if @trip_params is not set yet it would set to params.require(:trip).permit(:name, :date)

And then when you call trip_params[:name] = "Some name" it will ensure first that @trip_params is initialized by calling trip_params and then it will set the :name param to"Some name"`

Hope I've helped you

Comments

0

I had a similar need and this Q came up. Mine is not the exact same scenario, but has some applicable concepts.

I've got and endpoint that receives an array of trips. We used to have a field called date, but had to rename it in our schema, because reasons. We did not want to change our API, which many customers are using.

I wanted to change the field name as exhaustively as possible in our codebase. The only remaining places the old date field is used is in the controller and the serializers.

For the controller, I do the translation exactly one time in the strong params method. This proved quite clean and DRY.

def trip_params
  params.require(:trips).map do |trip|
    # They send "date", internally we use "departure_date" in the model. 
    trip[:departure_date] = trip.delete(:date)
    trip.permit(:name, :departure_date)
  end
end

This would work equally well with value transformations, I believe. Very explicit, no mystery, and various endpoints/controller methods can ignore the external field name and use the model name exclusively.

You could parlay this to seamlessly support both fields while in transition, deprecating the old field.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.