Wednesday, July 13, 2011

JRuby on Heroku via Clojure

The big news about "Matz to Heroku" reminded me another news from Heroku. That's "Clojure on Heroku"!!! Yes, this news was for me, a JVM language lover. JVM has started running on Heroku, which means all JVM languages run on Heroku via Clojure. When I heard the news, I thought I should have tried that. So, today, I actually tried to run JRuby from Clojure. Happily, JRuby easily ran on Heroku.

I read these two, Clojure on Heroku and Getting Started With Clojure on Heroku/Cedar, and tried. These are good documents to get started. Following the documents, I wrote project.clj below:

(defproject hello-world "0.0.1"
:dependencies
[[org.clojure/clojure "1.2.1"]
[org.jruby/jruby-complete "1.6.3"]
[ring/ring-jetty-adapter "0.3.9"]])

This dependency is parsed by Leiningen(https://github.com/technomancy/leiningen). The format is [maven's groupId/ArtifactId version], so I added [org.jruby/jruby-complete "1.6.3"] in project.clj. Next, I edited web.clj as in below:

(comment "filename: web.clj")

(ns demo.web
(:use ring.adapter.jetty))

(import '(org.jruby.embed ScriptingContainer))
(def c (ScriptingContainer.))
(def version (. c runScriptlet "JRUBY_VERSION"))

(defn app [req]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (str "Hello JRuby " version " from Clojure!")})

(defn -main []
(let [port (Integer/parseInt (System/getenv "PORT"))]
(run-jetty app {:port port})))

In this file, I imported org.jruby.embed.ScriptingContainer class. It is RedBridge. Then, I instantiated ScriptingContainer and evaluated JRUBY_VERSION constant which was assigned to "version" so that I could use later.

When I uploaded this project and requested from a browser, I could see the JRuby's version.
OK, JRuby worked!


I tried one more since just showing JRuby version is too simple. The second attempt was somehow proxy like code using Ruby's open-uri standard library. I wrote src/demo/jruby.clj as in below:

(comment "filename: jruby.clj")

(ns demo.jruby
(:use ring.adapter.jetty))

(import '(org.jruby.embed ScriptingContainer))
(def c (ScriptingContainer.))

(defn app [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body (. c runScriptlet "require 'rubygems'; require 'open-uri'; f = open('http://www.ruby-lang.org/').read")})

(defn -main []
(let [port (Integer/parseInt (System/getenv "PORT"))]
(run-jetty app {:port port})))

In jruby.clj, I changed text/plain to text/html of response header and, in response body part, put a result of evaluating a short Ruby code. As you know, this tiny Ruby code reads HTML from http://www.ruby-lang.org/ and returns the contents. Then, changed Procfile as in below because the file is jruby.clj this time.

Procfile

web: lein run -m demo.jruby

Again, upload all to Heroku. Browsing the url, I got the output below:
Yes! It worked!



My next attempt will be Rubygems to Clojure on Heroku. I think Rubygems will possibly cover something missing in Clojure.



You might be interested about using Rubygems from Clojure. My presentation at RubyConf 2010, "Rubygems to All JVM Languages" (slide: http://servletgarden-point.appspot.com/slideshow, sample code: https://github.com/yokolet/rubyconf2010), might help you understand.