Optionality in Ruby: compacting collections follow-up
Write in haste, repent at leisure.
There’s something, I find aesthetically unsatisfying about the example I gave in the last post.
{ :foo => 1,
:bar => 2,
:baz => maybe,
:qux => 4
}.compact.
merge(wat: 5)
Which is fine, I guess, as illustration of the principle, but I realize that I put mixed the constants and the optional value in the first hash and merged another constant. That’s not what I would actually do and what I would do deserves some words.
Here’s an example essentially just like how I actually use this technique:
{ :foo => 1,
:bar => 2,
:qux => 4,
:wat => 5
}.merge(
{ :baz => maybe
}.compact
)
And I’ve alluded to this before in this series, in Modifying params 2 but, reviewing that code, I think realize that isn’t quite right either. I’d do something like this:
class WatsController < ApplicationController
# ...
def new
@wat = Wat.new(default_wat_params)
@wat.assign_attributes(derived_wat_params)
end
def create
@wat = Wat.create(wat_params)
# ...
end
def edit
@wat = Wat.find(params[:id])
end
def update
@wat = Wat.find(params[:id])
@wat.update(wat_params)
# ...
end
# ...
private
def default_wat_params
{ :foo => default_foo,
:bar => default_bar
}
end
def derived_wat_params
{ :baz => derived_bar(@wat),
:qux => derived_qux(@wat)
}.compact
end
def wat_params
params.require(:wat).
with_defaults(
default_wat_params
).merge(
derived_wat_params
).permit(
:foo,
:bar,
:baz,
:qux
)
end
end
And here you can see, in derived_wat_params
the usage of compact
keeping the optional values out of the derived hash to be merged into
the params with their defaults. Presumably default_foo
and
default_bar
are never-nil, but derived_baz
and derived_qux
could
be, hence, using compact
this way.
The practice of building parameters for methods instead of defining objects such that defaults and derived values are determined by the destination may need some defending, but that must be deferred.
In the meantime, I hope this follow-up seems useful enough.