如何增强ruby中的attr_accessor?

时间:2012-05-02 08:43:56

标签: ruby attributes metaprogramming attr-accessor

我想实现一个(类)方法attr_accessor_with_client_reset,它与attr_accessor执行相同的操作,但是在每个编写器上它还会执行

@client = nil

所以,例如,

attr_accessor_with_client_reset :foo

应该产生与

相同的结果
attr_reader :foo

def foo=(value)
  @foo = value
  @client = nil
end

我如何实现这一目标?

2 个答案:

答案 0 :(得分:8)

塞尔吉奥的解决方案很好,但是不必要地复杂:没有必要复制attr_reader的行为,你可以委托给它。并且所有这些双模块都不需要包括钩子hackery。另外,attr_accessor有多个名称,因此attr_accessor_with_client_reset也应如此。

module AttrAccessorWithClientReset
  def attr_accessor_with_client_reset(*names)
    attr_reader *names

    names.each do |name|
      define_method :"#{name}=" do |v|
        instance_variable_set(:"@#{name}", v)
        @client = nil
      end
    end
  end
end

class Foo
  extend AttrAccessorWithClientReset

  attr_reader :client
  def initialize
    @foo = 0
    @client = 'client'
  end

  attr_accessor_with_client_reset :foo
end

f = Foo.new
f.foo    # => 0
f.client # => "client"
f.foo = 1
f.foo    # => 1
f.client # => nil

答案 1 :(得分:1)

如果你有一些红宝石元编程经验,那实际上非常简单。看看:

module Ext
  def self.included base
    base.extend ClassMethods
  end

  module ClassMethods
    def attr_accessor_with_client_reset name
      define_method name do
        instance_variable_get "@#{name}"
      end

      define_method "#{name}=" do |v|
        instance_variable_set "@#{name}", v
        @client = nil
      end
    end
  end

end

class Foo
  include Ext

  attr_reader :client
  def initialize
    @foo = 0
    @client = 'client'
  end

  attr_accessor_with_client_reset :foo
end

f = Foo.new
f.foo # => 0
f.client # => "client"
f.foo = 1
f.foo # => 1
f.client # => nil

如果您不完全清楚此代码,我强烈推荐这本书:Metaprogramming Ruby