How to reliably setup require path

I’m writing my first Ruby command line application (beyond a simple
script) and I’m little unsure about how to structure the application and
the best use of require. I’ve come as far as to have the following file
structure:

myapp/
bin/
myapp
docs/ (RDOC files)
lib/
myapp.rb
myapp/
files and folders here
test/ (Test::Unit tests)

And the application is working, but there are a few things of which I am
unsure:

The bin/myapp file is a symlink to the lib/myapp.rb file which has a
shebang line. This doesn’t really work, because inside lib/myapp.rb I
have:

require ‘myapp/file’
require ‘myapp/module/file’

which doesn’t make sense when running the application from anywhere else
than myapp/lib

I also tried

$MYAPP_ROOT = File.expand_path(File.join(File.dirname(FILE),
‘myapp’))
$LOAD_PATH << File.expand_path($MYAPP_ROOT)

Which still doesn’t work because this also relies on cwd…

How do I work around this? What’s the best practice way of making these
things make sense? Do you use the $RUBYLIB variable to set the full path
to the application? I was hoping to create the application as standalone
as possible, so installation will be easy.

On Apr 14, 8:15 am, Christian J. [email protected] wrote:

myapp.rb
myapp/
  files and folders here

test/ (Test::Unit tests)

I would suggest:

myapp/
bin/
myapp
doc/
rdoc/ (RDOC files)
lib/
myapp.rb
myapp/
files and folders here
test/ (Test::Unit tests)

And the application is working, but there are a few things of which I am
unsure:

The bin/myapp file is a symlink to the lib/myapp.rb file which has a
shebang line. This doesn’t really work, because inside lib/myapp.rb I
have:

Don’t use a symlink. Make something like lib/myapp/command.rb, then in
the bin:

require ‘myapp/command’

MyApp::Command.start # or what have you

How do I work around this? What’s the best practice way of making these
things make sense? Do you use the $RUBYLIB variable to set the full path
to the application? I was hoping to create the application as standalone
as possible, so installation will be easy.

To run your app you will need to first go through an install phase.
There a few ways to do this. The oldest is to add setup.rb to you
project and run that between trying your app out. Another is to
generate a .gem and install that between trial runs (you can automate
this process for convenience). In my case, I use Rolls, but it’s still
a little rough around the edges so I’m not actively recommending it to
anyone yet. But you can contact me off list if you’d like to do so.

On the other hand, if you do TDD you don’t really need go through an
install phase often. Your test runner should add lib/ to the
$LOAD_PATH. So you can run your tests without ever installing your
app.

HTH,
T.

Christian J. wrote:

myapp.rb

$LOAD_PATH << File.expand_path($MYAPP_ROOT)

Which still doesn’t work because this also relies on cwd…

How do I work around this? What’s the best practice way of making these
things make sense? Do you use the $RUBYLIB variable to set the full path
to the application? I was hoping to create the application as standalone
as possible, so installation will be easy.

I’d do a couple of things differently (and I’ve used this pattern in a
number of applications with the same dir structure as yours):

  1. make bin/myapp a very short ruby script instead of a symlink (if you
    want this to work on windows, symlinks won’t work anyway).

  2. use unshift instead of <<, so you get precedence

  3. use an ENV var instead of a global so child scripts can use it

  4. use File.dirname to go up two levels instead of one, so that you can
    use it to find non-lib files as well (images for example, or other data
    files). Note that the expand_path call has to happen before calling
    dirname twice.

Here’s the code:

ENV[‘MYAPP_BASE’] =
File.dirname(File.dirname(File.expand_path(FILE)))
libdir = File.join(ENV[‘MYAPP_BASE’], “lib”)
$LOAD_PATH.unshift libdir
ENV[‘RUBYLIB’] = libdir + “:” + ENV[‘RUBYLIB’]

HTH.

Thanks alot for your answers, both of you. They have cleared things up
for me and was very helpful. I have a couple of follow ups:

By setup.rb do you mean this project:
setup.rb ?

I totally see the benefits of using the command-script instead of the
symlink, but I’m now a bit unsure as to what lib/myapp.rb is supposed to
do? The way I have it now is that lib/myapp.rb is the actual executable.
Maybe this should actually serve some other purpose?

Is Roller a way of making gems? Are there other “tried and true” ways of
creating gems? (I guess I can look this up on my own as well).

I have in fact been doing TDD so, this hasn’t become an issue until now
when I have something runnable :slight_smile:

Again; thank you, much appreciated.

Christian J. wrote:

I totally see the benefits of using the command-script instead of the
symlink, but I’m now a bit unsure as to what lib/myapp.rb is supposed to
do? The way I have it now is that lib/myapp.rb is the actual executable.
Maybe this should actually serve some other purpose?

You could use that for global initialization. For example, if you’re
writing a FXRuby app, you could instantiate and create the FXApp here,
require a “myapp/main-window” file, and so on.

On Apr 14, 2008, at 16:16 PM, Joel VanderWerf wrote:

docs/ (RDOC files)
have:
How do I work around this? What’s the best practice way of making

before calling dirname twice.

