Ruby: контекст переключается при вызове динамически определенных методов - PullRequest
1 голос
/ 13 февраля 2010

Вот некоторый тестовый код, который объясняет мою проблему.Класс Child вызывает методы класса Parent.Один из методов Родителя определяет новый метод с именем foo для Родителя.После определения foo попытка вызова из дочернего класса работает, но контекст совершенно другой (я могу получить доступ только к переменным экземпляра дочернего элемента, а не к родительскому).

Я предполагаю, что это имеетчто-то делать с закрытием Руби.Должен ли я использовать что-то кроме блока, когда я вызываю define_method? Edit : я пытался использовать лямбду и процедуру, но ничего не изменилось.

class Parent
  @foo = 'foo'

  def self.existing_method
    puts "Calling existing_method, @foo is #{@foo}"
  end

  def self.define_new_method
    self.class.send :define_method, :foo do
      context = methods.include?('bar') ? 'child' : 'parent'

      puts "Context is #{context}, @foo is #{@foo.inspect}"
    end
  end
end

class Child
  @foo = 'childfoo'

  def self.method_missing(*args, &block)
    Parent.send args.shift, *args, &block
  end

  def self.bar
  end
end

Child.existing_method    # Calling existing_method, @foo is foo
Child.define_new_method
Child.foo                # Context is child, @foo is "childfoo"
                         # (in Ruby 1.9, the context is parent, but
                         # @foo is still "childfoo")

Parent.foo               # Context is parent, @foo is "foo"

Этот результатэто не то, что я хочу.Child.foo ответ должен совпадать с Parent.foo.

Заранее спасибо!

Ответы [ 3 ]

0 голосов
/ 15 февраля 2010

Это смешно; Я думаю, что сегодняшняя запись Иегуды Каца объясняет точно что вы хотите: http://yehudakatz.com/2010/02/15/abstractqueryfactoryfactories-and-alias_method_chain-the-ruby-way/


РЕДАКТИРОВАТЬ: Хорошо - это не "то, что вы просите", если подумать, поскольку это не метапрограммирование, но, скорее всего, оно ближе к тому, что вы хотите сделать. Что касается вашего конкретного ответа, я посмотрю на код.

0 голосов
/ 24 ноября 2011

в рубине 1.9х,

Метод 'методов' возвращает Symbol(method name) Array.

Поэтому используйте respond_to метод:

context = respond_to?(:bar) ? 'child' : 'parent'
0 голосов
/ 13 февраля 2010

После долгих раскопок вот что я понял.

  1. Вызов self.class.define_method фактически определяет экземпляр метод Object. Это означало, что все получило этот новый метод, который является причиной очевидного изменения контекста (Child.foo фактически вызывал Child.foo, а не Parent.foo). Упс.
  2. Чтобы определить метод класса, вам нужно получить фактический объект класса, который по какой-то причине не self, а class << self; self; end (да, я тоже этого не понял). Код кратко упоминается здесь: http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html

Руби иногда заставляет мой мозг болеть. Этот код должен возвращать ожидаемые результаты.

class Parent
  @foo = 'foo'

  def self.existing_method
    puts "Calling existing_method, @foo is #{@foo}"
  end

  def self.define_new_method
    inst = class << self; self; end # Do not understand this...
    inst.send :define_method, :foo do
      context = methods.include?(:bar) ? 'child' : 'parent'
      puts "Context is #{context}, @foo is #{@foo.inspect}"
    end
  end
end

class Child
  @foo = 'childfoo'

  def self.method_missing(*args, &block)
    return unless args.length > 0
    Parent.send args.shift, *args, &block
  end

  def self.bar
  end
end

Child.existing_method     # Calling existing_method, @foo is foo
Child.define_new_method
Child.foo                 # Context is parent, @foo is "foo"
Parent.foo                # Context is parent, @foo is "foo"
...