Making "require" just work with some dynamic load path manipulation

Discussion in 'Ruby' started by Greg Hurrell, Mar 7, 2007.

  1. Greg Hurrell

    Greg Hurrell Guest

    The problem:

    If I have a simple file, "a.rb":

    #!/usr/bin/env ruby
    require 'b'

    And it's companion, "b.rb":

    puts "Hello, world!"

    Running "a.rb" works fine if I start from the directory containing
    those files. If I change to another directory, however, I get a
    LoadError because Ruby doesn't know how to find "b.rb".

    Workarounds:

    1. Set or modify the RUBYLIB environment variable so that Ruby knows
    which additional locations to search.
    2. Manipulate the load path ($:) from within the program itself.

    These workarounds are fine for me because I understand the quirks of
    Ruby's load path. But I am looking for a way to make this stuff just
    work automatically for other users who don't necessarily understand
    Ruby's load path and will expect "a" to be able to find "b" because
    they're in the same directory, no matter what the current working
    directory is at the time.

    The solution:

    So I already have a solution for this based on workaround "2" and I
    wanted to get some feedback on whether this is a good idea or not. The
    basic idea is to make a "custom_require" method which does the
    following:

    1. Try requiring the file normally.
    2. On a LoadError, try adding the directory containing the file to the
    load path (only if it's not already present).
    3. Now try requiring it again.
    4. If you get a second LoadError then put the load path back the way
    it was and re-raise the original error.
    5. If you succeed then put the load path back the way it was anyway.

    So here's the basic code to do this. I try to make it as conservative
    as possible by making the modifications to the load path temporary,
    and always adding absolute paths so as to minimize the likelihood of
    the same file being evaluated twice. The security of dynamically
    modifying the load path is not really a concern because the users
    running this scripts already have full write access to them anyway and
    can therefore already do whatever they want, and they (and nobody
    else) have full control over the directories in which the scripts are
    stored.

    To use the new method you would do:

    custom_require 'b', __FILE__

    The second parameter is necessary (as far as I can tell) because I
    know of no other clean way for the "custom_require" method (itself in
    a different file) to know which file was being evaluated when the call
    to "custom_require" was made.

    What I'd like to know is, is there a better way?

    def custom_require(file, fallback)
    begin
    require file
    rescue LoadError => e
    candidate = Pathname.new(fallback).dirname.realpath
    if $:.any? { |path| Pathname.new(path).realpath == candidate
    rescue false }
    raise e
    else
    begin
    $:.push(candidate)
    status = require file
    rescue LoadError
    raise e
    ensure
    $:.pop
    status
    end
    end
    end
    end
    Greg Hurrell, Mar 7, 2007
    #1
    1. Advertising

  2. Greg Hurrell

    Trans Guest

    Is there a reason you are not using setup.rb and/or gems?

    T.
    Trans, Mar 7, 2007
    #2
    1. Advertising

  3. Greg Hurrell

    Greg Hurrell Guest

    On 7 mar, 22:01, "Trans" <> wrote:
    > Is there a reason you are not using setup.rb and/or gems?
    >
    > T.


    Neither of those is really appropriate because the files "a.rb" and
    "b.rb" are dynamically generated and probably not very long lived (the
    typical cycle will be: generate, use, throw away) and definitely not
    worth installing permanently. The thing that generates those files is
    actually distributed as a gem so there are no problems at that level;
    I just want the generated files to be as easy to use for end users as
    possible.

    Cheers,
    Greg
    Greg Hurrell, Mar 8, 2007
    #3
  4. On Mar 7, 2007, at 6:00 PM, Greg Hurrell wrote:

    > On 7 mar, 22:01, "Trans" <> wrote:
    >> Is there a reason you are not using setup.rb and/or gems?
    >>
    >> T.

    >
    > Neither of those is really appropriate because the files "a.rb" and
    > "b.rb" are dynamically generated and probably not very long lived (the
    > typical cycle will be: generate, use, throw away) and definitely not
    > worth installing permanently. The thing that generates those files is
    > actually distributed as a gem so there are no problems at that level;
    > I just want the generated files to be as easy to use for end users as
    > possible.
    >
    > Cheers,
    > Greg



    IF you want to garauntee to require b in the same dir as a you can
    do this:

    # a.rb
    puts "in a.rb"
    require File.join(FIle.dirname(__FILE__), 'b')

    # b.rb
    puts "in b.rb"

    Cheers-

    -- Ezra Zygmuntowicz
    -- Lead Rails Evangelist
    --
    -- Engine Yard, Serious Rails Hosting
    -- (866) 518-YARD (9273)
    Ezra Zygmuntowicz, Mar 8, 2007
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Paul
    Replies:
    4
    Views:
    302
    Cowboy \(Gregory A. Beamer\)
    Mar 28, 2008
  2. cap
    Replies:
    2
    Views:
    67
    Joel VanderWerf
    Mar 4, 2006
  3. Junkone
    Replies:
    5
    Views:
    111
    MonkeeSage
    Dec 15, 2007
  4. Remi Gillig

    Wrong load path and/or require

    Remi Gillig, Jun 17, 2008, in forum: Ruby
    Replies:
    2
    Views:
    95
    Remi Gillig
    Jun 23, 2008
  5. RJ
    Replies:
    5
    Views:
    118
    Peter J. Holzer
    Feb 19, 2007
Loading...

Share This Page