使用实例变量在初始化时定义单例方法

时间:2010-11-16 16:52:03

标签: ruby metaprogramming

我正在尝试优化一些代码,我想而不是检查每个方法调用上的值只需定义响应方法,检查已经预先计算,因为此检查不会在实例的整个实时中发生变化。

我决定为每个创建的实例定义不同版本的方法。或多或少这样:

class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    class << self
      METHODS.each do |method_name|
        if( favorite_method == method_name )
          define_method method_name do
            puts "#{method_name} its my favorite method"
          end
        else
          define_method method_name do
            puts "#{method_name} its not my favorite method"
          end
        end
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d

# $ ruby test/testing_singleton_methods_with_variable.rb 
# test/testing_singleton_methods_with_variable.rb:7:in `initialize': undefined local variable or method `favorite_method' for #<Class:#<TestingSingletonMethodsWithVariable:0x1001a77b8>> (NameError)
#   from test/testing_singleton_methods_with_variable.rb:6:in `each'
#   from test/testing_singleton_methods_with_variable.rb:6:in `initialize'
#   from test/testing_singleton_methods_with_variable.rb:21:in `new'
#   from test/testing_singleton_methods_with_variable.rb:21

正在发生的事情是变量发生了一些奇怪的事情:变量声明外部class << self块对于内部变量是不可见的。

任何人都可以解释我如何才能完成我正在寻找的行为?

由于

2 个答案:

答案 0 :(得分:9)

在Ruby中,只有块可以是闭包,类体(以及模块和方法体)不能是闭包。或者换句话说:只有块创建一个新的嵌套词法范围,所有其他(模块体,类主体,方法体和脚本体)创建新的顶级范围。

所以,你需要一个块。通常,这意味着使用某种形式的eval,但在这里您只需使用define_singleton_method

class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    METHODS.each do |method_name|
      if favorite_method == method_name
        define_singleton_method method_name do
          puts "#{method_name} its my favorite method"
        end
      else
        define_singleton_method method_name do
          puts "#{method_name} its not my favorite method"
        end
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d

答案 1 :(得分:1)

添加到Jörg的答案:define_singleton_method是Ruby 1.9+。如果你想在1.9之前运行它,以下工作:

class Object
  def metaclass
    class << self; self; end
  end
end
class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    METHODS.each do |method_name|
      if( favorite_method == method_name )
        metaclass.send(:define_method, method_name, Proc.new do
          puts "#{method_name} its my favorite method"
        end)
      else
        metaclass.send(:define_method, method_name, Proc.new do
          puts "#{method_name} its not my favorite method"
        end)
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d