我有一些昂贵的潜艇,如果多次召唤,可能导致整个过程需要永久完成。
为了阻止它们被重复调用,创建了一个类似于date.rb中的Once
模块:
module Once
# modify a method to run only once
def once(*ids) # :nodoc:
ids.each do |id|
module_eval <<-"end;"
alias_method :__#{id.to_i}__, :#{id.to_s}
private :__#{id.to_i}__
def #{id.to_s}(*args, &block)
(@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &block)])[0]
end
end;
end
end
end
使用的是这样的:
def expensive_function
blah, blah
end
once :expensive_function
这在Ruby 1.8.6中运行良好但是现在,在我升级到Ruby 2.0.0p648后,我收到以下错误:
:in `block in once': undefined method `to_i' for :log_level_from_env:Symbol (NoMethodError)
此错误引用包含alias_method
的行号。
需要进行哪些修改才能更正此模块,以便在我当前的Ruby版本中根据需要运行?
答案 0 :(得分:1)
首先要注意的是,因为ruby 2.0 def method_name; end
会返回:method_name
符号,所以你可以用这样的方式编写
once def method_name
...
end
以下是在ruby 2.3.3中使用的工作片段
module Once
# modify a method to run only once
def once(*method_names) # :nodoc:
method_names.each do |method_name|
module_eval <<~RUBY
alias_method :__#{method_name.to_s}__, :#{method_name.to_s}
private :__#{method_name.to_s}__
def #{method_name.to_s}(*args, &block)
(@__#{method_name.to_s}__ ||= [__#{method_name.to_s}__(*args, &block)])[0]
end
RUBY
end
end
end
class FooBar
extend Once
once def test
puts 'FooBar'
end
end
foo_bar = FooBar.new
100.times do
foo_bar.test
end
<强> P上。 S上。强>
还有一个很好的宝石是为了同样的目的而创建的memoizer,也许你觉得它很有用