Reputation: 3
I have following method with lots of parameters. I have converted this method from parameter list to Hash to avoid mess with default parameters.
def foo_bar(options = {})
Rails.logger.info("Got params #{options.inspect}")
good_name = options[:foo1]
another_param = options[:foo2]
yet_another = options[:foo3] || "yes"
still_one = options[:foo3]
valid_name = options[:foo4]
easy_name = options[:foo5]
mode = options[:foo6]
..
Calling this method is now easy, using just params that are necessary in context, that is good
foo_bar(foo1: my_foo1, foo3: my_foo3, foo6: my_mode)
But my question is, since Rails is often very intuitive language where you have many shortcuts and ways to do things, how to make this method param signature more clear so
want to avoid this since cannot freely choose which params I want assign value to when calling this method. Also call would not be clear which params is which since they are not referenced by name, only by value and order.
def foo_bar(good_name, another_param = nil, yet_another = "yes", still_one = "xx", valid_name = nil, easy_name = nil, foo6 = "std")
Method overloading is not working here either tried that too. That is not implemented like in C# or Java.
I have tried: Method overloading Parameters as hash Parameters "normal way"
Upvotes: 0
Views: 72
Reputation: 102001
The modern way of declaring the method would be to use keyword arguments instead of a hash as a positional argument.
def foo_bar(**options)
# ...
end
One of the issues with the positional argument approach is that you can actually pass input that's not a hash and it will blow up inside the method instead of getting an argument error which will immediately tell you what's wrong.
**
is a double splat and means that we are slurping up any keyword arguments passed to the method.
There are many ways of handling default values. If you don't want to list them explicitly in the method definition (def foo(bar: :Baz, ...)
) you can have a private method which produces the default options:
class Foo
def bar(**kwargs)
options = kwargs.reverse_merge(default_options)
# ...
end
private
def default_options
{
a: 1,
b: 2
}
end
end
This lets subclasses override the method and is found all over the Rails codebase.
reverse_merge
is from ActiveSupport, you can do the same thing with merge
in plain Ruby but in the reverse order.
I don't have to extract params from hash, they would automatically be assigned to correcponding local var
This pattern is commonly referred to as mass assignment:
class Thing
attr_accessor :foo
attr_accessor :bar
def initialize(**attributes)
attributes.each do |key, value|
send("#{key}=", value)
end
end
end
Thing.new(foo: "a", bar: "b")
For every keyword argument passed to the method we attempt to call a setter metod with the same name as the key. While you can call instance_variable_set
and set Ivars directly that doesn't allow you to control what can be assigned and how.
In Rails you get this for free in your models from ActiveModel::AttributeAssignment and it can be mixed into any class if desired.
Upvotes: 1