154

What is the best practice if I want to require a relative file in Ruby and I want it to work in both 1.8.x and >=1.9.2?

I see a few options:

  • just do $LOAD_PATH << '.' and forget everything
  • do $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • check if RUBY_VERSION < 1.9.2, then define require_relative as require, use require_relative everywhere where it's needed afterwards
  • check if require_relative already exists, if it does, try to proceed as in previous case
  • use weird constructions such as
    require File.join(File.dirname(__FILE__), 'path/to/file')
    - alas they don't seem to work in Ruby 1.9 throughly, because, for example:
    $ cat caller.rb
    require File.join(File.dirname(__FILE__), 'path/to/file')
    $ cat path/to/file.rb
    puts 'Some testing'
    $ ruby caller
    Some testing
    $ pwd
    /tmp
    $ ruby /tmp/caller
    Some testing
    $ ruby tmp/caller
    tmp/caller.rb:1:in 'require': no such file to load -- tmp/path/to/file (LoadError)
        from tmp/caller.rb:1:in '<main>'
  • Even weirder construction:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')
    seems to work, but it's weird and not quite good looking.
  • Use backports gem - it's kind of heavy, it requires rubygems infrastructure and includes tons of other workarounds, while I just want require to work with relative files.

There's a closely related question at StackOverflow that gives some more examples, but it doesn't give a clear answer - which is a best practice.

Is there are any decent, accepted-by-everyone universal solution to make my application run on both Ruby <1.9.2 and >=1.9.2?

UPDATE

Clarification: I don't want just answers like "you can do X" - in fact, I've already mentioned most of choices in question. I want rationale, i.e. why it is a best practice, what are its pros and cons and why it should be chosen among the others.

Community
  • 1
  • 1
GreyCat
  • 16,622
  • 18
  • 74
  • 112
  • 3
    Hi I'm new. Could someone explain from the start—what's the difference between `require` and `require_relative`? – Colonel Panic Oct 08 '12 at 17:18
  • 3
    In older Ruby 1.8 if you ran file `a.rb` and wanted to make interpreter read and parse contents of file `b.rb` in current directory (usually the same dir as with `a.rb`), you'd just write `require 'b'` and it would be fine as default search path included current directory. In more modern Ruby 1.9, you'll have to write `require_relative 'b'` in this case, as `require 'b'` would only search in standard library paths. That's the thing that kind of breaks forward and backward compatibility for simpler scripts that aren't going to be installed properly (for example, *install* scripts themselves). – GreyCat Oct 09 '12 at 07:57
  • You can now use `backports` just for `require_relative`, see my answer... – Marc-André Lafortune Mar 08 '13 at 19:26

11 Answers11

64

A workaround for this was just added to the 'aws' gem so thought I'd share as it was inspired by this post.

https://github.com/appoxy/aws/blob/master/lib/awsbase/require_relative.rb

unless Kernel.respond_to?(:require_relative)
  module Kernel
    def require_relative(path)
      require File.join(File.dirname(caller[0]), path.to_str)
    end
  end
end

This allows you to use require_relative as you would in ruby 1.9.2 in ruby 1.8 and 1.9.1.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Travis Reeder
  • 38,611
  • 12
  • 87
  • 87
  • 3
    How do you require the require_relative.rb file? You have to require require_relative.rb and then require_relative the rest of the requires. Or am I missing something? – ethicalhack3r Mar 23 '12 at 22:47
  • 7
    The `require_relative` function is included in an extension project to the Ruby core libraries, found here: http://www.rubyforge.org/projects/extensions You should be able to install them with `gem install extensions`. Then in your code add the following line before the `require_relative`: require 'extensions/all' (sourced from Aurril's post [here](http://stackoverflow.com/questions/2681031/why-doesnt-relative-require-work-on-ruby-1-8-6)) – thegreendroid Apr 17 '12 at 05:24
  • @ethicalhack3r just copy and paste that code at the top of your ruby script or if in rails, throw it in at the top environment.rb or something. – Travis Reeder Oct 16 '12 at 22:31
46

Before I made the jump to 1.9.2 I used the following for relative requires:

require File.expand_path('../relative/path', __FILE__)

It's a bit weird the first time you see it, because it looks like there's an extra '..' at the start. The reason is that expand_path will expand a path relative to the second argument, and the second argument will be interpreted as if it were a directory. __FILE__ obviously isn't a directory, but that doesn't matter since expand_path doesn't care if the files exist or not, it will just apply some rules to expand things like .., . and ~. If you can get over the initial "waitaminute isn't there an extra .. there?" I think that the line above works quite well.

Assuming that __FILE__ is /absolute/path/to/file.rb, what happens is that expand_path will construct the string /absolute/path/to/file.rb/../relative/path, and then apply a rule that says that .. should remove the path component before it (file.rb in this case), returning /absolute/path/to/relative/path.

