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, akaA::Aa::B.const_set(:A, A)
.
Related Topics
Ruby on Rails App on Google App Engine
Ruby: How to Get the First Character of a String
Are There Good Reasons for 'Private' to Work the Way It Does in Ruby
How to Write a Web Scraper in Ruby
Hash['Key'] to Hash.Key in Ruby
Ruby - Using Class_Eval to Define Methods
In Ruby, What Is the Cleanest Way of Obtaining the Index of the Largest Value in an Array
How to Convert Array of Activerecord Models to CSV
Where to Place/Access Config File in Gem
How Would You Test Observers with Rspec in a Ruby on Rails Application
Ruby: Create a String from Bytes
How to Use Array#Delete While Iterating Over the Array
How to Get the Number of Days in a Given Month in Ruby, Accounting for Year
Concatenating String with Number in Ruby