(大编辑,我在那里的一部分......) 我一直在乱砍,我想出了一个方法来指定在读取属性之前需要完成的事情:
class Class
def attr_reader(*params)
if block_given?
params.each do |sym|
define_method(sym) do
yield
self.instance_variable_get("@#{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
class Test
attr_reader :normal
attr_reader(:jp,:nope) { changethings if @nope.nil? }
def initialize
@normal = "Normal"
@jp = "JP"
@done = false
end
def changethings
p "doing"
@jp = "Haha!"
@nope = "poop"
end
end
j = Test.new
p j.normal
p j.jp
但changethings
并未被视为一种方法 - 任何人都有任何想法?
答案 0 :(得分:4)
您需要在实例的上下文中评估块。默认情况下,yield
将在其原生上下文中对其进行评估。
class Class
def attr_reader(*params, &blk)
if block_given?
params.each do |sym|
define_method(sym) do
self.instance_eval(&blk)
self.instance_variable_get("@#{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
答案 1 :(得分:1)
这是您可以看到的另一种替代方法。它并不像你使用define_method
那样优雅,但它可能值得一看。
向lazy_attr_reader
Class
class Class
def lazy_attr_reader(*vars)
options = vars.last.is_a?(::Hash) ? vars.pop : {}
# get the name of the method that will populate the attribute from options
# default to 'get_things'
init_method = options[:via] || 'get_things'
vars.each do |var|
class_eval("def #{var}; #{init_method} if !defined? @#{var}; @#{var}; end")
end
end
end
然后像这样使用它:
class Test
lazy_attr_reader :name, :via => "name_loader"
def name_loader
@name = "Bob"
end
end
行动中:
irb(main):145:0> t = Test.new
=> #<Test:0x2d6291c>
irb(main):146:0> t.name
=> "Bob"
答案 2 :(得分:1)
恕我直言,改变这个区块的上下文是非常违反直觉的,从某人的角度来看这个 attr_reader
类固醇。
也许你应该考虑使用可选参数“使用可选参数指定方法名称”的方法:
def lazy_attr_reader(*args, params)
args.each do |e|
define_method(e) do
send(params[:init]) if params[:init] && !instance_variable_get("@#{e}")
instance_variable_get("@#{e}")
end
end
end
class Foo
lazy_attr_reader :foo, :bar, :init => :load
def load
@foo = 'foo'
@bar = 'bar'
end
end
f = Foo.new
puts f.bar
#=> bar