Получение всех классов, определенных в модуле
У меня есть несколько файлов, определяющих вложенные модули, скажем:
File1:
module A
module B
class B1
class B1Error < Exception ; end
end
end
end
Файл 2:
module A
module B
class B2
class B2Error < Exception ; end
class B2_inner
end
end
end
end
Мне нужен метод, чтобы получить все классы, определенные в данном модуле.
def get_all_classes_under_module_hier(hier)
???
end
get_all_classes_under_module_hier(A::B)
#=> A::B::B1, A::B::B1::B1Error, A::B::B2, A::B::B2::B2Error, A::B::B2::B2_inner.
Как я могу достичь цели?
Причина, по которой мне это нужно: я пытаюсь использовать log4r. У меня есть несколько классов, и я создаю регистратор с classNames на каждом из них. В конфигурации YAML необходимо снова указать все определенные имена регистраторов для дальнейшей настройки. Я пытаюсь использовать общий код для извлечения всех классов в иерархии модулей и динамической конфигурации.
Любой вклад в мой подход к log4r (или любой более простой способ) также приветствуется.
3 ответа
Ответ Sawa в порядке, и вы также можете использовать такой метод для динамического создания логгера для каждого класса.
Однако я предпочитаю не включать все классы в модуль. В состоянии создать регистратор для каждого отдельного класса, вы можете сделать следующее.
module Kernel
#######
private
#######
def logger
Log4r::Logger[logger_name]
end
def logger_name
clazz = self.class
unless clazz.respond_to? :logger_name
name = clazz.module_eval 'self.name'
clazz.define_singleton_method(:logger_name) { name }
end
clazz.logger_name
end
end
module A
module B
class C
def hello
logger.debug logger_name
end
end
end
end
A::B::C.new.hello
Для классов в определенном модуле вы можете написать фильтр в logger_name
метод, например:
module Kernel
#######
private
#######
def logger
Log4r::Logger[logger_name]
end
def logger_name
clazz = self.class
unless clazz.respond_to? :logger_name
name = clazz.module_eval 'self.name'
name = 'root' unless name.start_with?('A::B')
clazz.define_singleton_method(:logger_name) { name }
end
clazz.logger_name
end
end
module A
module B
class C
def hello
logger.debug logger_name
end
class << self
def hello
logger.debug logger_name
end
end
end
end
class D
def hello
logger.debug logger_name
end
end
end
A::B::C.new.hello # A::B::C
A::B::C.hello # A::B::C
A::D.new.hello # root
А также, вы можете кэшировать регистратор:
def logger
_logger = Log4r::Logger[logger_name]
self.class.send(:define_method, :logger) { _logger }
self.class.define_singleton_method(:logger) { _logger }
return _logger
end
Надеюсь, поможет.
def get_all_modules_under_module_hier(hier)
a = hier
.constants
.map{|e| hier.const_get(e)}
.select{|e| e.kind_of?(Module)}
a + a.flat_map{|klass| get_all_classes_under_module_hier(klass)}
end
def get_all_classes_under_module_hier(hier)
get_all_modules_under_module_hier(hier)
.select{|e| e.instance_of?(Class)}
end
get_all_classes_under_module_hier(A::B)
# => [A::B::B1, A::B::B2, A::B::B1::B1Error, A::B::B2::B2Error, A::B::B2::B2_inner]
Вы можете использовать либо Module::nesting
как показано ниже:
Возвращает список модулей, вложенных в точке вызова.
module A
module B
class B2
class B1Error < Exception ; $b = Module.nesting ;end
class B2_inner
$a = Module.nesting
end
end
end
end
$a # => [A::B::B2::B2_inner, A::B::B2, A::B, A]
$b # => [A::B::B2::B1Error, A::B::B2, A::B, A]
или же,
Module::constants
возвращает массив имен всех констант, доступных из точки вызова.
module A
module B
class B2
class B1Error < Exception ;end
class B2_inner
$a = Module.constants
end
end
end
end
$a - Module.constants
# => [:B1Error, :B2_inner, :B2, :B]