One of the great things about the JRuby project is that it’s easy to run Ruby programs without installing Ruby. In fact, you don’t even need to install JRuby. All you need is a JVM runtime and jruby-complete.

Rationale

Check out this other post for a discussion of my reasons for locking down your JRuby runtime. In summary, embedding jruby-complete gives you complete control of your Ruby runtime. That’s a good thing. The downside is that discovering and executing commands through jruby-complete can be a pain. The rest of this post describes how to ameliorate the pain.

Running jruby-complete

The base jruby-complete command is:

java -jar jruby-complete-1.4.0.jar

This gives you the same behavior as typing ruby or jruby .

java -jar jruby-complete-1.4.0.jar -e "puts 'Hello'"

prints “Hello.” (Of course, this depends on what your jruby-complete jar is named. I usually put the version number in there so I know what it is. I expect anyone reading this to be able to figure out what their jar is named and fill it in appropriately. If this is a hurdle, then too bad for you.)

I lied.1 To get the same JVM heap and stack sizes as typing jruby , you need to pass a couple of JVM options:

java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -e "puts 'Hello'"

Want to run irb? Try this:

java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -e 'load "META-INF/jruby.home/bin/jirb"'

1 2 3 4 irb(main): 001 : 0 > puts " Hello " Hello => nil irb(main): 002 : 0 > %

As you can see, the java command is getting to be a pain. It’s time to introduce some rake tasks to help us out.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 JRUBY_COMPLETE = " jruby-complete-1.4.0.jar " JRUBY = " java -Xmx500m -Xss1024k -jar #{ JRUBY_COMPLETE } " namespace :jruby do desc " Run JRuby help " task :help do sh %+ #{ JRUBY } --help + end desc " Run any command with JRuby " task :run do sh %+ #{ JRUBY } -e ' #{ ENV [ " cmd " ] } ' + end end

Now I can type rake jruby:run cmd='puts "Hello"' . Shell escaping is becoming a real annoyance at this point. Thankfully, I’m usually not using jruby-complete to run silly little commands. By the time I’ve introduced a Rakefile I’ve got a real application with tasks oriented around testing and running it, so it’s rare that I’m using a task like jruby:run very often.

Running my application may introduce a task that looks something like this:

1 2 3 task :run do sh " #{ JRUBY } lib/application_bootstrap.rb " end

Running it:

1 2 3 4 fletcher-git/github/jruby-complete-example(master) rake run (in /Users/fletcher/git/github/jruby-complete-example) java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar lib/application_bootstrap.rb Hello from application_bootstrap

Of course, my application comes with RSpec specs. The standard jruby-complete distribution comes with RSpec built in. How can I use it? The -S parameter runs files in JRuby’s bin directory:

1 2 3 4 5 6 7 namespace :spec do desc " Run RSpec against a specific file " task :run do raise " You need to specify a spec with spec= " if not ENV [ " spec " ] sh %+ #{ JRUBY } -S spec -f specdoc #{ ENV [ " spec " ] } + end end

Here’s a spec:

1 2 3 4 5 describe " John Galt " do it " does not tolerate logical fallacies " do " A " .should == " A " end end

Running it:

1 2 3 4 5 6 7 8 9 10 fletcher-git/github/jruby-complete-example(master) rake spec:run spec=spec/unit/objectivism_spec.rb (in /Users/fletcher/git/github/jruby-complete-example) java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -S spec -f specdoc spec/unit/objectivism_spec.rb John Galt - does not tolerate logical fallacies Finished in 0.123 seconds 1 example, 0 failures

Is this more typing than the usual spec spec/unit/objectivism_spec.rb ? Yes. Do I care? No. I know how to use my shell.

1 2 3 4 5 6 7 fletcher-git/github/jruby-complete-example(master) which sp sp () { rake spec:run spec=$@ } fletcher-git/github/jruby-complete-example(master) sp spec/unit/objectivism_spec.rb (in /Users/fletcher/git/github/jruby-complete-example) java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -S spec -f specdoc spec/unit/objectivism_spec.rb

Alright, so now that my application has been built up, I might want to start compiling the .rb files into .class files. Here comes jrubyc :

1 2 3 4 5 6 7 8 9 10 11 12 require " rake/clean " namespace :jruby do output_directory = " classes " directory output_directory CLEAN .include output_directory desc " Compile Ruby files in lib " task :compile => output_directory do sh %+ #{ JRUBY } -S jrubyc -p com/atomicobject -t #{ output_directory } lib + end end

1 2 3 4 5 6 7 8 9 10 11 fletcher-git/github/jruby-complete-example(master) rake jruby:compile (in /Users/fletcher/git/github/jruby-complete-example) mkdir -p classes java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -S jrubyc -p com/atomicobject -t classes lib Compiling all in '/Users/fletcher/git/github/jruby-complete-example/lib'... Compiling lib/application_bootstrap.rb to class com/atomicobject/lib/application_bootstrap fletcher-git/github/jruby-complete-example(master) rake jruby:run cmd='require "classes/com/atomicobject/lib/application_bootstrap"' (in /Users/fletcher/git/github/jruby-complete-example) java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -e 'require "classes/com/atomicobject/lib/application_bootstrap"' Hello from application_bootstrap

Remember that since we’re executing a java command, we can pass any typical JVM parameters before the -jar parameter. We’ve done this for things like:

enabling antialiasing in Apple’s JVM via a Java property. tweaking Substance’s widget behavior via a Java property. enabling Yourkit Java Profiler via the -agentlib parameter. including libraries and directories in the JVM ’s classpath via the -cp parameter.

Since these parameters need to be passed before the -jar parameter, a more sophisticated method for setting up the JRuby command is needed than the constant I’ve used. A method like that is specific for your application and beyond the scope of this post, but is not be difficult to create.

Conclusion

There are an uncountable number of good things about JRuby and jruby-complete is one of them. A little help from scripts and your shell means you can build and run your application with a controlled Ruby runtime.

Additional resources

The Rakefile, jruby-complete, and other files used in this post are available in this GitHub project.

JarJar Links is an ant library that is useful for combining multiple jar files together.

The AGI Production Simulator is built using the jruby-complete commands described in this post.

Replicating the true jruby behavior is way, way beyond the scope of this post. Check out the jruby script if you really care. Most of the time the JVM heap and stack sizes are the most important things to worry about.

↩

Edit 2/6/2010: Reduced -e ‘load…’ parameters to -S