Here’s the code:

ENV[‘MYAPP_BASE’] =
File.dirname(File.dirname(File.expand_path(FILE)))
libdir = File.join(ENV[‘MYAPP_BASE’], “lib”)
$LOAD_PATH.unshift libdir
ENV[‘RUBYLIB’] = libdir + “:” + ENV[‘RUBYLIB’]

The simplest way is ruby -Ilib bin/myapp from the root of the
project dir.

(-I directories take precedence over RUBYLIB directories.)

Eric H. wrote:

The simplest way is ruby -Ilib bin/myapp from the root of the project
dir.

(-I directories take precedence over RUBYLIB directories.)

Also, it’s fragile with respect to the cwd. You have to be in myapp/ to
run it, and also the program can’t Dir.chdir without breaking the
reference to the lib dir.

Eric H. wrote:

lib/
require ‘myapp/module/file’
as possible, so installation will be easy.

  1. use an ENV var instead of a global so child scripts can use it
    libdir = File.join(ENV[‘MYAPP_BASE’], “lib”)
    $LOAD_PATH.unshift libdir
    ENV[‘RUBYLIB’] = libdir + “:” + ENV[‘RUBYLIB’]

The simplest way is ruby -Ilib bin/myapp from the root of the project
dir.

(-I directories take precedence over RUBYLIB directories.)

It depends. The -I solution won’t help with:

  • making your app double-clickable (or hash-bang executable from the
    command line)

  • locating other resources, like myapp/icons.

  • stting up the same access (to lib and other resources) for ruby
    processes fired off by system, popen, etc.

Joel VanderWerf wrote:

Eric H. wrote:

The simplest way is ruby -Ilib bin/myapp from the root of the project
dir.

(-I directories take precedence over RUBYLIB directories.)

Also, it’s fragile with respect to the cwd. You have to be in myapp/ to
run it, and also the program can’t Dir.chdir without breaking the
reference to the lib dir.

Exactly, and this was my original problem as well.I think I’ll go with
Eric H.s solution for now. Also, I will put the code behind the
binary in myapp/lib/myapp/command.rb

I have organized my files such that mostly there’s one class per file.
This means modules are spread over several files. Is it a good idea to
create a file named after each module which requires all the files
belonging to the module?

Then I could require all the modules in myapp/lib/myapp.rb so that
require ‘myapp’ from another app will give direct access to all my
classes and modules. Is it good? :slight_smile:

On Apr 16, 2008, at 00:50 AM, Christian J. wrote:

run it, and also the program can’t Dir.chdir without breaking the
reference to the lib dir.

Exactly, and this was my original problem as well.I think I’ll go with
Eric H.s solution for now. Also, I will put the code behind the
binary in myapp/lib/myapp/command.rb

My solution works if ordinarily you distribute myapp as a gem as you
avoid the problems with it that Joel pointed out.

I have organized my files such that mostly there’s one class per file.
This means modules are spread over several files. Is it a good idea to
create a file named after each module which requires all the files
belonging to the module?

If I would use it, I make such a file. Sometimes I don’t need such a
thing, but I always have a top-level one.

Then I could require all the modules in myapp/lib/myapp.rb so that
require ‘myapp’ from another app will give direct access to all my
classes and modules. Is it good? :slight_smile:

This is a very common practice.

Eric H. wrote:

reference to the lib dir.

Exactly, and this was my original problem as well.I think I’ll go with
Eric H.s solution for now. Also, I will put the code behind the
binary in myapp/lib/myapp/command.rb

My solution works if ordinarily you distribute myapp as a gem as you
avoid the problems with it that Joel pointed out.

Eric, how does that work? Does gem install put a stub in your bindir
that calls ruby with the -I? Something else?

Looking at what gems did with rake, there is a stub in /usr/local/bin,
but there doesn’t seem to be anything that modifies RUBYLIB or uses the
-I switch. Maybe there’s some magic in the line “gem ‘rake’, version”?

Is there somewhere in the gem docs that addresses how to use it for an
application specifically?

Thanks!

On Apr 16, 2008, at 10:37 AM, Joel VanderWerf wrote:

Also, it’s fragile with respect to the cwd. You have to be in

Eric, how does that work? Does gem install put a stub in your bindir
that calls ruby with the -I? Something else?

Looking at what gems did with rake, there is a stub in /usr/local/
bin, but there doesn’t seem to be anything that modifies RUBYLIB or
uses the -I switch. Maybe there’s some magic in the line “gem
‘rake’, version”?

Kernel#gem activates rake (adds its load_paths to $LOAD_PATH) so that
Kernel#load can find the executable. (See ri Kernel#gem and ri Gem::activate for the full description.) RubyGems only hooks into
Kernel#require, so rake needs to be activated for load to work.

Is there somewhere in the gem docs that addresses how to use it for
an application specifically?

(I think you mean “gem” by “it”, sorry if I’m confused.) For
Kernel#gem you only need to use Kernel#gem in two situations:

a) you want a specific version of a gem
b) the gem you’re using replaces ruby standard library functionality
(soap4r, rdoc and rake are the only three I know about).