升级到Ruby 2.0.0p648后,Ruby“once”方法不再有效

时间:2017-04-11 17:25:34

标签: ruby

我有一些昂贵的潜艇,如果多次召唤,可能导致整个过程需要永久完成。

为了阻止它们被重复调用,创建了一个类似于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版本中根据需要运行?

1 个答案:

答案 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,也许你觉得它很有用