获取模块下定义的所有类

时间:2013-08-20 20:12:35

标签: ruby reflection log4r

我有几个定义嵌套模块的文件,比如说:

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的logger。在YAML配置中,需要再次指出所有已定义的记录器名称以进一步配置。我正在尝试使用通用代码来提取模块层次结构下的所有类并进行动态配置。

关于我对log4r的方法(或任何更简单的方法)的任何输入也不胜感激。

3 个答案:

答案 0 :(得分:0)

您可以使用以下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]

答案 1 :(得分:0)

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]

答案 2 :(得分:0)

sawa的答案还可以,你也可以使用像this这样的方法为每个类动态创建记录器。

但是,我不希望在模块下获得所有课程。能够为每个类创建记录器,您可以执行以下操作。

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

希望它有所帮助。