【发布时间】:2015-01-10 03:08:38
【问题描述】:
问题
我发现自己经常使用一种模式,所以我想把它擦干。我有这样的东西:
class InfoGatherer
def foo
true
end
def people
unless @people
@people = # Long and complex calculation (using foo)
end
@people
end
end
我想把它晾干,看起来像这样:
class InfoGatherer
extend AttrCalculator
def foo
true
end
attr_calculator(:people) { # Long and complex calculation (using foo) }
end
为了实现这一点,我定义了一个模块 AttrCalculator 以扩展为 InfoGatherer。这是我尝试过的:
module AttrCalculator
def attr_calculator(variable_name_symbol)
variable_name = "@#{variable_name_symbol}"
define_method variable_name_symbol do
unless instance_variable_defined?(variable_name)
instance_variable_set(variable_name, block.call)
end
instance_variable_get(variable_name)
end
end
end
不幸的是,当我尝试像InfoGatherer.new.people 这样简单的事情时,我得到:
NameError: undefined local variable or method `foo' for InfoGatherer:Class
嗯,这很奇怪。为什么block运行在InfoGatherer:Class的范围内,而不是它的实例InfoGatherer.new?
研究
我知道我不能使用yield,因为那会尝试捕获错误的块,如here 所示。
我尝试使用self.instance_exec(block) 代替上面的block.call,但随后收到一个新错误:
LocalJumpError: no block given
嗯?我在this SO question 中看到了同样的错误,但我已经在使用括号表示法,所以那里的答案似乎不适用。
我也尝试使用class_eval,但我不确定如何在字符串中调用block。这肯定行不通:
class_eval("
def #{variable_name_symbol}
unless #{variable_name}
#{variable_name} = #{block.call}
end
#{variable_name}
end
")
【问题讨论】:
标签: ruby memoization