在我深入研究之前,我将尝试解释我的代码库的结构。 此问题与评论中的链接问题不同,因为它涉及模块类中包含模块的列表。不直接在课程或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
}
不幸的是,这会返回相同的列表。
答案 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]