Monday, April 7, 2008

CampDepict: JRuby, CDK, and Camping

JRuby has a bright future, especially in areas where the immediacy of Ruby and the established APIs of Java meet. Rich Apodaca recently created a smiles to 2D structure tool using ruby, Rails, the CDK Java cheminformatics library, and a java-to-ruby bridge (part 1 and part 2). Here is the same project, created using JRuby, one of Rich's Java libraries, and Camping , a lightweight web framework.

1. Install the JDK

2. Install JRuby - I'm using 1.1 here. Did you remember to set JRUBY_HOME? Is jruby in your PATH?

$ jruby -v
ruby 1.8.6 (2008-03-28 rev 6360) [x86-jruby1.1]

3. Install Camping and dependencies into jruby.
$ jruby $JRUBY_HOME/bin/gem install camping --source

Note: if you don't have c ruby installed, the following will work:
$ gem installed camping --source

4. Download structure-cdk-0.1.2 from sourceforge, and save the two jars (cdk-20060714.jar and structure-cdk-0.1.2.jar) from the lib directory to $JRUBY_HOME/lib.

5. Create a depict directory, and a depict/static directory.

6. Create two 200x200 pngs called invalid.png, and blank.png. Store them in the static directory.

7. Save campdepict.rb to the depict directory (text at the end of this entry).

8. And run! (You can stop the server with a Ctrl-C).
$ jruby $JRUBY_HOME/bin/camping campdepict.rb

(sorry, had some image upload issues with Blogger ... no pretty picture).


Camping deployments usually start as one file containing model, view and controller, but can be separated as needed (check that wiki). After some smiles validation by Index, the view passes the smiles string back to the Image_for controller, which calls the helper where the cdk routines get called.

The smiles-to-png code in the image_for helper metho is a hybrid of Rich's rcdk code and depict code. But instead of extracting the Java calls to CDK in a separate Java class, the calls are embedded directly into the jruby class. JRuby lets you store Java objects directly as ruby objects. Although the ruby smiles string is transparently translated to Java ( ...parseSmiles( smiles ) ), the translation of the Java ByteArrayOutputStream to a ruby String requires a thunk routine (String.from_java_bytes out.toByteArray).

Final Notes

The begin/rescue/end block is dependent on the cdk-20060714.jar packaged with structure-cdk-0.1.2. More recent versions have changes to both this code and the structure-cdk-0.1.2 package.

There are some bugs, which I haven't tracked down yet. The code will not render salts, metals, or accurately depict chiral centers with wedge bonds.

As per the jruby wiki, invoking jruby with a few command line switches will help the performance quite a bit:
jruby -J-server -J-Djruby.thread.pooling=true $JRUBY_HOME/bin/camping campdepict.rb

Finally, it works just as well under windows as Linux.


include Java

Camping.goes :Campdepict

module Campdepict::Controllers

class Index < '/'
def get
if input.smiles then
@smiles = input.smiles
@smiles = ''
@escapedsmiles = CGI::escape(@smiles)
render :smiles_picture

class Image_for < R '/image_for'
def get


module Campdepict::Views
def layout
html do
head do
title { "SMILES Depictor" }
body { self << yield }

def smiles_picture
h1 "Depict a SMILES String"
img :src=> '/image_for/?smiles=#{@escapedSmiles}"
form :action => R(Index), :method => 'get' do
label 'Smiles', :for => 'smiles'
input :name => 'smiles',
:value => @smiles,
:size => '50',
:type => 'text


module Campdepict::Helpers
EDGE_SIZE = 200 # image size
MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript',
'.jpg' => 'image/jpeg', '.png' => 'image/png'}
PATH = Dir.pwd

def render_smiles(smiles)
if ! smiles (smiles.eql? '') then
return static_get('blank.png')

# cdk-20060714 dependent code
smiles_parser =
sdg =
sdg.setMolecule( smiles_parser.parseSmiles( smiles ) )
image = Java::net.sf.structure.cdk.util.ImageKit.createRenderedImage(sdg.getMolecule(), EDGE_SIZE, EDGE_SIZE )
return static_get('invalid.png')

out =
javax.imageio.ImageIO.write image, "png", out

String.from_java_bytes out.toByteArray

# static_get is straight outa the Camping wiki
def static_get(path)
@headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] "text/plain"
unless path.include? ".." # prevent directory traversal attacks
@headers['X-Sendfile'] = "#{PATH}/static/#{path}"
@status = "403"
"403 - Invalid path"


Joe Mulvaney said...


Great post! Thanks :-)

I'm trying to get this working with jruby1.1.2, camping 1.5.180 and the correct structure-CDK stuff on OSX 10.5.3. Invoking:

$ jruby -S camping campdepict.rb

borks with a stacktrace that starts like this:

!! trouble loading campdepict: [SyntaxError] /Users/mulvaney/devel/camping/depict/campdepict.rb:41: , unexpected tIDENTIFIER
/Users/mulvaney/devel/camping/depict/campdepict.rb:502:in `/Users/mulvaney/devel/camping/depict/campdepict.rb'
/Users/mulvaney/devel/camping/depict/campdepict.rb:502:in `load'
/usr/local/jruby-1.1.2/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:502:in `load'

Any thoughts on what might be happening?


goeslightly said...

Joe. sorry for the delay! It is probably a typo due to my poor formatting.
I've pushed camping to github, in case there are typos (and copying that code above would be typo prone). I've included one of _why's very simple camping apps - nuts.rb as well. Both work under Linux and Windows, using the appropriate bat or sh file. Try the nuts.rb first to debug camping, then try campdepict again.

Once you register with github with your ssh key, you'd use something like:

git config

git clone