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:
- 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 thejruby
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
JRuby is great! I use jruby-complete.jar to pckage a war to deploy in JBoss.
Very good post btw.
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.
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.
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'”
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
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?
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 […]
[…] https://spin.atomicobject.com/2010/02/01/running-a-ruby-application-with-jruby-complete/ […]