有没有一种干净的方法可以删除常见的 ruby 对象初始化代码?

Is there a clean way to remove the common ruby object initialization code?

是否有一种简洁的方法来遍历 attr_reader 属性并设置它们?

class Foo
  attr_reader :bar, :baz, ...

  def initialize attrs
    @bar = attrs[:bar]
    @baz = attrs[:baz]
    .
    .
    .

如果你想让散列键匹配属性名,一种可能是继承一个结构:

class Foo < Struct.new(:bar, :baz, keyword_init: true) # Note the "keyword_init" parameter
end

puts Foo.new(bar: 42, baz: :foo).bar
# => 42

Struct 也会创建 public writers,所以如果你不想让它们成为 public 你必须将它们设置为 private

FOO_ATTRS = [:bar, :baz]
class Foo < Struct.new(*FOO_ATTRS, keyword_init: true) # Note the "keyword_init" parameter
  # Make method 'my_attribute=' private if needed
  private *FOO_ATTRS.map { |attr| "#{attr}=" }
end

puts Foo.new(bar: 42, baz: :foo).bar

如果您想为不同的属性名称重命名散列键:

class Foo
  INITIAL_ATTRS = {
   foo: :@bar,
   bar: :@baz
  }

  attr_reader *(INITIAL_ATTRS.values.map { |k| k.to_s.delete('@').to_sym })

  def initialize attrs
    attrs.slice(*INITIAL_ATTRS.keys).each do |var, value|
      instance_variable_set(INITIAL_ATTRS[var], value)
    end
  end
end

编辑

另一种可能性是使用 private attr_writer 这样你就不必处理 @ tricks

class Foo
  INITIAL_ATTRS = {
   foo: :bar,
   bar: :baz
  }

  attr_reader *(INITIAL_ATTRS.values)

  def initialize attrs
    attrs.slice(*INITIAL_ATTRS.keys).each do |var, value|
      send("#{INITIAL_ATTRS[var]}=", value)
    end
  end

  private

   attr_writer *(INITIAL_ATTRS.values)
end

请注意,如果您不想将 foo 转换为 bar,您可以使用数组而不是 INITIAL_ATTRS 的散列。这将通过删除 .values.keys

来简化代码
class Foo
  INITIAL_ATTRS = [:foo, :bar]

  attr_reader *INITIAL_ATTRS

  def initialize attrs
    attrs.slice(*INITIAL_ATTRS).each do |var, value|
      send("#{var}=", value)
    end
  end

  private

   attr_writer *INITIAL_ATTRS
end