Instance_eval: почему класс подкласса является суперклассом - PullRequest
2 голосов
/ 03 мая 2010
    def singleton_class
       class << self
         self
       end
    end

    class Human
      proc = lambda { puts 'proc says my class is ' + self.name.to_s }

      singleton_class.instance_eval do
        define_method(:lab)  do 
          proc.call
        end
      end
    end

    class Developer < Human
    end

    Human.lab # class is Human
    Developer.lab # class is Human ; oops

Следующее решение работает.

def singleton_class
  class << self
    self
  end
end

class Human
  proc =  lambda { puts 'proc says my class is ' + self.name.to_s }
  singleton_class.instance_eval do
    define_method(:lab) do
      self.instance_eval &proc
    end
  end
end

class Developer < Human
end

Human.lab # class is Human
Developer.lab # class is Human ; oops

Почему Developer.lab сообщает, что это Человек? И что можно сделать, чтобы proc сообщал разработчику при запуске Developer.lab.

Ответы [ 3 ]

4 голосов
/ 03 мая 2010

Замыкание захватывает self в контексте, где оно определено - точно так же, как и замыкания. Поэтому, когда он вызывается, он будет использовать ссылки на захваченный контекст. Закрытие не является идеальным инструментом для определения предполагаемой функциональности. Вместо proc.call содержимое вызова «define_method» должно быть «put» proc говорит, что мой класс - «+ name.to_s»

4 голосов
/ 03 мая 2010

Это тонко, но сводится к простому вызову блока (в этом случае оно действует как нормальное замыкание, а self соответствует тому, где оно было определено, то есть в Human), или его использованию (напрямую) как блок для определения метода или instance_eval:

def singleton_class
   class << self
     self
   end
end


class Human

  PROC = proc { puts 'proc says my class is ' + self.name.to_s }

  singleton_class.instance_eval do
    define_method(:lab)  do 
      PROC.call
    end
    define_method(:lab2, &PROC.method(:call))

    define_method(:lab3)  do 
      instance_eval(&PROC)
    end
    define_method(:lab4, &PROC) 
  end
end

class Developer < Human
end

Human::PROC.call  # => "class is Human"  (original closure)
Developer.lab     # Same as previous line, so "class is Human"  (original closure)
Developer.lab2    # ditto

Developer.instance_eval(&Human::PROC)  # => "class is Developer"  (instance_eval changes sets a different scope)
Developer.lab3    # Same as previous line, so "class is Developer"
Developer.lab4    # ditto
1 голос
/ 03 мая 2010

Мне нужно немного подумать о , почему это работает, но на данный момент работает :

class Human
  proc = -> { name }

  define_singleton_method(:lab, &proc)
end

class Developer < Human; end

require 'test/unit'
class TestClosures < Test::Unit::TestCase
  def test_that_the_human_class_is_named_human
    assert_equal 'Human', Human.lab
  end
  def test_that_the_developer_class_is_named_developer
    assert_equal 'Developer', Developer.lab
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...