Calling Clojure From JRuby

Geplaatst door Michiel de Mare

Clojure is a new functional programming language based on Lisp, running on the JVM. Even though Clojure is only a year old, there’s already a book being written about it.

Since this is a Ruby site, and Rails is still the best framework for web-apps regardless of language, I’ll demonstrate how to call Clojure functions from a JRuby Rails-app.

First an introduction to Clojure:

Clojure is an exciting language for four reasons. First, even though it’s a new language, there’s a vast amount of available libraries since it’s written for the JVM. Also, Clojure is not interpreted – it compiles functions directly to JVM bytecode, which means that it’s very fast. Thirdly, one of the goals of Clojure is to bring parallel programming to the masses, by supplying abstractions such as persistent datastructures (immutable, so they can be shared between threads), software transactional memory, and agents.

Finally, Clojure is a kind of Lisp, which means that you’ve got the power of macros. Macros are functions that run at compile time, and that return code, not data. Ruby makes generating code possible, but Lisp (and by extension Clojure) makes it easy, also removing the runtime performance penalty.

JAVA_HOME

~>java -version # => java version "1.6.0_07" git clone git://github.com/nicksieger/jruby.git git clone git://github.com/kevinoneill/clojure.git cd jruby; ant; cd .. cd clojure; ant; cd ..

JRUBY_HOME

$JRUBY_HOME/bin

jruby --version # => jruby 1.1.7 (ruby 1.8.6 patchlevel 114) (...)

jruby -S

jruby -S gem i rails mongrel jruby-openssl --no-rdoc --no-ri jruby -S gem i jdbc-mysql activerecord-jdbcmysql-adapter --no-rdoc --no-ri

You’ll need Java, Git and Ant for this tutorial. Make sure you’ve got Java 6 – it’s a lot faster. Pointto where your Java installation is.Now pointto your JRuby dir, and addto your path.I find it safest to call scripts with

To run your existing Rails projects, change the database driver from mysql to jdbcmysql , cd to the rails directory and enter jruby -S mongrel_rails start . If your project needs other gems, you need to install those first. Native gems won’t work.

export CLASSPATH=$HOME/clojure/clojure.jar:\ $HOME/clojure/clojure-contrib/clojure-contrib.jar:\ .:classes

REPL

java clojure.lang.Repl # for readline support, install rlwrap rlwrap java clojure.lang.Repl

java clojure.lang.Script myscript.clj

;; nl/rubyenrails/Blog.clj ;; clojure namespace corresponds to java qualified classname. (ns nl.rubyenrails.Blog ;; Create class with same name as namespace and ;; specify instance method "blog" with signature. ;; No main method please. (:gen-class :methods [[blog [Object] Object]] :main false)) ;; like in Python, first parameter is the instance itself ;; all methods of class start by default with prefix "-" (defn -blog [self x] (println (class x)) #{"basil" "rosemary" "thyme"}) ; return set to JRuby

*compile-path*

REPL

REPL

java clojure.lang.Repl (compile 'nl.rubyenrails.Blog) CTRL-C javap nl.rubyenrails.Blog # Now from a script cat > compile.clj (binding [*compile-path* "classes"] (compile 'nl.rubyenrails.Blog)) CTRL-D java clojure.lang.Script compile.clj # Let's package our classfiles: cd classes ; jar cf ../../blog.jar . ; cd ..

Now let’s turn to Clojure. To run Clojure, you’ll have to tell Java not only where to find the clojure libraries, but also where your own .clj files and your compiled clojure files are.Now you can start the interactive Clojurewith this command:To run a script:Now, let’s create a class in Clojure, and call it from JRuby.To compile this, Clojure will want to write the generated class files to the location defined in theglobal variable. In the, it’s set to ‘classes’, in scripts it’s not set at all. If you’re in a directory with Blog.clj in nl/rubyenrails, and there’s a directory “classes”, you can compile the file from the

Now we can finally call our code. Simply require the jar file we’ve created. All Java classes are in the Java module – within the Java module, Java package names are converted to (capitalized) module names.

jirb require 'blog.jar' puts *Java::nl::rubyenrails::Blog.new.blog(2 ** 99)

If you’ve made it this far, congratulations! You’ve now combined an hugely practical webframework with a incredibly powerful language running in the same enterprise-proof super-fast VM. The world lies at your feet.