试图找到包含模块的所有类

时间:2017-12-28 19:17:54

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4

在我深入研究之前,我将尝试解释我的代码库的结构。 此问题与评论中的链接问题不同,因为它涉及模块类中包含模块的列表。不直接在课程或1级模块中

1)可能有一个X类定义为:

module ParentModule
   class X
   end
 end

2)在不同的模块下可能还有一个嵌套类:

module ParentModule
   module ChildModule
     class Y
     end
   end
 end

3)也可能只有一个模块里面有一些类:

module ParentModule
   module OtherModule
     def some_awesome_method
       ..does something
     end
   end  
 end

我正在尝试获取ParentModule中包含OtherModule的类列表。以下是我到目前为止的工作情况:

include_resources = ->(klass) {
        begin
          klass_string = klass.to_s
          klass_resource = klass_string.constantize
          klass_string if klass_resource.included_modules.include?(OtherModule)
        rescue NameError # skip all bad naming and other irrelevant constant loading mishaps
          next
        end
      }

所以,如果我ParentModule.constants.find_all(&include_resources),我会得到包含OtherModule的类列表,太棒了!但遗憾的是,它无法找到嵌套在子模块下的类,如#2示例所示。所以我试着这样做:

include_resources = ->(klass) {
        begin
          klass_string = klass.to_s
          klass_resource = klass_string.constantize
          if klass_resource.class == Module
            return "ParentModule::#{klass_string}".constants.find_all do |module_klass|
              module_klass.constantize.included_modules.include?(OtherModule)
            end
          end
          klass_string if klass_resource.included_modules.include?(OtherModule)
        rescue NameError # skip all bad naming and other irrelevant constant loading mishaps
          next
        end
  } 

不幸的是,这会返回相同的列表。

1 个答案:

答案 0 :(得分:1)

[注意:@engineersmnky说明了使用flat_map执行此操作的方法,无需matching_classes参数。我发现它更难理解,但它完全合理地使用了flat_map和一个有价值的解决方案。代码发布在https://repl.it/@engineersmnky/IllinformedMountainousAnkole]

以下代码使用递归来下移模块的倒置树。结果(最后打印)是正确的,包括两个模块中的类。 (我编写了一个最小的模块和类层次结构来作为例子。)

#!/usr/bin/env ruby

module ParentModule
  module OtherModule; end
  class ParentClassYes; include OtherModule; end
  class ParentClassNo;  end

  module ChildModule
    class ChildClassYes; include OtherModule; end
    class ChildClassNo;  end
  end
end


def classes_for_module_tree(the_module, matching_classes = [])
  the_module.constants.each_with_object(matching_classes) \
        do |const, matching_classes|
    value = the_module.const_get(const)
    if value.is_a?(Class)
      if value.included_modules.include?(ParentModule::OtherModule)
        matching_classes << value
      end
    elsif value.is_a?(Module)
      # Here is where we call this method recursively. We suspend collecting
      # matches for this module, call the method to process the newly found
      # (sub)module, then use the array returned by that invocation to resume
      # processing this module.
      matching_classes = classes_for_module_tree(value, matching_classes)
    end
  end
  matching_classes
end


p classes_for_module_tree(ParentModule)
# prints: [ParentModule::ParentClassYes, ParentModule::ChildModule::ChildClassYes]