views:

3499

answers:

5

What is the best way to manage the require paths in a ruby program?

Let me give a basic example, consider a structure like:

\MyProgram

\MyProgram\src\myclass.rb

\MyProgram\test\mytest.rb

If in my test i use require '../src/myclass' then I can only call the test from \MyProgram\test folder, but I want to be able to call it from any path!

The solution I came up with is to define in all source files the following line:

ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(ROOT) and then always use require "#{ROOT}/src/myclass"

Is there a better way to do it?

+5  A: 

Here is a slightly modified way to do it:

$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "src"))

By prepending the path to your source to $LOAD_PATH (aka $:) you don't have to supply the root etc. explicitly when you require your code i.e. require 'myclass'

sris
+1  A: 

sris's answer is the standard approach.

Another way would be to package your code as a gem. Then rubygems will take care of making sure your library files are in your path.

rampion
+3  A: 

The same, less noisy IMHO:

$:.unshift File.expand_path("../../src", __FILE__)
require 'myclass'

or just

require File.expand_path "../../src/myclass", __FILE__

Tested with ruby 1.8.7 and 1.9.0 on (Debian) Linux - please tell me if it works on Windows, too.

Why a simpler method (eg. 'use', 'require_relative', or sg like this) isn't built into the standard lib? This is beyond my comprehension.

inger
While it makes my spine tingle, this is the best option I've seen for doing this _inside_ of ruby so far.
Matt
That is to say, I prefer appending (not prepending) the path to the ruby path, as you don't inadvertently stomp over something that exists in the standard ruby libraries.
Matt
Didn't you mean 'I prefer prepending (not appending)' instead? (in other words: don't you actually want to make sure your stuff to take precedence?) My original example was '$:<< newpath' which was appending, now fixed it by 'unshift', as the previous reply did. Thanks
inger
Matt, 'best option I've seen for doing this inside of ruby so far' -- are there some options outside of ruby? Like preprocessor or something?
inger
@inger, options for doing it outside ruby are shell variables and ruby command line arguments.
j-g-faustus
A: 

This is what I ended up with - a Ruby version of a setenv shell script:

  # Read application config                                                       
$hConf, $fConf = {}, File.expand_path("../config.rb", __FILE__)
$hConf = File.open($fConf) {|f| eval(f.read)} if File.exist? $fConf

  # Application classpath                                                         
$: << ($hConf[:appRoot] || File.expand_path("../bin/app", __FILE__))

  # Ruby libs                                                                     
$lib = ($hConf[:rubyLib] || File.expand_path("../bin/lib", __FILE__))
($: << [$lib]).flatten! # lib is string or array, standardize

Then I just need to make sure that this script is called once before anything else, and don't need to touch the individual source files.

I put some options inside a config file, like the location of external (non-gem) libraries:

# Site- and server specific config - location of DB, tmp files etc.
{
  :webRoot => "/srv/www/myapp/data",
  :rubyLib => "/somewhere/lib",
  :tmpDir => "/tmp/myapp"
}

This has been working well for me, and I can reuse the setenv script in multiple projects just by changing the parameters in the config file. A much better alternative than shell scripts, IMO.

j-g-faustus
@j-g-faustus: "Merging __ FILE __ with the working directory ensures that I can call it from wherever":File.expand_path(path, base) should have the same effect.Are you sure inger's answer above didn't do the job?What does this give you? (put it in test.rb, for example):"puts File.expand_path "../../src/myclass", __ FILE __ "
inger
You're right, File.expand_path does the same. I've updated the post. It does become a bit cleaner, thanks :)Apart from that, the primary difference is that this is a separate script run once at application startup, and I don't need to touch the individual source files.
j-g-faustus
+4  A: 

As of Ruby 1.9 you can use require_relative to do this:

require_relative '../src/myclass'

If you need this for earlier versions you can get it from the extensions gem as per this SO comment.

David Tchepak