Calling Clojure From JRuby

Geplaatst door Michiel de Mare vr, 12 dec 2008 11:23:00 GMT

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.

You’ll need Java, Git and Ant for this tutorial. Make sure you’ve got Java 6 – it’s a lot faster. Point JAVA_HOME to where your Java installation is.
~>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 ..
Now point JRUBY_HOME to your JRuby dir, and add $JRUBY_HOME/bin to your path.
jruby --version
# => jruby 1.1.7 (ruby 1.8.6 patchlevel 114) (...)
I find it safest to call scripts with 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

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.

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.
export CLASSPATH=$HOME/clojure/clojure.jar:\
$HOME/clojure/clojure-contrib/clojure-contrib.jar:\
.:classes
Now you can start the interactive Clojure REPL with this command:
java clojure.lang.Repl
# for readline support, install rlwrap
rlwrap java clojure.lang.Repl
To run a script:
java clojure.lang.Script myscript.clj
Now, let’s create a class in Clojure, and call it from JRuby.
;; 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
To compile this, Clojure will want to write the generated class files to the location defined in the *compile-path* global variable. In the REPL, 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 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 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.

Recommend Michiel de Mare

Geplaatst in , , ,  | Tags ,  | 4 reacties

Reacties

  1. / zei 86 dagen later:

    java.lang.IllegalArgumentException: Don’t know how to create ISeq from: Keyword (Blog.clj:3)

  2. NAN zei 86 dagen later:

    java.lang.IllegalArgumentException: Don’t know how to create ISeq from: Keyword (Blog.clj:3)

  3. Michiel zei 86 dagen later:

    Oops, fixed.

  4. Mark zei 97 dagen later:

    Usefull post!

(Laat url/e-mail achter »)

   Voorvertoning