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. require
s 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 require
d 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 methodrequire
by allowing you to load a file that is relative to the file containing therequire_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 require
ing from, you should use require_relative
. (Which, as you can see, isn't exactly extensively documented.)
Related Topics
How to Find Where a Ruby Method Is Declared
Rails Can't Login to Postgresql - Pg::Error - Password - Correct Info
How to Authorize a Google Service Account Without the Default Credentials File
Find Memory Leak in a Ruby on Rails Project
Ruby: How to "Require" a File from the Current Working Dir
How to Get the Version from a Gemspec File
Active Admin Scopes for Each Instance of a Related Model
Can't Convert Symbol into String
Running Multiple Background Parallel Jobs with Rails
Do You Check in Your Rvmrc File
Appending to Rake Db:Seed in Rails and Running It Without Duplicating Data
How Does Pack() and Unpack() Work in Ruby
Get Title, Content via Link in Rails
CSV - Unquoted Fields Do Not Allow \R or \N (Line 2)
Why I Can Not Call Super in Define_Method with Overloading Method
Why Do I Get "No Implicit Conversion of String into Integer (Typeerror)"