如何在 Ruby 中一次为一个对象分配多个属性

How to assign multiple attributes at once to an object in Ruby

提问人:max 提问时间:11/25/2021 最后编辑:max 更新时间:11/25/2021 访问量:914

问:

我有一个对象,想一次给它分配多个属性,类似于在 Rails 中:Fooassign_attributes

class Foo
    attr_accessor :a, :b, :c
end

f = Foo.new
my_hash = {a: "foo", b: "bar", c: "baz"}
f.assign_attributes(my_hash)

除非该类是 Rails 中的 ActiveRecord 模型,否则上述方法不起作用。有什么方法可以在 Ruby 中做到这一点吗?

Ruby-on-Rails Ruby 对象 方法

评论

0赞 Stefan 11/25/2021
顺便说一句,大多数 Rails 都是用 Ruby 实现的,所以你总是可以看看它的源代码。(尽管其中一些非常复杂,可能难以理解)

答:

7赞 Stefan 11/25/2021 #1

您可以自己实现质量分配方法。

一种选择是通过以下instance_variable_set设置相应的实例变量:

class Foo
  attr_accessor :a, :b, :c

  def assign_attributes(attrs)
    attrs.each_pair do |attr, value|
      instance_variable_set("@#{attr}", value)
    end
  end
end

请注意,这将绕过任何自定义 setter。如文档中所述:

这可能会规避类作者想要的封装,因此应谨慎使用。

另一种方法是通过public_send动态调用 setter:

  def assign_attributes(attrs)
    attrs.each_pair do |attr, value|
      public_send("#{attr}=", value)
    end
  end

这相当于按顺序设置每个属性。如果已(重新)定义 setter 以包含对所设置值的约束和控制,则后一种方法会尊重这一点。

如果您尝试设置未定义的属性,它还会引发异常:(因为相应的 setter 不存在)

f = Foo.new
f.assign_attributes(d: 'qux')
#=> NoMehodError: undefined method `d=' for #<Foo:0x00007fbb76038430>

此外,您可能希望确保传递的参数确实是哈希值,如果提供的属性无效/未知,则可能会引发自定义异常。

评论

1赞 engineersmnky 11/25/2021
我认为后者是一个更好的实现,因为实例变量赋值选项缺乏例如attr_accessorf.assign_attributes(('a'..'zzz').then {|a| a.zip(a)}.to_h)
0赞 Matheus Moreira 11/25/2021
哈希值可以通过使用 和 的约定来确保。to_hto_hash
1赞 Stefan 11/25/2021
@MatheusMoreira可能取决于您的用例,但我宁愿使用 or .is_a?(Hash)respond_to?(:each_pair)
0赞 Nitin Srivastava 11/25/2021 #2

assign_attributesActiveRecord

如果不使用assign_attributesActiveRecord

def assign_attributes(attrs_hash)
  attrs_hash.each do |k, v|
    self.instance_variable_set("@#{k}", v)
  end
end