我有以下带有很多参数的方法。我已将此方法从参数列表转换为哈希,以避免与默认参数混淆。
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]
..
现在调用这个方法很容易,只使用上下文中必需的参数,这很好
foo_bar(foo1: my_foo1, foo3: my_foo3, foo6: my_mode)
但我的问题是,由于 Rails 通常是非常直观的语言,你有很多快捷方式和方法来做事情,如何使这个方法参数签名更清晰
想要避免这种情况,因为在调用此方法时无法自由选择我想要为其赋值的参数。另外,调用也不清楚哪个参数是哪个参数,因为它们不是按名称引用,而是按值和顺序引用。
def foo_bar(good_name, another_param = nil, yet_another = "yes", still_one = "xx", valid_name = nil, easy_name = nil, foo6 = "std")
方法重载在这里不起作用,也尝试过。这不像 C# 或 Java 那样实现。
我尝试过: 方法重载 参数作为哈希值 参数“正常方式”
首先,声明该方法的现代方法是使用关键字参数而不是哈希作为位置参数。
def foo_bar(**options)
# ...
end
位置参数方法的问题之一是,您实际上可以传递不是哈希的输入,它会在方法内部爆炸,而不是得到一个参数错误,这会立即告诉您出了什么问题。
**
是一个双标,意味着我们正在吞噬传递给该方法的任何关键字参数。
处理默认值的方法有很多种。如果您不想在方法定义中显式列出它们,您可以使用一个私有方法来生成默认选项:
class Foo
def bar(**kwargs)
options = kwargs.reverse_merge(default_options)
# ...
end
private
def default_options
{
a: 1,
b: 2
}
end
end
这让子类可以重写该方法,并且在 Rails 代码库中随处可见。
reverse_merge
来自 ActiveSupport,您可以在普通 Ruby 中使用 merge
执行相同的操作,但顺序相反。
我不必从哈希中提取参数,它们会自动分配给相应的本地变量
这种模式通常称为批量分配:
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")
对于传递给方法的每个关键字参数,我们尝试调用与键同名的 setter 方法。虽然您可以直接调用
instance_variable_set
并设置 Ivars,但这不允许您控制可以分配的内容和方式。
在 Rails 中,您可以从 ActiveModel::AttributeAssignment 在模型中免费获得此功能,并且如果需要,可以将其混合到任何类中。