如果你有:
module A
class B
end
end
你可以通过A.constants找到B和类似的类。但是,在Ruby 1.9.3中,如果它在另一个模块中,则无法获得B.在Ruby 1.8.7中你可以。
module A
module Aa
class B
end
end
end
你如何从A的第一级获得B?我想要输出的是一个常量数组,其中包括所有类,如B,但在模块A中的任何位置。
答案 0 :(得分:14)
class Module
def all_the_modules
[self] + constants.map {|const| const_get(const) }
.select {|const| const.is_a? Module }
.flat_map {|const| const.all_the_modules }
end
end
A.all_the_modules
# => [A, A::Aa, A::Aa::B]
如果你确实有圆形命名空间,那么这段代码就会中断
A::Aa::B.const_set(:A, A)
。
答案 1 :(得分:0)
当我在RSpec之类的大型库上尝试Reactormonk's answer时,我出现了堆栈溢出的情况。 这是一个解决方案,应通过检查以确保“子级”确实是我们要遍历的父级模块的子级,来过滤掉循环引用和外部引用。
def parent_of(mod)
parent_name = mod.name =~ /::[^:]+\Z/ ? $`.freeze : nil
Object.const_get(parent_name) if parent_name
end
def all_modules(mod)
[mod] + mod.constants.map { |c| mod.const_get(c) }
.select {|c| c.is_a?(Module) && parent_of(c) == mod }
.flat_map {|m| all_modules(m) }
end
({parent_of()
方法是从ActiveSupport的Module#parent改编而成的,该方法似乎无法可靠地用于库类。)
module Foo
class Bar
module Baz
class Qux
CORGE = Random::Formatter
GARPLY = Foo::Bar::Baz
module Quux
end
end
end
end
end
Foo::Bar::Baz::Qux::CORGE.is_a?(Module)
# => true
all_modules(Foo)
# => [Foo, Foo::Bar, Foo::Bar::Baz, Foo::Bar::Baz::Qux, Foo::Bar::Baz::Qux::Quux]