Reputation: 17072
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
Upvotes: 6
Views: 5527
Reputation: 3592
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.
Upvotes: 2
Reputation: 369428
@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
Upvotes: 3
Reputation: 29895
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
Upvotes: 4
Reputation: 186994
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
.
Upvotes: 11
Reputation: 1425
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.
Upvotes: 1