ruby confusing -- local variable or instance_method?
I have the following program.
module C
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def test_for
class_eval <<-DEFINECLASSMETHODS
def self.my_method(param_a)
puts "SELF is: #{self.inspect}"
puts param_a
puts "#{param_a}"
end
DEFINECLASSMETHODS
end
end
end
class A
include C
end
class B < A
test_for
end
when I run B.new.my_method("aaa")
, I got this error
NameError: undefined local variable or method `param_a' for B:C开发者_运维问答lass
I am quite confused.
I define param_a as a local variable in class method my_method,
puts param_a
runs good, and will output the "aaa".
however,
puts "#{param_a}"
output that error.
why?
Can anyone explain this?
You get that error because the #{}
doesn't interpolate param_a into the string passed to puts - it interpolates it into the string passed to class_eval. It will work when you escape it, i.e.
puts "\#{param_a}"
You can also disable interpolation inside the heredoc by using <<-'DEFINECLASSMETHODS'
instead of <<-DEFINECLASSMETHODS
. This will also allow you to use other meta characters without having to escape them.
Try using "class_eval do; end" instead, like this:
def test_for
class_eval do
def self.my_method(param_a)
puts "SELF is: #{self.inspect}"
puts param_a
puts "#{param_a}"
end
end
end
This way, no code escaping is necessary.
Those are some majorly complex hoops you are jumping through, to achieve basically this:
module C
def test_for
define_singleton_method :my_method do |param_a|
puts "SELF is: #{inspect}"
p param_a
end
end
end
class A
extend C
end
class B < A
test_for
end
B.my_method 'foo'
# => SELF is: B
# => "foo"
EDIT: I just realized that the solution above is still much more complicated than it needs to be. In fact, we do not need any metaprogramming at all:
module C
module D
def my_method(param_a)
puts "SELF is: #{inspect}"
p param_a
end
end
def test_for
extend D
end
end
class A
extend C
end
class B < A
test_for
end
B.my_method 'foo'
# => SELF is: B
# => "foo"
精彩评论