Reputation: 38772
I'm trying to optimize some code and I want to instead on checking a value on every method call just define the method to respond with the checking already pre-calculate, because this checking doesn't change on the whole live of the instance.
I decided to define different versions of the method for every instance created. More or less this way:
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
What is happening is that something weird is happening with the variables: the variables declares out-side the class << self
block are not visible for the variables inside.
Any one can explain me how can I do the behavior I'm looking for?
Thanks
Upvotes: 0
Views: 1780
Reputation: 369438
In Ruby, only blocks can be closures, class bodies (as well as module and method bodies) cannot be closures. Or to put it another way: only blocks create a new nested lexical scope, all others (module bodies, class bodies, method bodies and script bodies) create new top-level scopes.
So, you will need a block. Normally, this would mean using some form of eval
, but here you can just use define_singleton_method
instead:
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
Upvotes: 9
Reputation: 18043
Adding to Jörg's answer: define_singleton_method is Ruby 1.9+. If you want to run it in pre 1.9, the following works:
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
Upvotes: 1