Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple classes of the same name in Ruby

I have an existing codebase of unit tests where the same classes are defined for each test, and a program that iterates over them. Something like this:

test_files.each do |tf|   
  load "tests/#{tf}/"+tf
  test=     ::Kernel.const_get("my_class")
  Test::Unit::UI::Console::TestRunner.run( test )

While working on these tests, I've realized that ruby allows you to require classes with the same name from different files, and it merges the methods of the two. This leads to problems as soon as the class hierarchy is not the same: superclass mismatch for class ...

Unfortunately, simply unloading the class is not enough, as there are many other classes in the hierarchy that remain loaded.

Is there a way to execute each test in a different global namespace?

While I figure that using modules would be the way to go, I'm not thrilled with idea of changing the hundreds of existing files by hand.

--EDIT--

Following Cary Swoveland's suggestion, I've moved the test running code to a separate .rb file and am running it using backticks. While this seems to work well, loading the ruby interpreter and requireing all the libraries again has a considerable overhead. Also, cross-platform compatibility requires some extra work.

like image 410
loopbackbee Avatar asked May 08 '26 15:05

loopbackbee


1 Answers

first I wanted to propose the following solution:

#main.rb
modules = []

Dir.foreach('include') do |file|
  if file =~ /\w/
    new_module = Module.new

    load "include/#{file}"

    new_module.const_set("StandardClass", StandardClass)

    Object.send(:remove_const, :StandardClass)

    modules << new_module
  end

end

modules.each do |my_module|
  my_module::StandardClass.standard_method
end

#include/first.rb
class StandardClass
  def self.standard_method
    puts "first method"
  end
end

#include/second.rb
class StandardClass
  def self.standard_method
    puts "second method"
  end
end

this wraps each class with it's own module and calls the class inside it's module:

$ruby main.rb
second method
first method

But as you answered there are references to other classes so modules can break them. I checked this by calling the third class from one of the wrapped classes and got:

include/second.rb:5:in `standard_method': uninitialized constant StandardClass::Independent (NameError)

so probably using backticks is better solution but I hope my answer will be helpful for some other similar situations.

like image 166
Nickolay Kondratenko Avatar answered May 11 '26 16:05

Nickolay Kondratenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!