问题
I want to implement a (class) method attr_accessor_with_client_reset
, which does the same thing as attr_accessor
, but on every writer it additionally executes
@client = nil
So, for example,
attr_accessor_with_client_reset :foo
should produce the same result as
attr_reader :foo
def foo=(value)
@foo = value
@client = nil
end
How do I achieve this?
回答1:
Sergio's solution is good, but needlessly complex: there's no need to duplicate the behavior of attr_reader
, you can just delegate to it. And there's no need for all this double module include hook hackery. Plus, attr_accessor
takes multiple names, so attr_accessor_with_client_reset
should, too.
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
回答2:
It's actually pretty straightforward if you have some experience in ruby metaprogramming. Take a look:
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
If this code is not completely clear to you, then I strongly recommend this book: Metaprogramming Ruby.
来源:https://stackoverflow.com/questions/10410391/how-to-enhance-attr-accessor-in-ruby