I talked with a couple of folks about my last post, and based on those conversations and further experiments, I’m going to suggest a different approach, one that doesn’t require mutating the params values.

For one thing, you really should make a solid effort at avoiding it. The params is already exposed to lots of things, and while they shouldn’t be meddling with it, neither really, should we. Also, we don’t have to, since ActionController::Parameters has a pretty solid interface, such that it can be avoided in most circumstances.

Here’s a basic controller setup, lightly modified from scaffolding.

class WatsController < ApplicationController


  # the usual ...

  # POST /wats
  def create
    @wat = Wat.create(wat_params)
  end

  # PATCH/PUT /wats/{id}
  def update
    @wat = Wat.find(params[:id])
    @wat.update(wat_params)
  end

  private

  def default_baz
    # ...
  end

  def derive_qux
    #...
  end

  def wat_params
    params.require(:wat).
      with_defaults(
        baz: default_baz
      ).merge(
        qux: derive_qux
      ).permit(
        :foo,
        :bar,
        :baz,
        :qux
      )
  end
end

In a series about optionality, I have to take this moment to appreciate ActionController::Parameters#reverse_merge and it’s contextual alias, with_defaults here. I like how it all chains together, using #reverse_merge to set defaults, followed by #merge to set derived values, then vetting the results with #permit. This seems like an 80% solution for this sort of thing, if not a whole lot more.

More here:

https://api.rubyonrails.org/classes/ActionController/Parameters.html