Why Relative Path Doesn't Work in Ruby Require

Why doesn't Ruby 'require' allow relative paths?

tl;dr: IRB is special and has some odd rules. Ruby in general works just fine with relative paths.

require will search the load path (which you can see by inspecting $: or $LOAD_PATH). This will not include the directory that you launched IRB from:

> $:
=> ["/usr/local/rvm/rubies/jruby-head/lib/ruby/2.2/site_ruby", "/usr/local/rvm/rubies/jruby-head/lib/ruby/stdlib"]

So there's no joy there, unless you explicitly add your directory to the load path. This is what Rubygems and Bundler spends most of their time doing - they manage the load paths for gems so you don't have to worry about it. However, this doesn't help you with single files.

Additionally, require_relative will search from the directory that __FILE__ is in, but in IRB, this is a non-directory (irb) value! This is why you get the "can't infer basepath" issue when trying require_relative from IRB; since the currently executing file, __FILE__, isn't a proper path, require_relative can't figure out where to start from.

When you are not running from IRB, this isn't really an issue; require_relative 'mytest.so' should work just fine when you execute it in a script, since the currently-executing script will populate __FILE__. That is, if you have loader.rb and mytest.so and execute loader via ruby loader.rb, require_relative should work just fine.

If you want to run this in IRB, consider something like:

require "#{__dir__}/mytest.so"

which will expand out to the current working directory, which should by default be the directory you've launched it from. I would recommend that you not do this in a script, though, since it depends on __dir__ not having been changed, and that may be difficult to guarantee.

Why relative path doesn't work in Ruby require

Relative path is based on working dir. I assume that there is main file on the same directory. If you run ruby ./shapes/main.rb on project root, ruby try to find {project_root}/shape.rb, not {project_root}/shapes/shape.rb. It doesn't work.

You need to use require_relative like below.

# {project_root}/shapes/main.rb
require_relative './shape'
require_relative './rectangle'
require_relative './square'

Requiring with relative path

require doesn't do relative path expanding. You have to expand the path before passing to it.

require File.expand_path("../const", __dir__)

Relative path to your project directory

You can get current directory (directory of current file) with this

File.dirname(__FILE__)

You can then join it with relative path to the root

File.join(File.dirname(__FILE__), '../../') # add proper number of ..

Or you can use expand_path to convert relative path to absolute.

ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', File.dirname(__FILE__))

Or you can calculate relative path between two dirs.

require 'pathname'; 
puts Pathname.new('/').relative_path_from(Pathname.new('/some/child/dir/')).to_s
# => ../../..

Ruby Struggling including/requiring relative modules

require and require_relative load the code in the specified file and execute it. In order for Greeter to exist in Person.rb you need to require the file in which Greeter is defined. requires should usually go at the top of the file, not inside a class or method.

include takes the methods in the given module and adds them to the class in which you called include. But you can't include Greeter if you haven't required the file in which it's defined.

In other words, you need both require (or require_relative) and include:

#!/usr/bin/ruby
require_relative "./Greeter.rb"

class Person
include Greeter
end

alice = Person.new
alice.greet

In Ruby, classes and modules are objects like any other. You can't tell Ruby to do something with an object if the code that defines the object hasn't been run. require is what tells Ruby to load and run the code that defines Greeter, and include, in this case, is what tells Ruby to use that object.

Here's another way to think about it. Suppose you have these two files:

constants.rb

Message = "Hello, world!"

greet.rb

#!/usr/bin/env ruby
puts Message

You probably wouldn't be surprised that greet.rb raises a NameError that says uninitialized constant Message.. We defined Message in another file that greet.rb doesn't know about. We have to tell Ruby to load it first:

greet2.rb

#!/usr/bin/env ruby
require_relative "./constants.rb"
puts Message

Running greet2.rb will now print the message "Hello, world!"

The only difference between your Greeter and my Message is that Greeter is a module and Message is a string.

What is the difference between require_relative and require in Ruby?

Just look at the docs:

require_relative complements the builtin method require by allowing you to load a file that is relative to the file containing the require_relative statement.

For example, if you have unit test classes in the "test" directory, and data for them under the test "test/data" directory, then you might use a line like this in a test case:

require_relative "data/customer_data_1"

Ruby require 'file' doesn't work but require './file' does. Why?

If you want to require a file not from the system $LOAD_PATH but rather relative to the directory of the file you are requireing from, you should use require_relative. (Which, as you can see, isn't exactly extensively documented.)



Related Topics



Leave a reply



Submit