开发者

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"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