60

I have a module Foo, that it is the namespace for many classes like Foo::Bar, Foo::Baz and so on.

Is there an way to return all class names namespaced by Foo?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Sławosz
  • 11,187
  • 15
  • 73
  • 106

5 Answers5

76
Foo.constants

returns all constants in Foo. This includes, but is not limited to, classnames. If you want only class names, you can use

Foo.constants.select {|c| Foo.const_get(c).is_a? Class}

If you want class and module names, you can use is_a? Module instead of is_a? Class.

Matt Briggs
  • 41,224
  • 16
  • 95
  • 126
sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 1
    This is a great answer. You sort of forget sometimes that constants in ruby are anything that _starts_ with a capital, so class names are constant instances of type class. +1 – Matt Briggs Sep 09 '10 at 12:32
  • 5
    @jcaudle Ruby does *not* lazy load classes. You're thinking of Rails. – sepp2k Sep 20 '13 at 15:13
  • 6
    Make sure you load all the classes before: `Dir["app/models/foo/*.rb"].each {|file| load file}` – espinchi Dec 15 '13 at 16:04
  • 1
    @espinchi When giving Rails-specific advice, it is best to say so. Otherwise it might confuse people. – sepp2k Dec 15 '13 at 17:06
  • 2
    This only works for classes that have been previously loaded. – jaydel Aug 12 '19 at 13:49
  • This is NOT the correct answer, as it depends on the autoloader and loading strategies. When your class is never referenced/ needed, thus the file never looked-up via the autoloader, the class will not be listed with the constants. The reference of constants will NOT contain all the constants, because of this issue. – Dennis Dec 15 '21 at 10:09
42

If, instead of the names of the constants, you want the classes themselves, you could do it like this:

Foo.constants.map(&Foo.method(:const_get)).grep(Class)
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
16

In short no. However, you can show all classes that have been loaded. So first you have to load all classfiles in the namespace:

Dir["#{File.dirname(__FILE__)}/lib/foo/*.rb"].each {|file| load file}

then you can use a method like Jörg W Mittag's to list the classes

Foo.constants.map(&Foo.method(:const_get)).grep(Class)

Silverdev
  • 496
  • 4
  • 10
14

This will only return the loaded constants under the given namespace because ruby uses a lazy load approach. So, if you type

Foo.constants.select {|c| Foo.const_get(c).is_a? Class}

you will get

[]

but after typing:

Foo::Bar

you will get

[:Bar]
Pikachu
  • 774
  • 13
  • 15
1

This alternative solution will load and show all classes under Foo:: including "nested" classes, e.g. Foo::Bar::Bar.

Dir["#{File.dirname(__FILE__)}/lib/foo/**/*.rb"].each { |file| load file }
ObjectSpace.each_object(Class).select { |c| c.to_s.start_with? "Foo::" }

Note: **/*.rb recursively globs.

If you want to include the Foo class itself, you could change your select to e.g. { |c| c.to_s =~ /^Foo\b/ }

tantrix
  • 1,248
  • 12
  • 14