我正在尝试优化一些代码,我想而不是检查每个方法调用上的值只需定义响应方法,检查已经预先计算,因为此检查不会在实例的整个实时中发生变化。
我决定为每个创建的实例定义不同版本的方法。或多或少这样:
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
块对于内部变量是不可见的。
任何人都可以解释我如何才能完成我正在寻找的行为?
由于
答案 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