We're hiring!

We're actively seeking developers for our new Detroit location. Learn more

Running a Ruby Application with jruby-complete

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.
  • I wrote a post a while ago about using JarJar to combine jruby-complete and other application dependences into a single file.
  • The AGI Production Simulator is built using the jruby-complete commands described in this post as well as the above jar-rolling technique.
  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

Matt Fletcher (73 Posts)

Matt is a software product developer and designer with Atomic Object.

This entry was posted in jRuby and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

7 Comments

  1. Posted February 1, 2010 at 1:10 pm

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

  2. Chris
    Posted February 1, 2010 at 1:10 pm

    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.

  3. cg
    Posted February 1, 2010 at 1:10 pm

    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.

  4. Babar
    Posted February 1, 2010 at 1:10 pm

    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’”

  5. Posted February 1, 2010 at 1:10 pm

    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

  6. David
    Posted February 1, 2010 at 1:10 pm

    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?

  7. mk
    Posted February 1, 2010 at 1:10 pm

    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?

2 Trackbacks

  1. [...] 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 [...]