Ответ 1
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]
У меня есть mixin, для которого я хотел бы получить список всех классов, которые его включили. В модуле 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"]
Теперь я хотел бы извлечь эту часть в отдельный модуль, который может быть включен в мои другие mixins. Итак, я придумал следующее:
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
Что мне не хватает?
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]
На самом деле, модуль расширения модуля работает. Проблема в вашем тесте: когда вы создали случайный неназванный класс с Class.new
, вы забыли включить MyModule
. В качестве дополнительной заметки вы можете использовать свой доступ только для чтения для классов, которые включают модуль, и использовать полезный метод Module#attr_reader
.
Вероятно, вы должны использовать расширение вместо include, поскольку ранее добавляет методы уровня класса, тогда как методы уровня последнего экземпляра (почему у вас есть доступ к @classes
).
Попробуйте следующее:
module MyModule
extend ListIncludedClasses::ClassMethods
end