0

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

文件1:

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

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

4

3 回答 3

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]
于 2013-08-20T20:16:22.547 回答
0

sawa's answer is ok, and also you can use a method like this to dynamically create logger for each class.

However, I prefer not to get all classes under a module. In able to create logger for every single class, you can do things as follow.

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

For classes within a specific module you can write a filter in the logger_name method, for example:

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

And also, you can cache the logger:

def logger
  _logger = Log4r::Logger[logger_name]
  self.class.send(:define_method, :logger) { _logger }
  self.class.define_singleton_method(:logger) { _logger }
  return _logger
end

Hope it helps.

于 2015-01-07T09:23:39.747 回答
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]
于 2013-08-21T02:30:35.707 回答