James Britt

Maximum R&D

Using a gem with Rawr

January 2014

Rawr is an application-packaging tool for JRuby. It grew out of a commercial product where JRuby was used with Monkeybars to provide a desktop application.

Rawr takes all of an application’s files and packages them up into an executable jar. The end result includes a copy of jruby-complete.jar such that, so long as you have Java installed, you can run this jar as you would any other executable jar file.

It’s very slick.

Some Rawr basics

Rawr works on the assumption that your application is self-contained. That is, you should not be trying to load any gems. Indeed, Rawr makes an effort to adjust the contents of $LOAD_PATH so that you are only loading files from specific directories local to your application.

If you are used to using gems in you Ruby apps this can be a hurdle. However, there’s nothing magical about gems; they just (ordinarily) live in locations outside of your project tree, and Ruby provides a way for your program to discover their location.

Since locations outside of your project tree are a no-no with Rawr you need to move any gem files into your project tree.

This is easy. Use gem unpack. By default this will locate the latest version of that gem that you have installed and create a folder in the current directory named for the gem and version number.

If you look at what’s in that folder you will (almost always) find at least a lib subfolder as well as subfolders for tests, examples, and maybe a few other things. In almost all cases you will really only need what’s in the lib subfolder.

Rawr doesn’t demand that you follow too many requirements for where you put your folders but there are some conventions.

Ruby libraries typically go into a subfolder of your project tree called lib/ruby. I guess that technically this is a subfolder of a subfolder.

But note that while this is something of a convention you still have to tell Rawr about this path.

Rawr wants to know

When you create a new Rawr project (i.e. you decide to use Rawr in some project you want to package up as a self-contained executable, and you generally do this by running rawr install) you should have a file named build_configuration.rb.

If you opted for the auto-generated file it should contain a number of configuration settings, any of which will be commented out.

One of these indicates the location of your Ruby source files.

@ c.source_dirs = [“src”, “lib/ruby”]@

This example shows another convention: Main application files are placed under src while helper library files go under lib/ruby, but in all cases you have to tell Rawr about this.

Rawr uses this information in order to know what files and folders are to be bundled up. It also takes the paths set in c.source_dirs and stuffs them into $LOAD_PATH.

Rawr works by providing a Main.java class that serves as the entry point for the executable jar. When you run the jar it calls into this Main class, and the default code will look for a Ruby file called main.rb someplace in the paths you set.

The default main.rb (that is, the one generated when you run rawr install) will do assorted set-up things and load another auto-generated file, manifest.rb. It is in this file where some additional $LOAD_PATH alterations take place.

This may also a good place to set up any other folder paths you want to ensure are on the load path. You can also do this in main.rb, but in any event you’ll want to set up these paths before your application code tries to load any other files (such as the ones from the gem you unpacked).

An unpacking tip

When you unpack a gem and place it into your rawr-ready project you may only need the contents of the gem’s lib folder, but it can be useful to include the parent folder, the one that has the gem name and version. It makes it easier to quickly see just what you’re using.

For example, in your project tree:

@ /lib/ruby/restafarian-0.5.1/lib@

In your build_configuration.rb you are likely setting lib/ruby as a folder to bundle up; that means when you want to require any (in this example) restafarian files you’ll to have added this additional info to your load path. So, while lib/ruby may be part of the load path, you would need to add /lib/ruby/restafarian-0.5.1/lib
as well before calling require 'restafarian'

The downside is that this ties the gem version number to your code. If you update the gem and unpack this later version into your project you’ll need to update the path references.

I will confess that this may just be a “works for James” situation. However you decide to manage this just be sure that you’ve set up your configuration and any $LOAD_PATH alterations to include the proper path to the files you will want to require.

Oh, by the way …

If a gem you unpack decides to use another gem then you will need to unpack that gem as well and make sure it is part of your project. All code dependencies should be in your project. What you need to watch for, though, are gems that attempt to load a gem by explicitly invoking rubygems. It’s dopey but sadly not unheard of.

Rawr should be sorting out the load path but as rubygems has become a built-in part of Ruby it is possible that an unexpected call to a gem might end up loading code that is not part of your project. Problem one is that you program might seem to work fine on your machine but fail when run on another machine. Problem two is that a different version of a gem might get loaded causing mysterious behavior.

UPDATE

While checking out a report of some trouble including third-party libraries I created a test program that had assorted directories and subdirectories under lib/ruby. I ran rawr:jar and then checked the results. There was no lib/ruby nor src folder in the jar. All the files and folders from those directories were combined into the root of the jar. There is a ruby folder there, but it’s empty.

If you have two files with the same name in both src and lib/ruby only one comes out alive. This seems like a Bad Thing.

The goofy thing is that this may have been the behavior for quite some time. I haven’t yet gone through the code; it’s plausible that this combining of folders was intentional, but the presence of that empty ruby directory suggests a bug, albeit one that apparently (at least in my use of Rawr of time) didn’t seem to break anything. However, I dug up some jars made a few years ago and it appears that this was not always the case.

Interesting.