I have a module MyModule. I dynamically load classes into it. How can I get a list of the classes defined within its namespace?
Example:
def load_plugins
Dir.glob(File.dirname(__FILE__) + '/plugins/*.rb') do |f|
MyModule.class_eval File.read(f)
end
# now how can I find the new classes I've loaded into MyModule?
end
I should say that each f contains something like "class Foo; end".
You can also think of it like this: in Rails, how could I programatically find all classes defined within the ActiveRecord 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}
If you're on rails, you need to access the constants first for them to show up, because they're lazyly loaded.
MyModule::NotAClass = "not a class"
MyModule.constants => [:NotAClass]
MyModule::AClass => :AClass # Access class for it to be autoloaded
MyModule.constants => [:AClass, :NotAClass]
# Now, select the constants which are class instances
MyModule.constants
.map(&MyModule.method(:const_get))
.select { |constant| constant.is_a? Class}
=> [MyModule::AClass]**
If you'd like to get all classes in a module recursively, you can do something like
def get_classes(mod)
mod.constants.map do |c|
const = mod.const_get(c)
if const.is_a? Class
const
elsif const.is_a? Module
get_classes(const)
else
next
end
end.flatten
end
Then, given some module structure like:
module MyModule
module Submodule1
class Class1
end
end
module Submodule2
class Class2
end
end
end
the output looks like:
puts get_classes(MyModule)
# => MyModule::Submodule1::Class1
# => MyModule::Submodule2::Class2
Related
I am using Ruby to create some simple project, and I am following RubyGems project structure. In my codebase I have two classes in different "namespaces":
lib
u
x
class_a.rb
m
p
class_b.rb
I am using nested modules for this classes, so ClassA is in module X which is in module U.
While requiring ClassA inside ClassB I can use it by referecing it with U::X::ClassA. I wonder if there is any pattern that will let me just typing ClassA, without full namespace.
You can do something like
module M::P
ClassA = U::X::ClassA
end
defining ClassA as a constant inside P. It's not a good practise, but you can do it..
IMO, just use U::X::ClassA.
Two comments in advance:
I agree to Fede, IMO, just use U::X::ClassA.
the namespace in ruby is independent from the file structure. So your example may explain what you want, but it does not give a code example.
That said, you may build a minimal example like this:
module U
module X
class Class_a
end
end
end
module M
module P
class Class_b
include U::X
def initialize
a = U::X::Class_a.new ##you want replace this with:
#a= Class_a.new
end
end
end
end
M::P::Class_b.new
Fedes solution with Class_a = U::X::Class_a would look like:
module U
module X
class Class_a
end
end
end
module M
module P
Class_a = U::X::Class_a ##<- define here the local version
class Class_b
def initialize
a= Class_a.new
end
end
end
end
M::P::Class_b.new
Another possibility is to include the module that contains the class:
module U
module X
class Class_a
end
end
end
module M
module P
class Class_b
include U::X #<- Include the module
def initialize
a= Class_a.new
end
end
end
end
M::P::Class_b.new
Attention: This solution will include all classes and constants of the module U::X. This may be a solution you need, but it may also be wrong for your purpose.
I was looking at the Nokogiri source code, and found this:
module Nokogiri
class << self
def XML # some args
# some code
end
end
module XML
# more code
end
end
How come the names don't conflict? How does it know what I'm referring to when then using Nokogiri::XML? Is this best practice? If so, is this just a way to get default module entry-points as is the case here?
xml = Nokogiri::XML(open("www.foo.com/bar.xml"))
They are really look very similar:
module Noko
class << self
def XML # some args
'method'
end
end
module XML
end
end
Noko::XML #access to a module
=> Noko::XML
Noko.XML # access to class method
=> "method"
Noko::XML() #access to a class method too
=> "method"
So, we can say, that without any params this expression behaves like a module, with params - like a method.
More about scope resolution operator in Ruby you can see in this SO question.
Is it possible to know all the classes defined inside a module in ruby.
module A
class Klass
end
class Klass1
end
end
Is there any ruby introspection method to get all the classes defined in module A?
Here is one way
module A
class Klass
end
X = 10
module B;end
end
# Just to list the class(s) defined inside A
A.constants.select { |k| A.const_get(k).instance_of? Class } # => [:Klass]
Nice post to do the same in recursively.
I have a class within several modules: This::Is::A::Long::ClassName. Is there any way, within one script or method, to make ClassName available without having to reference the namespace? Instead of writing:
This::Is::A::Long::ClassName.do_something
This::Is::A::Long::ClassName.do_something_else
This::Is::A::Long::ClassName.do_something_different
is anything as below possible?
include This::Is::A::Long
ClassName.do_something
ClassName.do_something_else
ClassName.do_something_different
If you are using modules for namespacing, the code you posted should work, see this example:
module Long
module Name
class ClassName
end
end
end
ClassName
# => ... uninitialized constant ClassName (NameError)
include Long::Name
ClassName
# => Long::Name::ClassName
Ruby has no equivalent to C++ using namespace, and you can not reference a class without being in the right namespace, but you can always make it a variable since a class is also an object
long_class = This::Is::A::Long::ClassName
long_class.do_something
long_class.do_something_else
# and so on
EDIT
An include does not put you in the right namespace, it includes the methods & classes in the module you are including (that is, it puts the module in the classes ancestors) and is therefore most certainly not suitable for your needs: Consider the following:
module This
module Is
module A
def foo
puts 'A#foo'
end
def bar
puts 'A#bar'
end
class ClassName
end
end
end
end
Now, you may not want to write This::Is::A::ClassName in another class, let's say:
class C
def foo
puts 'C#foo'
end
end
class B < C
include This::Is::A
end
Now, B.new.foo still puts out C#foo, right? Wrong. Since you included the module, the method has been overwritten.
I have some existing ruby classes in a app/classes folder:
class A
...
end
class B
...
end
I'd like to group those classes in a module MyModule
I know I could do like:
module MyModule
class A
...
end
class B
...
end
end
but is there a meta programming shortcut that could do the same so I could "import" all the existing classes ?
Thanks,
Luc
module Foo
A = ::A
B = ::B
end
Foo::A.new.bar
Note that the :: prefix on a constant starts searchign the global namespace first. Like a leading / on a pathname. This allows you differentiate the global class A from the modularized constant Foo::A.
Use the const_missing hook. If the constant can't be found in the current module, try to resolve in the global namespace:
class A; end
class B; end
module M
def self.const_missing(c)
Object.const_get(c)
end
end
M::A.new
M::B.new
#Squeegy's answer already tells you what to do, but I think it is equally important to understand why that works. And it's actually pretty simple: classes in Ruby are nothing special. They are just objects like any other object that get assigned to variables just like any other variable. More precisely: they are instances of the Class class and they usually get assigned to constants (i.e. variables whose name starts with an uppercase letter).
So, just like you can alias any other object to multiple variables:
a = ''
b = a
a << 'Hello'
c = b
b << ', World!'
puts c # => Hello, World!
You can also alias classes to multiple variables:
class Foo; end
bar = Foo
p bar.new # => #<Foo:0x1d9f220>
If you want to move the classes into the namespace instead of just aliasing them, you also need to set the original variables to some other object like nil, in addition to #Squeegy's answer:
::A = nil
::B = nil
If you do want to put them in a module I don't see the point of first including them in the global namespace and then aliasing them inside the module.
I think what you want to do (although I doubt it is a good thing to do) is something like this:
file classes/g1.rb
class A1
def self.a
"a1"
end
end
class B1
def self.b
"b1"
end
end
file classes/g2.rb
class A2
def self.a
"a2"
end
end
class B2
def self.b
"b2"
end
end
file imp.rb
module MyModule
["g1.rb", "g2.rb"].each do |file|
self.class_eval open("classes/#{file}"){ |f| f.read }
end
end
puts defined? MyModule
puts defined? A1
puts defined? MyModule::A1
puts MyModule::A1.a
puts MyModule::B2.b
outputs
constant
nil
constant
a1
b2
I can think of a few disadvantages of this approach (harder to debug for one thing, and probably a bit slower to load although I am only guessing).
Why don't you just do something like this:
Dir["classes/*.rb"].each do |file|
contents = open(file) { |f| f.read }
open(file, "w") do |f|
f.puts "module MyModule\n"
contents.each { |line| f.write " #{line}" }
f.puts "\nend"
end
end
That'll fix your classes to be in your module since in ruby you can reopen a module at any time. Then just include them like you do normally.
Yes, in your module create the class and have it inherit from your outside classes.
For example,
class A
...
end
module MyModule
class NewA < A
end
end
The class MyModule::NewA will have all the attributes and methods of class A.
Then again, modules in ruby are never locked, so there is nothing stopping you just writing the class definition straight into the module.