如何实现Ruby结构的自动修改?

时间:2016-12-13 06:09:50

标签: ruby struct autovivification

Ruby通过将块传递给Hash.new来支持哈希的自动生成:

hash = Hash.new { |h, k| h[k] = 42 }
hash[:foo] += 1   # => 43

我也想为结构实现自动修复。这是我能想到的最好的:

Foo = Struct.new(:bar) do
  def bar
    self[:bar] ||= 42
  end
end

foo = Foo.new
foo.bar += 1   # => 43

当然,这只会对命名的访问者(foo.bar)进行自动生成,而不是[]表单(foo[:bar])。是否有更好的方法来实现结构的自动修复,特别是对foo.barfoo[:bar]形式有效的结构?

1 个答案:

答案 0 :(得分:3)

我会采用以下方法:

foo.bar

foo[:bar]以上通过method_missing魔术调用Struct#[],因此唯一要覆盖的是Hash#default_proc方法。

预先安装模块使其更加健壮,每个实例并且通常更灵活。

上面的代码只是一个例子。要复制module StructVivificator def self.prepended(base) raise 'Sorry, structs only!' unless base < Struct base.singleton_class.prepend(Module.new do def new(*args, &λ) # override `new` to accept block super(*args).tap { @λ = λ } end end) base.send(:define_method, :default_proc=) { |λ| @λ = λ } base.send(:define_method, :default_proc) { |&λ| λ ? @λ = λ : @λ } # override accessors (additional advantage: performance/clarity) base.members.each do |m| base.send(:define_method, m) { self[m] } base.send(:define_method, "#{m}=") { |value| self[m] = value } end end def [](name) super || default_proc && default_proc.(name) # or more sophisticated checks end end 一个人的行为(可以归功于@Stefan评论):

default_proc

现在name lambda将收到一个Foo = Struct.new(:bar, :baz) do prepend StructVivificator end foo = Foo.new foo.default_proc = ->(name) { name == :bar ? 42 : 0 } puts foo.bar # => 42 puts foo[:bar] += 1 # => 43 puts foo.bar += 1 # => 44 puts foo[:baz] += 1 # => 1 来决定如何在这种情况下表现。

<a href="/policies/shipping-policy" target="_blank" data-bind="i18n: 'Shipping Policy'"></a>