Получение списка классов, включающих модуль - PullRequest
15 голосов
/ 12 октября 2010

У меня есть миксин, для которого я хотел бы получить список всех классов, которые его включили.В модуле mixin я сделал следующее:

module MyModule
  def self.included(base)
    @classes ||= []
    @classes << base.name
  end

  def self.classes
    @classes
  end
end

class MyClass
  include MyModule
end

Это работает очень хорошо:

> MyModule.classes #=> nil
> MyClass.new #=> #<MyClass ...>
> MyModule.classes #=> ["MyClass"]

Теперь я хотел бы выделить эту часть в отдельный модуль, который может бытьвключены в мои другие миксины.Итак, я пришел к следующему:

module ListIncludedClasses
  def self.included(base)
    p "...adding #{base.name} to #{self.name}.classes"

    @classes ||= []
    @classes << base.name

    base.extend(ClassMethods)
  end

  def self.classes
    @classes
  end

  module ClassMethods
    def included(module_base)
      p "...adding #{module_base.name} to #{self.name}.classes"

      @module_classes ||= []
      @module_classes << module_base.name
      super(module_base)
    end
    def classes
      @module_classes
    end
  end

end

module MyModule
  include ListIncludedClasses
end

Это не работает, потому что метод #included (module_base), добавляемый в MyModule из ListIncludedClasses, никогда не запускается.Интересно, что он успешно добавляет #classes в MyModule.

> MyModule.classes #=> 
  "...adding Rateable to ListIncludedClasses.classes"
  => nil 
> ListIncludedClasses #=> ["MyModule"]
> MyClass.new #=> #<MyClass ...>
# ^^ THIS SHOULD BE ADDING MyClass TO MyModule.classes ^^
> MyModule.classes #=> nil

Чего мне не хватает?

Ответы [ 3 ]

17 голосов
/ 07 марта 2016
module MyMod; end

class A; include MyMod; end
class B < A; end
class C; end

ObjectSpace.each_object(Class).select { |c| c.included_modules.include? MyMod }
  #=> [B, A]
2 голосов
/ 16 марта 2011

На самом деле, ваш модуль расширения модуля работает.Проблема в вашем тесте: когда вы создали случайный безымянный класс с Class.new, вы забыли включить MyModule.В качестве дополнительного примечания вы можете использовать свой метод доступа только для чтения для классов, которые включают модуль, и использовать полезный метод Module#attr_reader.

1 голос
/ 12 октября 2010

Вы, вероятно, должны использовать extension вместо include, поскольку первый добавляет методы уровня класса, а второй - методы уровня экземпляра (почему у вас есть доступ к @classes).

Попробуйте это:

module MyModule
  extend ListIncludedClasses::ClassMethods
end
...