If you have:
module A
class B
end
end
You can find B and similar classes via A.constants. However, in Ruby 1.9.3, you cannot get B if i
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]
This code will break if you do have circular namespaces, aka
A::Aa::B.const_set(:A, A)
.
I was getting stack overflows when I tried Reactormonk's answer on large libraries like RSpec. Here's a solution that should filter out circular references and foreign references by checking to make sure that "children" are really children of the parent module we're iterating through:
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
(The parent_of()
method is adapted from ActiveSupport's Module#parent, which doesn't seem to work reliably for library classes.)
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]