Is this best practice? Depends on what you mean by that, but it seems like it's all over the Rails code base, so I'd say it's at least a common enough idiom.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Theo
  • 131,503
  • 21
  • 160
  • 205
  • 1
    I see this commonly as well. It's ugly, but it seems to work well. – yfeldblum Dec 02 '10 at 12:47
  • 12
    a bit cleaner: require File.expand_path('relative/path', File.dirname(__FILE__)) – Yannick Wurm May 03 '11 at 07:07
  • 1
    I don't think it's much cleaner, it's just longer. They are both fugly as hell, and when choosing between two bad options I prefer the one that requires less typing. – Theo May 03 '11 at 20:14
  • 6
    It seems that File.expand_path('../relpath.x', File.dirname(__FILE__)) is a better idiom, even though it is more verbose. Relying on the arguably broken functionality of a file path being interpreted as a directory path with an extra nonexistent directory might break when/if that functionality is fixed. – jpgeek Oct 18 '12 at 03:07
  • 1
    Broken, perhaps, but it's been that way forever in UNIX. There's just no difference between a directory and a file when it comes to paths and the resolution of '..' -- so I'm not loosing any sleep over it. – Theo Oct 18 '12 at 13:14
  • Thank You. Most concise answer I have seen on the weird `..` – fontno Jun 27 '13 at 05:15
  • 1
    @Theo Can you give an example of that? It's clearly not true in the most obvious cases, e.g. `cd path/to/a/file/../..` fails. – Adam Spiers Mar 17 '14 at 10:11
6

The Pickaxe has a snippet for this for 1.8. Here it is:

def require_relative(relative_feature)
  c = caller.first
  fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
  file = $`
  if /\A\((.*)\)/ =~ file # eval, etc.
    raise LoadError, "require_relative is called in #{$1}"
  end
  absolute = File.expand_path(relative_feature, File.dirname(file))
  require absolute
end

It basically just uses what Theo answered, but so you can still use require_relative.

Paul Hoffer
  • 12,606
  • 6
  • 28
  • 37
  • How to check if this snippet should be activated or not properly? Using `$RUBY_VERSION` or by checking if `require_relative` exists directly? – GreyCat Dec 02 '10 at 19:56
  • 1
    Always duck type, check if `require_relative` is defined. – Theo Dec 12 '10 at 12:39
  • @Theo @GreyCat yes, I would check if it's needed. I was just putting the snippet here for people to show. Personally, I use would Greg's answer anyways, I was really just posting this because someone had mentioned it without having it themselves. – Paul Hoffer Dec 12 '10 at 21:20
6
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

It's not a good security habit: why should you expose your whole directory?

require './path/to/file'

This doesn't work if RUBY_VERSION < 1.9.2

use weird constructions such as

require File.join(File.dirname(__FILE__), 'path/to/file')

Even weirder construction:

require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')

Use backports gem - it's kind of heavy, it requires rubygems infrastructure and includes tons of other workarounds, while I just want require to work with relative files.

You have already answered why these are not the best options.

check if RUBY_VERSION < 1.9.2, then define require_relative as require, use require_relative everywhere where it's needed afterwards

check if require_relative already exists, if it does, try to proceed as in previous case

This may work, but there's safer and quicker way: to deal with the LoadError exception:

begin
  # require statements for 1.9.2 and above, such as:
  require "./path/to/file"
  # or
  require_local "path/to/file"
rescue LoadError
  # require statements other versions:
  require "path/to/file"
end
Claudio Floreani
  • 2,441
  • 28
  • 34
5

I'm a fan of using the rbx-require-relative gem (source). It was originally written for Rubinius, but it also supports MRI 1.8.7 and does nothing in 1.9.2. Requiring a gem is simple, and I don't have to throw code snippets into my project.

Add it to your Gemfile:

gem "rbx-require-relative"

Then require 'require_relative' before you require_relative.

For example, one of my test files looks like this:

require 'rubygems'
require 'bundler/setup'
require 'minitest/autorun'
require 'require_relative'
require_relative '../lib/foo'

This is the cleanest solution out of any of these IMO, and the gem isn't as heavy as backports.

Edward Anderson
  • 13,591
  • 4
  • 52
  • 48
4

The backports gem now allows individual loading of backports.

You could then simply:

require 'backports/1.9.1/kernel/require_relative'
# => Now require_relative works for all versions of Ruby

This require will not affect newer versions, nor will it update any other builtin methods.

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
3

Another option is to tell the interpreter which paths to search

ruby -I /path/to/my/project caller.rb
eradman
  • 1,996
  • 1
  • 17
  • 23
3

One issue I've not seen pointed out with the solutions based on __FILE__ is that they break with regards to symlinks. For example say I have:

~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb

The main script, the entry point, the application is foo.rb. This file is linked to ~/Scripts/foo which is in my $PATH. This require statement is broken when I execute 'foo':

require File.join(File.dirname(__FILE__), "lib/someinclude")

Because __FILE__ is ~/Scripts/foo so the require statement above looks for ~/Scripts/foo/lib/someinclude.rb which obviously doesn't exist. The solution is simple. If __FILE__ is a symbolic link it needs to be dereferenced. Pathname#realpath will help us with this situation:

require "pathname"
require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
jptros
  • 111
  • 2
  • 7
2

If you were building a gem, you would not want to pollute the load path.

But, In the case of a standalone application it is very convenient to just add the current directory to the load path as you do in the first 2 examples.

My vote goes to the first option on the list.

I would love to see some solid Ruby best practices literature.

Casey Watson
  • 51,574
  • 10
  • 32
  • 30
  • 1
    Re: "I would love to see some solid Ruby best practices literature." You can download Gregory Brown's [_Ruby Best Practices_](http://blog.rubybestpractices.com/posts/gregory/022-rbp-now-open.html). You can also check out the [Rails Best Practices site](http://rails-bestpractices.com/). – Michael Stalker Oct 05 '12 at 20:29
1

I would define my own relative_require if it doesn't exist (i.e. under 1.8) and then use the same syntax everywhere.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
0

Ruby on Rails way:

config_path = File.expand_path("../config.yml", __FILE__)
Vaibhav
  • 858
  • 10
  • 13