Running a Ruby Application with jruby-complete

Article summary

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:

  1. enabling antialiasing in Apple’s JVM via a Java property.
  2. tweaking Substance’s widget behavior via a Java property.
  3. enabling Yourkit Java Profiler via the -agentlib parameter.
  4. 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.
  1. 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

Conversation
  • Luke says:

    JRuby is great! I use jruby-complete.jar to pckage a war to deploy in JBoss.
    Very good post btw.

  • Chris says:

    Very good post.

    Anyway, can anybody help me understand why “jruby” standalone and using jruby-complete-1.4.9.jar below behave different in this example:

    [cg ~/workspace/easysim]$ jruby -e “require ‘rubygems’; require ‘eventmachine’; puts ‘hi'”
    hi
    [cg ~/workspace/easysim]$ java -Xmx500m -Xss1024k -jar /home/cg/Downloads/firefox/jruby-complete-1.4.0.jar -e “require ‘rubygems’; require ‘eventmachine’; puts ‘hi'”
    file:/home/cg/Downloads/firefox/jruby-complete-1.4.0.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’: no such file to load — eventmachine (LoadError)
    from file:/home/cg/Downloads/firefox/jruby-complete-1.4.0.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
    from -e:1
    [cg ~/workspace/easysim]$

    It seems to me that using jruby-complete.jar the installed rubygems are not picked up !?

    Any help is very much appreciated.

  • cg says:

    Is there an easy way to use installed (j)rubygems in a script run launched via jruby-complete?

    In other ways i’d like to do something like this:

    java -Xmx500m -Xss1024k -jar jruby-complete.jar -e “require ‘eventmachine’; puts ‘eventmachine required’ ”

    so that the installed ‘eventmachine’ gem is picked up from my gem repository.

  • Babar says:

    On Windows, this line doesn’t work :
    java -Xmx500m -Xss1024k -jar jruby-complete-1.4.0.jar -e ‘load “META-INF/jruby.home/bin/jirb”‘

    You must inverse double quotes with single quotes, like this :
    java -jar jruby-complete-1.4.0.jar -e “load ‘META-INF/jruby.home/bin/jirb'”

  • Eric Kramer says:

    I’m also interested in the subject of accessing gems using JRuby-complete. I just stumbled upon this blog posting from Nick Sieger that appears quite relevant. Note that this is 1.1.6 so perhaps things have changed.

    http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar

  • David says:

    I use jruby-complete for testing with RSpec. Now I’m trying to use RCov with it, but no luck right now because RCov is using binary that can’t be installed in the jar package.

    How could I do that? I mean, to use RCov with jruby-complete? Did anyone try?

  • mk says:

    I try to start jirb_swing with JSR 223 script and jruby-complete but it does not show the commmand prompt. How can I do this?

  • […] Object's Blog On Software Design & Development « Running a Ruby application with jruby-complete Beyond UX and Agile » The case for embedding jruby-complete into your application By Matt […]

  • Comments are closed.