Luc
Luc

Reputation: 17072

Add existing classes into a module

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

Answers (5)

einarmagnus
einarmagnus

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

Jörg W Mittag
Jörg W Mittag

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

horseyguy
horseyguy

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

Alex Wayne
Alex Wayne

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

bennybdbc
bennybdbc

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

Related Questions