1

I am using Ruby 2.0 and I have two files: hello.rb & assets/display.rb.

hello.rb:

class Hello
  def self.run_it(name)
    ui = Display.new(name)
    ui.say_hi
  end
end

require_relative "assets/display"

Hello.run_it("Someone")

assets/display.rb:

class Hello::Display
  def initialize(name = "World")
    @name = name
  end

  def say_hi  
    puts "Hello #{@name}"  
  end
end

If in hello.rb I move require_relative "assets/display" before class Hello (1st line), ruby hello.rb outputs an uninitialized constant error. Why is that? What is the best practice when requiring external files and is require_relative the correct method (vs require and require "./some_file") in this short example ?

ilovebigmacs
  • 983
  • 16
  • 28

3 Answers3

3

The standard practice is to put all or most of the require statements at the top of the file. Files should be designed so there is as little dependency as possible to other files.

The problem you have is that you have designed the file display.rb to be dependent on the class Hello.

When you say this:

class Hello::Display
end

It is the same as:

class Hello
  class Display
  end
end

But the difference is that in the first case Hello needs to be defined before you can say Hello::Display. Since Hello has not been defined when you put the require at the top of the file you will get the error.

You can fix it like this:

class Hello
   class Display
     # ..your Display code here..
   end
end

Or like this:

# Predefine Hello as a class name
class Hello
end

class Hello::Display
  # ..your Display code here..
end
Casper
  • 33,403
  • 4
  • 84
  • 79
  • Thank you for this detailed explanation. It makes sense now. Marked as accepted answer. Can you also confirm that `require_relative`should be used instead of `require`if the goal is to package the code as a gem at a later stage? – ilovebigmacs Jan 16 '14 at 14:05
  • 1
    @NicoS. Yes `require_relative` is perfect for gem code. However notice that it's not strictly required as your `lib` dir will get added to the load path by rubygems. However I think it might still be a better choice as it reduces the chances of conflicts. You can read more about code loading with rubygems here: http://guides.rubygems.org/patterns/#loading_code and here you can read a bit more about `require_relative`: http://stackoverflow.com/questions/3672586/what-is-require-relative-in-ruby – Casper Jan 16 '14 at 14:20
  • Thank you again. Actually, I want my code to work *before* I package it as a gem (so, not in Load Path just yet) and not have to change it when packaging it (it would seem like a terrible pattern!). So, require_relative fits the bill :) – ilovebigmacs Jan 16 '14 at 14:23
  • 1
    Sure. In most cases (before require_relative was introduced) when testing gem code developers will have to manually modify the load path to simulate rubygems. But like you say it will be much easier when using relative everywhere. That makes the code automatically more portable. – Casper Jan 16 '14 at 14:25
1

If you include the file display.rb at the beginning of hello.rb, when ruby interpreter encounter class Hello::Display, it expects Hello to defined somewhere before. But at this time, class Hello has not been defined, so you see the error.

vidang
  • 1,761
  • 13
  • 13
1

The reason of this error is that you are trying to define a class inside Hello class, which is not defined yet at this point. Simply split the name into two classes:

class Hello
  class Display
    def initialize(name = "World")
      @name = name
    end

    def say_hi  
      puts "Hello #{@name}"  
    end
  end
end

That way you define both classes at once (and you can later on open Hello class)

BroiSatse
  • 44,031
  • 8
  • 61
  • 86