How to Get All Class Names in a Namespace in Ruby

How to get all class names in a namespace in Ruby?

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.

How to get all classes in namespace without manually loading the classes?

You could glob the files in a namespaced directory as such:

Dir.glob('/path/to/namespaced/directory/*').collect{|file_path| File.basename(file_path, '.rb').constantize}

So in a Rails initialization file or model you could do:

Dir.glob(File.join(Rails.root, "app", "models", "my_namespace", "*")).collect{|file_path| File.basename(file_path, '.rb').constantize}

How to get only class name without namespace

The canonical way to do this is to invoke Object#class and Module#name. For example:

bar.class.name.split('::').last
#=> "Bar"

How do you find the namespace/module name programmatically in Ruby on Rails?

None of these solutions consider a constant with multiple parent modules. For instance:

A::B::C

As of Rails 3.2.x you can simply:

"A::B::C".deconstantize #=> "A::B"

As of Rails 3.1.x you can:

constant_name = "A::B::C"
constant_name.gsub( "::#{constant_name.demodulize}", '' )

This is because #demodulize is the opposite of #deconstantize:

"A::B::C".demodulize #=> "C"

If you really need to do this manually, try this:

constant_name = "A::B::C"
constant_name.split( '::' )[0,constant_name.split( '::' ).length-1]

Find classes available in a Module

Classes are accessed through constants. Classes defined within a module are listed as constants in that module. So you just need to choose the constants that refer to classes.

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

Accessing a class's containing namespace from within a module

In your example, User is just a constant that points to a Class object. You can easily create another constant pointer when MyMagicMixin is included:

module MyMagicMixin
class <<self
def self.included(klass)
base.extend MyMagicMixin::ClassMethods
create_pluralized_alias(klass)
end

private

def create_pluralized_alias(klass)
fq_name = klass.to_s
class_name = fq_name.demodulize
including_module = fq_name.sub(Regexp.new("::#{class_name}$", ''))
including_module = including_module.blank? ? Object : including_module.constantize
including_module.const_set class_name.pluralize, klass
end
end

module ClassMethods
# cass methods here
end
end

Of course this doesn't answer whether you should do such a thing.

Namespace methods inside class

This isn't really doable. Namespacing is only available on the class, not instance level, so Clickup::Users would work but Clickup.new::Users can't. You could maybe do it with some metaprogramming, but it'd be a bit complicated and make your code more difficult to understand.

Don't make the mistake of thinking a namespace is anything more than that - namespacing. Just because A::B has B nested in A, doesn't mean there is any relationship between them. They have completely separate state and behavior.

The following is a somewhat similar approach that could work, though it makes you have to rewrite initialize a few times. This can be a good thing though. It means that each of the classes works independently and can have only the required dependencies passed in.

note, I took the liberty of changing the 4-space indentation to 2 since this is the norm in Ruby.

class Clickup   
def initialize(config)
@config = config
end

def users
Users.new(@config)
end

def lists
Lists.new(@config)
end

class Users
def initialize(config)
@config = config
end

def get_all
# make call to get all users
end
end

class Lists
def initialize(config)
@config = config
end

def get_all
# make call to get all lists
end
end
end

And usage would be similar to your idea:

clickup = Clickup.new(foo: "bar")
clickup.users.get_all # calls Users#get_all
clickup.lists.get_all # calls Lists#get_all

Actually, I just remembered something ... :: actually is an alias for .. This isn't something normally used, but technically it is possible to use the exact call signature you wanted:

clickup = Clickup.new(foo: "bar")
clickup::users.get_all # calls Users#get_all
clickup::lists.get_all # calls Lists#get_all

Create dynamically named class within module namespace

Object.const_set explicitly sets the constant in the Object namespace, which is the root namespace. If you use const_set without Object, it will set the constant in whatever the current namespace is, which is MyModule::SubModule in your example.

How do you find all modules and classes within a module, recursively?

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).



Related Topics



Leave a reply



Submit