2011
09.23

In my previous post, I talked about Polyglot programming in Grails. As I mentioned in that post, there are plugins for Clojure and Scala, but not for Ruby or Python. So, today I thought I would tackle one of those by creating a new Grails plugin for Ruby! It was really easy to integrate JRuby (the Ruby implementation for the JVM) with Grails and I think the plugin works pretty good so far! So, keep reading to see how I created the plugin as well as a demonstration of how to use the plugin!

I decided that I should do things the right way this time and start with some test cases and then implement the code to satisfy those tests (test-driven-development). So, lets create a sample app with a test class and a controller. I’m using an integration test instead of a unit test because as you’ll see later, I’m actually going to be doing some meta-programming and need access to the entire Grails environment in the test for my dynamic “ruby” method to work.

  • grails create-app grails-ruby-sample
  • cd grails-ruby-sample
  • grails create-controller home
  • grails create-integration-test grails.ruby.sample.HomeController

These are the only Groovy files we’ll need in the sample app. The only other thing will be a Ruby file which we’ll add later. Here’s the tests:


// test/integration/grails/ruby/sample/HomeControllerTests.groovy
package grails.ruby.sample
 
import static org.junit.Assert.*
import org.junit.*
 
class HomeControllerTests extends GroovyTestCase {
  def dayOfWeek
 
  @Before
  void setUp() {
    dayOfWeek = new Date().format("EEEE")
  }
 
  void testHi() {
    def controller = new HomeController()
    controller.hi()
    assertEquals controller.response.contentAsString, "Hi Bobby! Today is $dayOfWeek.".toString()
  }
 
  void testBye() {
    def controller = new HomeController()
    controller.bye()
    assertEquals controller.response.contentAsString, "Bye Bobby! Today is $dayOfWeek.".toString()
  }
}

As you can see, we are just testing the output of two Grails controller actions. One says hi and one says bye. This would be very easy to implement in straight Groovy, but we’re actually going to have the controller call out to Ruby for the implementation! Here’s the controller:


// grails-app/controllers/grails/ruby/sample/HomeController.groovy
package grails.ruby.sample
 
class HomeController {
  def hi() {
    ruby.put('name', 'Bobby')
    ruby.put('greeting', 'Hi')
    render ruby.eval('greet($name, $greeting)')
  }
 
  def bye() {
    ruby.put('name', 'Bobby')
    ruby.put('greeting', 'Bye')
    render ruby.eval('greet($name, $greeting)')
  }
}

So now we have our sample app all setup to test! The first thing you are probably wondering though is where the heck did that “ruby” property come from, right? To explain that, let’s switch gears and start looking at the source for the plugin (grails-ruby)! There’s really only four parts:

1) grails-app/conf/BuildConf.groovy: We need to add a runtime dependency for JRuby.

dependencies {
  runtime 'org.jruby:jruby:1.6.4'
}

2) RubyGrailsPlugin.groovy: We need to implement onChange and doWithDynamicMethods for evaluating Ruby code, automatically re-loading when a Ruby file changes and inserting the “ruby” property into all Groovy classes via meta-programming.

def onChange = { event ->
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("jruby")
 
  def source = event.source
  if(source instanceof FileSystemResource && source.file.name.endsWith('.rb')) {
    source.file.withReader { reader ->
      engine.eval(reader);
    }
  }
}
 
def doWithDynamicMethods = { ctx ->
  ScriptEngine engine = new ScriptEngineManager().getEngineByName("jruby")
 
  def rubyFiles
  if(application.warDeployed) {
    rubyFiles = parentCtx?.getResources("**/WEB-INF/ruby/*.rb")?.toList()
  } else {
    rubyFiles = plugin.watchedResources
  }
 
  rubyFiles.each {
    it.file.withReader { reader ->
      engine.eval(reader)
    }
  }
 
  application.allClasses*.metaClass*."getRuby" = {
    return engine
  }
}

3) scripts/_Events.groovy: We need to use the eventCompileStart event to copy Ruby files over to the destination directory (WEB-INF/ruby).

eventCompileStart = {
  def rubyDestDir = "${grailsSettings.projectWarExplodedDir}/WEB-INF/ruby"
  ant.mkdir dir: rubyDestDir
  ant.copy (todir: rubyDestDir) {
    fileset(dir:"${basedir}/src/ruby", includes:"*.rb")
  }
}

4) scripts/_Install.groovy: We need to create a directory for applications using the plugin to put Ruby files in (src/ruby).

ant.mkdir(dir:"${basedir}/src/ruby")

And that’s really all there is to it! You can view the plugin source on GitHub here. So, now back to our sample application (grails-ruby-sample)! Let’s install the plugin, create a Ruby file that we will use from the controller, implement the Ruby code to satisfy our Groovy test cases and test our app!

  • grails install-plugin ruby
  • touch src/ruby/ruby_hello_world.rb


// src/ruby/ruby_hello_world.rb
class RubyHelloWorld
  def create_greeting(name, greeting)
    time = Time.new
    day = time.strftime("%A")
    "#{greeting} #{name}! Today is #{day}."
  end
