Ruby метапрограммирование, определение нескольких «унаследованных» функций - PullRequest
5 голосов
/ 09 сентября 2010

Я хочу, чтобы в мой класс был включен следующий модуль:

module InheritanceEnumerator  
  def self.included(klass)  
      klass.instance_eval do  
        instance_variable_set('@subclasses',[])  
        def self.subclasses  
          @subclasses  
        end  
        original_method = self.respond_to?(:inherited) ? self.public_method(:inherited) : nil  
        instance_variable_set('@original_inherited_method', original_method)  
        def self.inherited(subclass)  
          @original_inherited_method.call(subclass) if @original_inherited_method  
          @subclasses<<subclass  
        end  
      end  
   end  
end  

Я пытаюсь добиться того, чтобы в моем родительском классе были ссылки на прямых потомков.Мне также нужны любые другие предыдущие «унаследованные» методы, установленные в моем классе другими вещами, чтобы оставаться на месте.Что я делаю не так?

1 Ответ

2 голосов
/ 10 сентября 2010

Ваш код работает в этой ситуации (для меня):

class C; include InheritanceEnumerator; end
C.subclasses #=> []
class C1 < C; end
class C2 < C; end
C.subclasses #=> [C1, C2]

, но не работает в следующей ситуации:

class C11 < C1; end
C1.subclasses => NoMethodError: undefined method `<<' for nil:NilClass

Это потому, что вы инициализируете @subclasses только когдамодуль включен;но вы забываете, что подклассы C также имеют доступ к методам модулей, но явно не include it.

Это можно исправить, выполнив следующее:

def self.subclasses
    @subclasses ||= []
    @subclasses
end

def self.inherited(subclass)
    @original_inherited_method.call(subclass) if @original_inherited_method

    @subclasses ||= []
    @subclasses << subclass  
end

РЕДАКТИРОВАТЬ:

Хорошо, в будущем, пожалуйста, укажите, в чем ваша проблема, и предоставьте тестовый код, который вы используете;так как это было упражнение в разочаровании.

Следующее прекрасно работает с вашим кодом:

class C
    def self.inherited(s)
        puts "inherited by #{s}!"
    end

    include InheritanceEnumerator
end

class D < C; end #=> "inherited by D!"
C.subclasses #=> [D]

Возможно, причина, по которой он не работает для вас, состоит в том, что вы включили InheritanceEnumerator до вы определили метод inherited?

...