Ruby: require vs require_relative - best practice to workaround running in both Ruby <1.9.2 and >=1.9.2
Asked Answered
D

11

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.

Disinterested answered 2/12, 2010 at 9:8 Comment(3)
Hi I'm new. Could someone explain from the start—what's the difference between require and require_relative?Erick
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).Disinterested
You can now use backports just for require_relative, see my answer...Acrospire
F
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.

Forejudge answered 17/1, 2011 at 22:4 Comment(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?Delinda
The require_relative function is included in an extension project to the Ruby core libraries, found here: 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)Downtrodden
@Delinda 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.Forejudge
P
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.

Pratt answered 2/12, 2010 at 9:44 Comment(7)
I see this commonly as well. It's ugly, but it seems to work well.Inductive
a bit cleaner: require File.expand_path('relative/path', File.dirname(FILE))Linnet
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.Pratt
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.Indoaryan
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.Pratt
Thank You. Most concise answer I have seen on the weird ..Blizzard
@Pratt 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.Opportina
T
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.

Traditional answered 2/12, 2010 at 18:4 Comment(3)
How to check if this snippet should be activated or not properly? Using $RUBY_VERSION or by checking if require_relative exists directly?Disinterested
Always duck type, check if require_relative is defined.Pratt
@Pratt @Disinterested 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.Traditional
S
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
Subtorrid answered 25/10, 2011 at 14:15 Comment(0)
R
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.

Reverberatory answered 4/10, 2012 at 18:30 Comment(0)
L
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.

Longley answered 8/3, 2013 at 19:25 Comment(0)
S
3

Another option is to tell the interpreter which paths to search

ruby -I /path/to/my/project caller.rb
Summit answered 30/3, 2013 at 2:59 Comment(0)
B
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")
Beano answered 15/8, 2013 at 18:5 Comment(0)
V
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.

Vega answered 1/7, 2011 at 14:57 Comment(1)
Re: "I would love to see some solid Ruby best practices literature." You can download Gregory Brown's Ruby Best Practices. You can also check out the Rails Best Practices site.Seism
A
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.

Announcement answered 2/12, 2010 at 14:56 Comment(0)
S
0

Ruby on Rails way:

config_path = File.expand_path("../config.yml", __FILE__)
Sapowith answered 23/2, 2017 at 17:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.