Christian
Christian

Reputation: 2200

The scope of Require

I am having trouble accessing a module I declared in another file, and I am wondering if I have this right conceptually. Let me explain.

If I have file a, b and c setup like so

File a

module HelloWorld
  def greet
    "hello world"
  end
end

File b

require "a"

File c

require "b"
include HelloWorld

puts greet

So it's a -> b -> c, will this work?

Now what if I do it this way

File a

module HelloWorld
  def greet
    "hello world"
  end
end

File b

include HelloWorld

puts greet

File c

require "a"
require "b"

Would that change anything? It seems to me, gems - once required - are accessible no matter what file, but I am having trouble accessing modules in the second scenario. Any help is appreciated.

UPDATE: I tested both scenarios, and they both work, which leads me to believe this is not a conceptual problem, but a bug in my code. I am going to work on debugging the project.

Upvotes: 0

Views: 983

Answers (1)

sonna
sonna

Reputation: 620

When I try to run your code I get the following error message

$ ruby c.rb
~/.rbenv/versions/2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- a (LoadError)
  from /Users/Sonna/.rbenv/versions/2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
  from c.rb:2:in `<main>'

It is simply saying that it uses the Kernel's require method it cannot find the a.rb file and then raises and LoadError exception.

In order to require the file you can use the Kernal's require_relative method

require_relative "a"
require_relative "b"

and it will a those a & b files relative to the c file.

Or you can add the following lines of code to your c.rb file, which is a common convention used in Ruby Gems to load their custom scripts/libraries

current_directory = File.expand_path("../", __FILE__)
$LOAD_PATH.unshift(current_directory) unless $LOAD_PATH.include?(current_directory)

This will add the current directory ../ from the current file __FILE__, expand it to be an aboslute path of said current directory and add it to the existing Load Path global variable; e.g.

puts $LOAD_PATH
# => ["~/Projects/ruby/stackoverflow_questions/the_scope_of_require",
#     "/usr/local/Cellar/rbenv/1.0.0/rbenv.d/exec/gem-rehash",
#     "~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib",
#     "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0",
#     "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0/x86_64-darwin15",
#     "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby",
#     "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0",
#     "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin15",
#     "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby",
#     "~/.rbenv/versions/2.3.1/lib/ruby/2.3.0",
#     "~/.rbenv/versions/2.3.1/lib/ruby/2.3.0/x86_64-darwin15"]

Which require internally uses to find by filename, if it is not given an absolute path

If the filename does not resolve to an absolute path, it will be searched for in the directories listed in $LOAD_PATH ($:).

-- Kernel's require method

So when I run you code again I see the following

$ ruby c.rb
hello world

It should be noted that

A file will not be loaded again if its path already appears in $". For example, require 'a'; require './a' will not load a.rb again.

-- Module: Kernel (Ruby 2_4_0)

So as long as one of your require methods occur once before one of the methods in that file are called, it should work; so both of your examples will work (as long as either the directory the sit is added to the $LOAD_PATH or you use require_relative instead)

Upvotes: 1

Related Questions