Editor's note: IBM® WebSphere® sMash and IBM WebSphere sMash Developer Edition are based on the highly acclaimed Project Zero incubator project. Project Zero is the development community for WebSphere sMash and will continue to offer developers a cost-free platform for developing applications with the latest builds, the latest features, and the support of the community.
This article assumes that you have downloaded Project Zero and either completed the introductory tutorial or written a simple application yourself. You should also be familiar with the Ruby programming language, but I assume that's what brought you here in the first place!
Project Zero provides support for scripting in Groovy and PHP out-of-the-box, making it easy to create RESTful resources and utility scripts without any of the complexity or configuration that Java™ developers are so used to dealing with. But even though the Zero team has chosen to throw its support behind Groovy and PHP, that doesn't mean that other dynamic scripting languages aren't just as popular or useful; it would be nice if, say, Ruby or Python developers could be at home with Zero too, reusing their skill sets and taking advantage of Zero's innovation. This article shows Ruby enthusiasts how to add support for their favorite language to the Zero platform and explains how users of other languages could do the same.
Understanding Zero's approach to scripting languages
Groovy and PHP may be the primary scripting languages for the Zero platform, but they are integrated into the Zero runtime (called Zero Core) in a very generic way. The architecture of Zero Core is designed to allow for executing disparate scripts and templates, and nothing in the programming model depends on the aforementioned languages. This section explains how Zero Core provides support for Groovy and PHP and how you can use these same mechanisms to add Ruby.
Zero Core and the interpreter API
The Zero Core API includes an interface named zero.core.interpreter.Interpreter that is central to its execution of user scripts. This interface is used by the HTTP request handler to invoke different types of scripts based on their file extensions. When a new HTTP request is received, the request handler puts all of the HTTP data into Zero's global context, uses the target script's file extension to find the right Interpreter object, and then delegates the script invocation to that object; the script that is executed can access the HTTP request data through the global context. The Interpreter class has one method, invoke(), which is given the name of the script to execute and not much more.
Groovy support is implemented in a class named zero.core.interpreter.GroovyInterpreter. Its invoke() method uses Groovy's compiler API to parse and execute scripts. The mapping of .groovy files to the Groovy interpreter is established in Zero Core's own zero.config file, as shown in Listing 1:
Listing 1. Configuration of Groovy interpreter for Zero Core
[/app/interpreters]
.groovy=zero.core.interpreter.GroovyInterpreter
|
As you can see, interpreters are configured in zero.config by mapping file extensions to interpreter classes. All you need to do to add support for Ruby is to write an interpreter class and then add an entry to zero.config that associates it with Ruby (.rb) files.
Strategy for creating a Ruby interpreter
You must write your Ruby interpreter class in Java code to integrate with Zero Core, which means you have two options for executing Ruby scripts: you can either use the JDK's Runtime.exec() API to run Ruby as a separate process or you can use JRuby, which is a Java-based implementation of Ruby, to process the scripts in the same process. The JRuby option is more attractive for three reasons:
- Creating a separate process would prevent you from sharing the global context data structure, which is an essential part of all Zero scripts.
- JRuby allows you to invoke Java APIs from your Ruby code, so all of the Zero Core APIs will be accessible.
- Adding JRuby to a Zero application is as simple as adding a few JAR files to its /lib directory -- no environment variables are necessary.
This article uses JRuby 1.0. (For a link to the JRuby 1.0 download page, check the Resources section.)
To create an interpreter class for Ruby, you need to use JRuby's org.jruby.Main class, which allows you to not only execute Ruby
scripts but also redirect standard I/O and handle exceptions that are raised
because of developer error. The next section shows how to take the data that is passed to Interpreter.invoke() and convert it into API calls on org.jruby.Main.
Implementing an interpreter with JRuby
Before you extend Zero Core with your JRuby-based interpreter, you first need a sample application to develop and test with. If you're following along with the article, create a new Zero application named zero.scripting on your machine; if you want to see the final results right now, the Resources section links to a .zip file with the completed project.
Once you create the the zero.scripting project, you need to add some .jar files from the JRuby distribution to its /lib directory. Specifically, you need to copy over the jruby.jar and asm-2.2.3.jar files. If you intend to edit the code for the interpreter class in Eclipse, you also want to add these .jar files to your Eclipse project's classpath.
To create the JRubyInterpreter class, create a new
source code file with an empty class skeleton and add the Interpreter interface to the definition. You then need to add a method declaration for invoke() so that the class compiles. From there, it's a matter of passing the data given to invoke() over to org.jruby.Main in the right format. Listing 2 shows an implementation that performs the following tasks:
- Redirects JRuby's standard output to the current HTTP request's output stream. This allows Ruby developers to send HTTP response data back to the client in the same way that Groovy developers do.
- Creates an array of arguments for the JRuby runtime. These arguments include the name of the target script and are in the same format they would be in if you were invoking JRuby from the command line.
- Executes the script by calling
Main.run().
Listing 2. The JRubyInterpreter class
package zero.scripting.ruby;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import org.jruby.Main;
import org.jruby.RubyInstanceConfig;
import zero.core.context.GlobalContext;
import zero.core.events.HandlerInfo;
import zero.core.interpreter.Interpreter;
public class JRubyInterpreter implements Interpreter
{
public void invoke(HandlerInfo handlerInfo)
{
final PrintWriter writer = GlobalContext.get("/request/writer");
//
// JRuby's API uses the deprecated PrintStream class, so we
// have to wrap the response writer in a PrintStream
//
PrintStream stream = new PrintStream(new OutputStream(){
public void write(int b) {
writer.write(b);
}
});
//
// Map stdout/stderr to HTTP response writer
//
RubyInstanceConfig jrubyConfig = new RubyInstanceConfig();
jrubyConfig.setOutput(stream);
jrubyConfig.setError(stream);
File scriptFile = new File(handlerInfo.handler);
String[] jrubyArgs = new String[]{ scriptFile.getPath() };
//
// Execute Ruby script!
//
Main jruby = new Main(jrubyConfig);
jruby.run(jrubyArgs);
}
}
|
You can add this code to your application's /java directory and compile it using the zero make command. The resulting class files will be copied to the application's /classes directory and loaded at run time.
Updating the Zero configuration file
Your code is in place, but you still have one more step to complete: like the
Groovy interpreter, you must register your Ruby interpreter with Zero Core so that
it knows how to handle requests for .rb files. Adding the stanza from Listing 3 to
your application's /config/zero.config file makes
things official:
Listing 3. Registering your Ruby interpreter with Zero Core
[/app/interpreters]
.rb=zero.scripting.ruby.JRubyInterpreter
|
Now that Ruby support is in place, let's test it out by adding a Ruby script to your application and invoking it from a Web browser. To illustrate the complete integration of Ruby with Zero, your script will use Zero's GlobalContext API to read request data and create a response. The Ruby code in Listing 4 shows the entire contents of the global context by serializing it within the HTML response. Notice that even though it is using Zero Core's Java APIs, the syntax is pure Ruby.
Listing 4. Ruby script for testing your code
require "java"
include_class "zero.core.context.GlobalContext"
include_class "zero.core.context.SimpleFormatter"
print "<p>The current contents of the global context are:<pre>"
formatter = SimpleFormatter.new
GlobalContext.dump formatter
print formatter
print "</pre></p>"
|
Add the code from Listing 4 to a file named test-ruby.rb in your application's /public directory. You should then start the Zero application with the zero run command and test things out by pointing your favorite Web browser to http://localhost:8080/test-ruby.rb. The result should be a simple HTML page that displays the contents of the global context. Figure 1 shows a screenshot of what the page looks like in Mozilla Firefox.
Figure 1. HTML page created by your Ruby script
Project Zero does not include support for Ruby by default, but that doesn't mean that Ruby programmers have to drop their favorite language to use this new platform. Developers who are experienced with Ruby on Rails and considering a move to Zero can ease that transition by using the techniques in this article to keep their Ruby skills relevant while still taking advantage of Zero's new paradigms. Ruby developers can use the code that accompanies this article and tweak it as they see fit.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for this article | wa-pz-rscript.zip | 4KB | HTTP |
Information about download methods
Learn
-
Browse the developerWorks Web development zone to find tools, code, and resources to get you started developing Web 2.0 applications today.
-
The developerWorks Ajax resource center is packed with information for all skill levels to help you build Ajax into your applications and dramatically improve your user's Web experience.
Get products and technologies
-
Download Project Zero and start applying the best practices covered in this article.
-
Download JRuby 1.0 to build and run the code included with this article.
-
Download the sample project created in this article, including the JRuby integration code and test files.
Discuss
-
Participate in the projectzero.org discussion forum.

Dan Jemiolo is an Advisory Software Engineer on IBM's Project Zero team in Research Triangle Park, NC. He is currently working on reusable components for the Zero platform and its service catalog. His previous work includes the design and development of Apache Muse 2.0 and participation in OASIS Web services standards bodies. Dan came to IBM three years ago after earning his Master of Science degree in Computer Science from Rensselaer Polytechnic Institute.
Comments (Undergoing maintenance)