end
 
def greet(name, greeting)
  hello_world = RubyHelloWorld.new
  hello_world.create_greeting(name, greeting)
end

  • grails test-app

And that’s it! The two integration test cases for our sample app should pass. It might take awhile to download the JRuby libraries the first time testing or running after you install the plugin though.

The source code for the plugin and sample app are located on GitHub and the plugin is documented in the Grails plugin portal:

I used Jeff Brown’s Clojure plugin as a guide for creating this Ruby plugin. I have set the version for this first release of the plugin to 1.0.M1 which simply means it’s a milestone release and not ready for production yet. I hope to have a final 1.0 release in the near future after I get some feedback on the plugin as people test it out. I hope you enjoyed reading this post about Grails Ruby!

2011
09.21

I have been playing around with using different JVM languages in Grails lately. For example, Clojure is really easy to integrate into Grails via the plugin. The simple fact that you can do this in Grails by just installing one little plugin is pretty cool! So, I decided it would be fun to create a Grails app that uses as many languages as possible, ha! There’s absolutely no good reason to do this other than it’s kind of fun!

There aren’t any Ruby or Python plugins for Grails yet, so I just add them as dependencies manually. I’m actually using JRuby and Jython though, which are implementations of these languages specifically for the JVM. For JavaScript, Rhino which comes bundled with JDK 6. The end goal is to have one Grails controller (a Groovy class) that uses Ruby, Python, JavaScript and Clojure (note at bottom about why Scala wasn’t included).

First, we need to setup our polyglot Grails application!

  • grails create-app polyglot
  • cd polyglot
  • grails install-plugin clojure


// grails-app/conf/BuildConfig.groovy
dependencies {
  runtime 'org.jruby:jruby:1.6.4'
  runtime 'org.python:jython-standalone:2.5.2'
}

Then we need to create three Grails files (a controller, unit test and integration test). Why an integration test and unit test? Well, this is simply because it is not possible (at least right now) to test Clojure code via the plugin in unit tests. There is a property (clj) injected into Grails artifacts (our controller in this case), but it only works in integration tests. The rest of the languages can be tested in a unit test though.

  • grails-app/controllers/polyglot/TestController.groovy
  • test/unit/polyglot/TestControllerUnitTests.groovy
  • test/integration/polyglot/TestControllerIntegrationTests.groovy

Next, we need a simple problem we want to try out in all languages. I’m a math nerd, so I’m simply going to make sure that the value of PI is the same in all languages. Here’s my controller:


package polyglot
import javax.script.*
class TestController {
  def javascript() {
    def engine = new ScriptEngineManager().getEngineByName("JavaScript")
    render engine.eval("Math.PI")
  }
  def ruby() {
    def engine = new ScriptEngineManager().getEngineByName("jruby")
    render engine.eval("Math::PI")
  }
  def python() {
    def engine = new ScriptEngineManager().getEngineByName("python")
    engine.eval("import math")
    render engine.eval("math.pi")
  }
  def clojure() {
    render clj.pi
  }
}

As you can see, I’m using the ScriptEngine class that comes with Java 6 to invoke the Ruby, Python and JavaScript languages. You could just as easily have put the code in separate Ruby, Python, JavaScript files, but I’m just keeping it simple for this example. The only real difference is that for Python you need to import math first. The Clojure implementation is in a separate file because the plugin automatically adds a folder under src and hooks into Grails events to compile. Here’s the Clojure code:


; src/clj/pi.clj
(ns grails)
(def pi Math/PI)

We can certainly test the controller by viewing these actions in a browser to make sure all the languages are working, but the whole point is to make sure they are all the same, right? So, I’m going to write actual tests that compare the value returned from the actions for each language against the value of PI in Groovy! Here’s my unit tests:


package polyglot
@TestFor(TestController)
class TestControllerTests {
  void testJavaScript() {
    controller.javascript()
    assertEquals response.text, Math.PI.toString()
  }
  void testRuby() {
    controller.ruby()
    assertEquals response.text, Math.PI.toString()
  }
  void testPython() {
    controller.python()
    assertEquals response.text, Math.PI.toString()
  }
}

And here’s the integration test for Clojure:

package polyglot
class TestControllerIntegrationTests extends GroovyTestCase {
  void testClojure() {
    def test = new TestController()
    test.clojure()
    assertEquals test.response.contentAsString, Math.PI.toString()
  }
}

And there we have it! A Grails application that calculates PI in Clojure, Ruby, Python and JavaScript as well as ensures PI in these languages is the same as PI in Groovy! Hope you enjoyed reading!

As a side note, there also is a Scala plugin for Grails, but I unfortunately could not get it to work with Grails 2.0.0.M2. I keep getting the following error when I try to compile Scala source code via the plugin.

Could not compile Scala sources: BuildException: Compile failed because of an internal compiler error (object scala not found.)

If anybody who has successfully used the Scala plugin with Grails 2.0 has any suggestions for what I might be doing wrong, please leave a comment.

Switch to our mobile site