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:
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.
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
.
$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
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 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.
Another option is to tell the interpreter which paths to search
ruby -I /path/to/my/project caller.rb