Notes to self

Using Ruby gems in Java/Gradle projects with JRuby

Let’s have a quick look on how to reuse our Ruby code and gems in Java leveraging the awesome work of JRuby guys. What we need is Java, Gradle and JRuby installed and we are ready to rock.

I decided to try reusing my already made invoicing gem InvoicePrinter and see how much pain it would be to actually use a gem like that in a Java project instead of writing the same thing again in pure Java. For starters I installed Java, Gradle and JRuby. I installed JRuby with ruby-install via chruby (see my bold opinions on managing Ruby versions here):

$ chruby
 * jruby-9.1.5.0
$ chruby jruby-9.1.5.0

And then created a new project with Gradle:

$ gradle init --type java-library

Since we will need to load our gems from somewhere let’s install them to a folder called rubygems and let’s create a JAR out of them:

$ gem fetch invoice_printer
$ GEM_PATH=rubygems GEM_HOME=rubygems ruby -S gem install ./invoice_printer-0.0.8.gem
$ cd rubygems && jar -cf rubygems.jar .

We need to work with JARs in Java, but luckily JRuby can automatically find your gems for requires if you package a RubyGems tree like that in a JAR and compile them as other JARs in the project.

Now we are ready to implement our Java class with some embedded Ruby:

└── src
    └── main
        └── java
            └── invoicing_automat
                └── Main.java

I am calling the project invoicing_automat and creating a file called Main.java with the following content:

package invoicing_automat;

import org.jruby.*;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Main {
  public static void main(String[] args) {
    ScriptEngineManager m = new ScriptEngineManager();
    ScriptEngine rubyEngine = m.getEngineByName("jruby");

    try {
      rubyEngine.eval(
        "require 'rubygems'\n"+
        "require 'invoice_printer'\n"+
        "item = InvoicePrinter::Document::Item.new(\n"+
        "name: 'Programming',\n"+
        "quantity: '10',\n"+
        "unit: 'hr',\n"+
        "price: '$ 90',\n"+
        "amount: '$ 900'\n"+
        ")\n"+

        "invoice = InvoicePrinter::Document.new(\n"+
        "number: 'NO. 198900000001',\n"+
        "provider_name: 'John White',\n"+
        "provider_street: '5th Avenue',\n"+
        "provider_street_number: '1',\n"+
        "provider_postcode: '747 05',\n"+
        "provider_city: 'NYC',\n"+
        "purchaser_name: 'Will Black',\n"+
        "purchaser_street: '7th Avenue',\n"+
        "purchaser_street_number: '1',\n"+
        "purchaser_postcode: '747 70',\n"+
        "purchaser_city: 'NYC',\n"+
        "issue_date: '05/03/2016',\n"+
        "due_date: '19/03/2016',\n"+
        "total: '$ 900',\n"+
        "bank_account_number: '156546546465',\n"+
        "items: [item],\n"+
        "note: 'This is a note at the end.'\n"+
        ")\n"+

        "InvoicePrinter.print(\n"+
        "document: invoice,\n"+
        "file_name: 'simple_invoice.pdf'\n"+
        ")\n"
      );
    } catch (ScriptException e) {
      e.printStackTrace();
    }
  }
}

The Ruby code itself is taken from the gem examples and we are just evaluating the Ruby code via JRuby ScriptEngineManager. Unfortunately Java lacks the Ruby’s HEREDOC syntax which makes the code looks kind of bad (there is an option to load the Ruby code from separate file of course).

Last thing missing is our Gradle config build.gradle in which we have to specify our compile dependencies:

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = '1.8'

mainClassName = 'invoicing_automat.Main'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jruby:jruby-complete:9.1.5.0'
    compile files('../rubygems/rubygems.jar')
}

We need JRuby package from Maven and our local JAR containing the gems we are requiring from Main.java file. Once updated let’s see the result:

$ gradle run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run

BUILD SUCCESSFUL

Total time: 13.977 secs
...

In our project repository appeared a file called simple_invoice.pdf. Yay JRuby works! Mixing Ruby in Java and Java in Ruby is a promising approach and I will hopefully explore more of JRuby in foreseeable future.

Work with me

I have some availability for contract work. I can be your fractional CTO, a Ruby on Rails engineer, or consultant. Write me at strzibny@strzibny.name.

RSS