Ruby, the full-featured object-oriented dynamic (scripting) language, with strong support for functional programming and metaprogramming, has drawn attention recently for its flexibility and ease of development. JRuby, a JVM-based interpreter for Ruby, combines the ease of the Ruby language with execution in the powerful JVM, including full integration to and from Java libraries.
Since my previous JavaWorld article on the topic ("JRuby for the Java World"), there have been some exciting developments for JRuby. Sun Microsystems hired the two lead JRuby developers, Charles Nutter and Thomas E. Enebo, in a sign of support for Ruby in the JVM. Java Platform, Standard Edition 6 (Java SE 6) was released with a new standard API for plugging in interpreters for dynamic languages. Plans are firming up for the Java 7 VM to support dynamic languages directly with a new "invoke dynamic" bytecode and hot-swapping of class definitions at runtime. Meanwhile, the JRuby team has released version 0.9.2 with broader support for Ruby on Rails, and the next big release of JRuby, expected in February, will include full support for Ruby on Rails.
Ruby on Rails, an easy-to-use but powerful Web framework built on the Ruby language, has rapidly gained popularity for new database-backed Web applications, especially in the Web 2.0 world. I'll refer you elsewhere for details of Ruby on Rails, also called Rails. Even though the project is only 3 years old, plenty of articles and books have been written about it, and its documentation is outstanding for an open source project (see the Ruby on Rails Web site). Likewise, I refer you to my earlier article for an introduction to JRuby.
In this article, I examine the junction between Rails and Java. I compare the Rails and Java Web frameworks, describe the benefits of running Rails with JRuby, and review some lessons that a Java developer — even one who does not use Rails — can learn from this innovative framework.
Power plus simplicity
Rails radically speeds and simplifies the development of Web applications, but it suffers from an image of immaturity, especially in high-end enterprise-strength capabilities.
On the other hand, the Java platform, with its virtual machines, libraries and application servers, has been gaining in speed, stability and functionality, to the point where it is generally considered the leading platform for high-end server applications. Yet as long as it remains tied to the Java language, the Java platform risks falling behind as newer languages gain popularity.
JRuby ties together the complementary strengths of all these technologies, promising added popularity for both Ruby and Rails, while giving the Java platform a new role in running non-Java languages.
Rails: Where Java frameworks are heading
To a Java developer, Rails seems like the natural culmination of trends in the evolution of Java Web frameworks: less unnecessary code, more abstraction and dynamism, and fuller out-of-the-box functionality.
Convention over configuration
Early versions of Java Platform, Enterprise Edition (Java EE) required extensive configuration and code for each component. Enterprise JavaBeans, for example, had multiple source-code and XML configuration files for each bean. This complexity turned EJB into a byword for heavyweight development, and eventually led to a 180 degree turn in EJB 3, which aims for POJO (plain-old Java objects) beans with minimal redundancy and configuration. Even so, heavyweight Java EE applications still require developers to develop code to express the same business objects across multiple software tiers — GUI, business logic and persistence. Then, despite the redundancy and similarity between the layers, developers must glue the layers together with configuration files. In contrast, the newer Java Web frameworks Seam and Spring expose business objects with much less configuration and code.
Java frameworks have also been moving towards standardization and integration of a stack across the tiers of a Web application. In the earliest days, Java Web application developers hand-coded HTML output from servlets, created their own Model-View-Controller architectures, and accessed their databases with SQL over Java Database Connectivity (JDBC). Later, they gathered components to execute much of the common functionality, such as tag libraries, Struts and Hibernate. Recently, Spring integrated much of the functionality in a single top-to-bottom lightweight stack.
From the start, Rails has embodied these principles of simplicity, principles known to the Rails community as "Don't Repeat Yourself" and "Convention over Configuration." (Non-redundancy and meaningful defaults are among the oldest principles of software engineering; it's a wonder that we have had to wait so long for something like Rails.) The framework guesses the connection between different tiers based on straightforward conventions. For example, there is no need for XML, annotations or the like, to tell the framework that the customer class is backed by the
customers table; Rails' ActiveRecord database-wrapping layer guesses this (while taking into account pluralization and capitalization). Rails goes so far as to implicitly and dynamically add attributes to reflect database columns: a
last_name column automatically brings a
last_name attribute into being.
In special cases, where the conventions don't meet your needs, you can still add configuration, using pure Ruby code or the lightweight Ruby-like YAML format, both of which omit XML's redundant brackets and closing tags. But you should stick to the defaults where possible. Rails is "opinionated software," which makes it far easier when you go with the flow.
Rails is a "batteries included" framework (a phrase popularized by Python): it includes everything you need for a standard database-backed Web application, from data-access layer, through model, view, and controller. It lets you focus on what's specific to your application, instead of recoding common functionality or searching for open source libraries that integrate well together.
Dynamism and reflection
Java frameworks have also been moving towards greater use of reflection and metaprogramming. Spring, for example, uses reflection to plug all of its pieces together with dependency injection, in contrast to the more static approach of the standard Java EE server stack. Hibernate, the popular object-relational mapping framework, does its mapping with dynamic metaprogramming, updating the bytecode at runtime, in contrast to early data-access frameworks, which required cumbersome source-code or bytecode generation at development time.
Hibernate's developers had to use some advanced techniques to accomplish this functionality, but in Ruby, metaprogramming is such a natural part of the language that Rails, at runtime, dynamically generates not only mappings, but also the business-layer class definitions needed to access and display the underlying database, thereby minimizing the need for manual coding or creation of inflexible generated code.
Supporting the development process
Around the end of the 1990s, Java programmers got "test infected" with the JUnit frameworks, but writing tests for server-side applications has always been difficult. Spring now generates tests together with the Web application. Rails does the same, taking advantage of dynamism and metaprogramming to support multiple types of tests: unit tests, which exercise the individual methods of the model classes; functional tests, which work on the level of the individual Web request; and integration tests, which run a series of Web requests in a simulated user session.
The popular Ant and Maven tools standardized the automation of builds in Java. Rails, too, makes builds easy with Ruby's rake build tool; it adds an innovative migration system, which automates the upgrade (or rollback) of database schemas and data.