запутанный рубин - локальная переменная или instance_method? - PullRequest
0 голосов
/ 14 мая 2010

У меня есть следующая программа.

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

когда я запускаю B.new.my_method("aaa"), я получаю эту ошибку

NameError: undefined local variable or method `param_a' for B:Class

Я совершенно сбит с толку.

Я определяю param_a как локальную переменную в методе класса my_method,

puts param_a

работает нормально и выдаст «ааа».

однако,

puts "#{param_a}"

вывести эту ошибку.

почему

Может кто-нибудь объяснить это?

Ответы [ 3 ]

3 голосов
/ 14 мая 2010

Вы получаете эту ошибку, потому что #{} не интерполирует param_a в строку, переданную в put - он интерполирует ее в строку, переданную class_eval. Это сработает, когда вы избежите этого, т.е.

puts "\#{param_a}"

Вы также можете отключить интерполяцию внутри heredoc, используя <<-'DEFINECLASSMETHODS' вместо <<-DEFINECLASSMETHODS. Это также позволит вам использовать другие метасимволы без необходимости экранировать их.

2 голосов
/ 14 мая 2010

Попробуйте вместо этого использовать "class_eval do; end", например:

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 

Таким образом, экранирование кода не требуется.

1 голос
/ 14 мая 2010

Это несколько сложных обручей, через которые вы прыгаете, чтобы достичь в основном этого:

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"

РЕДАКТИРОВАТЬ: Я только что понял, что решение выше все еще гораздо сложнее, чем нужно. На самом деле, нам вообще не нужно метапрограммирование :

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