Beyond Java/Ruby on Rails

From WikiContent

< Beyond Java(Difference between revisions)
Jump to: navigation, search
(Initial conversion from Docbook)
Current revision (17:12, 17 March 2008) (edit) (undo)
(Initial conversion from Docbook)
 
(One intermediate revision not shown.)
Line 21: Line 21:
Coauthor of ''Better, Faster, Lighter Java''
Coauthor of ''Better, Faster, Lighter Java''
-
[[Image:Beyond Java_I_7_tt95.png|]]
+
[[Image:Beyond Java_I_7_tt95.png]]
''Justin Gehtland is the co-founder of Relevance, a consulting/training organization based in Durham, North Carolina. He's the coauthor of the Jolt-winning book'', Better, Faster, Lighter Java, and has ''been developing applications of all sizes since the early 1990s. Over the last six years, he has delivered products using Java, .NET, LAMP, and now, Ruby on Rails''.
''Justin Gehtland is the co-founder of Relevance, a consulting/training organization based in Durham, North Carolina. He's the coauthor of the Jolt-winning book'', Better, Faster, Lighter Java, and has ''been developing applications of all sizes since the early 1990s. Over the last six years, he has delivered products using Java, .NET, LAMP, and now, Ruby on Rails''.

Current revision

Beyond Java

As I screamed uphill toward the 3-foot ledge, the voice inside my head said "Don't fight it. Go for it." Knowledgeable mountain bikers called the move the lunge, but I had neither named nor internalized it yet. My brain rebelled against the completely unintuitive idea that a moving biker could thrust his bike forward near the top of such a ledge and accomplish anything other than a spectacular crash, but I'd seen it work. I hit the ledge with speed and thrust the bike forward by simply pushing on the handlebars, and the bike was over the ledge. On some level, I didn't understand that success was a possibility. Though I was safely on top, I stepped off my pedals anyway—I'd been sure that I would fail. The idea seemed too much like flying by pulling hard enough on your shoestrings. Learning this mysterious lunge would take a while.

Like the lunge, metaprogramming also seems a little unnatural to me. Then again, I've been coding in Java and C for most of my professional career. If you want to experience the power of a framework that uses metaprogramming extensively, Rails is the gold standard.

Contents

The Numbers Game

As a fairly content Java programmer, I really didn't go searching for an alternative. In some ways, Rails found me. Dave Thomas and I speak at the same conference. I taught several sessions on the Spring framework with Hibernate, and I was very happy with my productivity. Of course, compared with EJB, I was very productive. Dave pointed out that even in Hibernate with Spring, you tend to repeat yourself on a fairly regular basis.

I reflected on David's comments. To make a persistent domain model, you need to specify a database table with its fields and indexes, specify an object domain model with a class (repeating yourself) and a field as an attribute (repeating yourself), and add accessors for that field (repeating again and again). Then, you need to build a mapping with the database table (repeating again) and the class name (and yet again). Finally, your mapping must specify each database column and the corresponding database field (repeating each column twice more). Of course, most sane Java developers do not do all of that repeating. They let the tools do most of it for them, but now your programming model dictates your tool set, your development experience, and generates more lines of code to maintain. I came to the conclusion that ORM makes sense when the domain model and object model are sufficiently different, and I decided I'd take the slight productivity hit and be compensated with better performance and the possibilities of better mapping.

A Blinding Flash of Insight

As I've said, I worked with a company that builds safety software for a manufacturing plant. We effectively build a web user interface to manage a complex domain model. We decided to build this application with a lightweight Java stack of Spring, Hibernate, and Web Work. We moved pretty quickly, and were pleased with our progress. We were proud of our accomplishments, because we'd rewritten a one-year Microsoft application in four months with Java. Naturally, as we accumulated a bigger code base, coding began to take longer.

Over time, Dave's arguments nagged at my subconscious. Dave and my business partner had the same conversations. As luck would have it, over the same week, we tried building a part of the application in Rails. I banged out the model and scaffolding in a couple of days. With his stronger HTML experience, Justin got further than I did. He actually implemented the whole application in Rails in four nights. The Rails version shocked us in another way—it was actually faster!

Making the Commitment

Of course, playing with a prototype and getting a customer to switch from tried and proven Java to a relatively unknown framework on an unknown language was an altogether different proposition. I had a conversation with the start-up's owner, and the results surprised me. He jumped at the chance to move. I guess I shouldn't be surprised. We simply couldn't ignore the differences in raw productivity between the frameworks.

In a start-up environment, productivity rules all other concerns. Since you don't often have the resources of your competition, you need to iterate fast. You can get some leverage by working longer hours and cutting bureaucracy. If you can generate an edge with technology, you've got to take that opportunity. In an increasingly competitive global landscape, we'll all need to act more like start-ups. If a framework makes you a mere 120% faster, you might be tempted to stay with a safer language like Java. But if you can be 400% faster or more, I don't think you can ignore the difference.

Remember, my premise is that Java is drifting away from its base. Most of us need to build web applications on relational databases. Language issues are important, but Java's drivers are so focused on hard-core enterprise problems that they're not making any progress on this simple core problem. If Rails doesn't step into this gap, something else will.

Some Numbers

I'm going to give you some performance and productivity numbers based on experience. I recognize the numbers are imperfect, for a whole lot of reasons. In some ways, the deck was stacked against Rails:

  • The Ruby application implemented more customer requirements. By the time Justin realized that his experience was important, he'd implemented some features that never made it into the Java version.
  • Justin was a recognized expert in Java, but had never used Ruby in a project, and had never used Rails. He wrote a Spring book, and he taught two weekend sessions 16 times per year for Hibernate.
  • The Rails framework has some design philosophies that are unfamiliar to Java developers.

More importantly, some of the factors worked against Java in the implementation:

  • The Java code was in no way fully tuned. The Java apps were much harder to tune, so we didn't get as far. We'd only started to look into performance. (The Ruby code was not fully tuned either, but its default implementation performs quite well with only some minor tweaks.)
  • We had already implemented the problem once, so the Ruby implementation had the benefit of some experience. The dramatic difference in application structure tempers this somewhat, but the user interface was nearly identical.
  • Justin did not have a chance to implement all possible tuning scenarios in all possible environments. The Java version used Tomcat on an Apple iBook instead of Resin or something faster. Justin just made a few simple tests.
  • The caching models are fundamentally different, and are far easier to tune on Rails.

Still, with Ruby, we develop faster; we're probably four or five times as productive. Table 7-1 shows the raw productivity metrics. We write less code. There's less code to maintain. With this type of increase in our cycle time, the customer is much happier, and we can better react to last-minute changes. Our test code is every bit as rich, and probably more so.

Table 7-1. Productivity metrics

Metric Java Ruby
  Spring/Hibernate Rails
Time to market 4 months, approximately 20 hours/week 4 nights (5 hours/night)
Lines of code 3,293 1,164
Lines of configuration 1,161 113
Number of classes/methods 62/549 55/126


Table 7-2 shows the performance numbers. They're probably a little more controversial. I'm not trying to show that a Ruby application will always be faster than a Java application. I'm just showing that in this case, Ruby is fast enough, and it took almost no time or experience to get to this point.

Table 7-2. Difference in performance between untuned versions of a Java application after we moved it to Ruby on Rails

Metric(requests per second) JavaSpring/Hibernate RubyRails
User scenario 1 (100 runs)(no preexisting cache) 71.89 75.59
User scenario 1 (100 runs)(with preexisting cache) 80.86 174.39
User scenario 2 (100 runs)(no preexisting cache) 80.86 62.50
User scenario 2 (100 runs)(with preexisting cache) 88.97 1,785.15


To be clear, in no way is Justin claiming that we've done everything possible to tune the Java application. The point here is that tuning Rails to this level was nearly effortless, and tuning the Java examples requires much more skill, time, and effort. The Ruby version is fast enough to meet requirements, with very little additional effort.

The Community Response

When Justin published this experience, followed by supporting data across two blogs,[1] the Java community lashed out with surprising vigor. It's ironic, because Justin was completely honest with his numbers, and he presented performance numbers only after he was challenged by the community. You probably know that backlash will be particularly strong around disruptive technologies. In this case, the backlash may well be justified, because Rails is a credible threat to Java in some important niches, and it's likely to get stronger quickly. If Rails does happen, a whole lot of knowledge can get marginalized in a hurry.

Look, I'm not saying that this data is scientific, thorough, or even broadly applicable to other applications. It just reflects our experience, and as such, it is compelling. It tells me that Rails is productive, is fast enough to get the job done, generates less code, and is much easier to tune. The data does not prove but strongly suggests a few other hints as well. Rails could well be much more productive than Java for a pretty wide class of applications. Rails can handle sophisticated domains with inheritance and relationships. And Rails is often enough to get the job done.

Keep an open mind. Judge for yourself.

Rails by Example

The best way to understand Rails is to see it in action. Go to http://rubyforge.org and download Ruby and RubyGems . (If you use the Windows one-click installer, you'll get RubyGems with that distribution.) If you don't already have one, download a relational database manager, too. I used MySQL. You'll begin to get the Rails experience at install time. RubyGems lets you install Ruby applications and their dependencies. At the command line, type:

    gem install rails -v 0.12.1

Ruby will start the installation process. It goes up to RubyForge (rubyforge.org) and pulls down an index including the appropriate version of Rails and its dependencies. If you were to omit the version number, Ruby would get you the latest stable version. RubyGems will then prompt you for each dependency. Answer "Y," or answer "a" once for all dependencies:

    Attempting remote installation of 'rails'
    Updating Gem source index for: http://gems.rubyforge.org
    Install required dependency rake? [Yn]  Y
    Install required dependency activesupport? [Yn]  Y
    Install required dependency activerecord? [Yn]  Y
    Install required dependency actionpack? [Yn]  Y
    Install required dependency actionmailer? [Yn]  Y
    Install required dependency actionwebservice? [Yn]  Y
    Successfully installed rails, version 0.12.1

You'll notice that RubyGems will then attempt to build the documentation for each of the subcomponents and Rails. And that's it. Rails is installed. You're already getting hints about the approachability of Rails.

Generating a Basic Application

You can now generate a Rails project. Go to your working directory and ask Rails to generate a project called trails:

    rails trails

Ruby creates a full directory structure that will contain your application. There's no guesswork, and all Rails projects will have a consistent format. I'll point out a few important directories:

app
This directory has your application code. You'll see a directory for each component of MVC and a couple of others.
config
This directory will be very light. You'll put in anything that needs special configuration, like the connection parameters for your database. Since Ruby makes excellent use of defaults, your config directory will stay sparse.
script
Your trails app comes with scripts that will help you generate code, and start your application server.

You'll notice a few other goodies as well, but for now, let's use one of the scripts to start Ruby's application server. Change to the trails directory, and type:

    ruby script/server

If things are working, you'll see a server started on port 3000. You can go to http://127.0.0.1:3000/ to make sure things are running. You'll get a Rails welcome message. You just started a development Ruby web server, configured for Rails. If you need to change some properties of the server, you'll just change the script/server script. Notice that Ruby programmers typically do configuration, like this server script, in Ruby scripts. You've already learned that Ruby handles structured data well, without XML. For example, this is the part of the server script that has the configuration options:

    OPTIONS = {
      :port        => 3000,
      :ip          => "0.0.0.0",
      :environment => "development",
      :server_root => File.expand_path(File.dirname(_ _FILE_ _) + "/../public/"),
      :server_type => WEBrick::SimpleServer
    }

This code simply defines a hash map called OPTIONS. The => operator maps keys on the lefthand side to values on the right. Nothing has really happened yet, but you should be paying attention. You've set up a whole lot of infrastructure in a very short time.

Our trails project will collect descriptions of mountain bike trails. We'll start simple, collecting an ID to go with a trail name, description, and difficulty. You'll type the field names once. The Rails metaprogramming features will read the columns from the database and dynamically add properties to your model objects. If you're using MySQL, you can fire up the mysql command processor. Create a database called trails and switch to it. Now, create a table called trails:

    mysql> CREATE TABLE trails (
        ->   id int(6) NOT NULL auto_increment,
        ->   name varchar(20),
        ->   description text,
        ->   difficulty varchar(20),
        ->   primary key (id));
    Query OK, 0 rows affected (0.36 sec)

Notice the names. They are important. By convention, if you're working with more than one row (as in a table or a list), the name should be a plural. A column or class that refers to a singular object should be singular. Rails is smart enough to translate English plurals, so it knows to create a model called Person for a table called people. Watch the capitalization in these examples, too. It's important. If you follow Rails conventions with your names, you can just stay with Rails defaults, and your code will be much more concise.

You'll need to tell Rails where to find your database. Edit config/database.yml to look like this:

    development:
      adapter: mysql
      database: trails
      host: localhost
      username: root
      password: password

Stop and restart the server. (You only have to do so when you change your database configuration.) Let's generate a simple model. In the trails directory, simply type:

    ruby script/generate model trail

Rails generates the model, some helper files, tests, and fixtures. For example, you can take a look at the model. Edit the file at app/models/trail.rb:

    class Trail < ActiveRecord::Base
    end

That certainly looks anticlimactic. It looks like you'll simply type custom code here, in hopes that Rails will generate the rest of the code somewhere else. But that's not what happens at all. At runtime, Rails will load the ActiveRecord base class. Rails will look at the name of the class and load the definition of a table called trails. Then, it will dynamically add attributes, getters, setters, and database access methods to the Trail base class! So, there's a lot more than meets the eye.

One of the scripts that Rails generates lets you manipulate your model from an irb session. Type:

    ruby script/console

You can now easily manipulate your model. For example, you can say:

    Trail.new do |trail|
      trail.name="Walnut Creek"
      trail.description="Meandering trail in Austin park"
      trail.difficulty="hard"
      trail.save
    end

Now, you'll need a controller. You can generate that, too:

    ruby script/generate controller trails
    ruby script/generate model trails

You just created the model and a default controller in app/controllers/trails_controller.rb for a collective page of trails. When you edit it, the controller is empty. Make it look like this:

    class TrailsController < ApplicationController
      def index
        render_text "This will be a trail someday..."
      end
    end

Point your browser to the URL http://localhost:3000/trail. You'll see your message printed. Of course, you didn't learn Rails to print strings, so change your controller to this:

    class TrailsController < ApplicationController
      scaffold :trails
    end

scaffold is a method. The first parameter is :trails, a literal pointing to the Trails class. Save it, and load the same URL. Now, that's more interesting. You see a listing of trails. Click on the new trail link on the bottom. That's beyond interesting! You'll get a form like the one shown in Figure 7-1. You can see that the metaprogramming framework is working overtime. The scaffold method inferred the properties of the database and propagated them through the model and up to the user interface. You'll see exactly what goes into a scaffold controller later, but trust the magic for now.

Managing Relationships and Updating Views

A list of trails will not get you very far by itself. It's the interactions between objects that gets difficult. Say you want to access trails by their city. The first job is to generate the model for locations. First you'll need a database table:

    mysql> CREATE TABLE locations (
        ->   id int(6) NOT NULL auto_increment,
        ->   city varchar(20),
        ->   state varchar(20),
        ->   primary key (id));
    Query OK, 0 rows affected (0.35 sec)

Instead of dynamically generating the scaffolding, you can simultaneously generate the source code for the controller, and view, complete with scaffolding with ruby script/generate scaffold locations. Build the model for a single location with ruby script/generate model location. While you're at it, just to get a better look at what's going on behind the curtains, do the same for Trail with ruby script/generate scaffold trails. Look at what you've done by pointing your browser to http://localhost:3000/locations. Make sure it works, and add a few locations. I'm adding Austin, Texas, and Durango, Colorado.

It's time to write some code ourselves, instead of letting Rails do all the work. You're going to need to update your trails table to point to the right row in the new locations table. You'll do so by adding a new database column that points to location_id, like this:

    alter table trails add location_id int(6);

You also need to tell Rails about the relationship. Modify the Trails model and the Location model to reflect the new relationships, like this:

    class Trails < ActiveRecord::Base
      belongs_to :location
    end

    class Locations < ActiveRecord::Base
      has_many :trails
    end

Figure 7-1. This application has less than 10 lines of code and configuration, because Rails inferred the structure from the database

This application has less than 10 lines of code and configuration, because Rails inferred the structure from the database

A little description here is interesting. You've created a subclass of the ActiveRecord class in the Base module. You've then fired a method called belongs_to and passed it a symbol for the Locations class. This method will fire more metaprogramming code that actually adds the properties and methods to your code that will manage the relationships for you.

Next, you're going to have to edit the Trails view and controller to edit a location. The scaffolding created the new controllers and views under trails and locations, respectively.

It's time to modify some of the view code. The view code consists of HTML, with Ruby scripting mixed in, between the <% and %> tags. First, you'll need to make sure the view has all the information it needs. You'll do this in the edit method, in the controller. Change the edit method in trails_controller.rb to create a property called @locations that has all the locations:

    class TrailsController < ApplicationController
    ...
      def edit
        @trail = Trail.find(@params[:id])
        @locations = Location.find_all
      end
    ...
    end

It's also time to take over the full view that lets you edit a trail. You'll want the user to pick a location from a pick list with all possible locations. Change app/views/trails/edit.rhtml to look like this:

    <html>
     <head><title>Edit a Trail</title></head>
     <body>
       <h1>Edit Trail</h1>

     <form action="../update" method="POST">
      <input id="trial_id" name="trail[id]" size="20"
             type="hidden" value="<%= @recipe.id %>" />
      <p><b>Name</b><br>
      <input id="trail_name" name="trail[name]" size="20"
             type="text" value="<%= @trail.name %>" />
      </p>
      <p><b>Location:</b><br>

      <%= collection_select("trail", "location_id", @locations, "id","city") %>

      <p><b>Description</b><br>
      <textarea cols="40" id="trail_description"
                name="trail[description]"
                rows="20" wrap="virtual">
        <%= @trail.description %>
      </textarea> </p>
      <input type="submit" value="Update" />
     </form>

     <a href="/trail/show/<%= @trail.id %>">
       Show
     </a> |
      <a href="/trail/list">
      Back
     </a>

     </body>
    </html>

As with most applications, your scaffolding won't hold up infinitely. Often you'll want to replace most of the view code. Rails lets you build toward the goal, instead of creating all of a model, view, and controller right off the bat.

Notice the code in bold. It adds an option value for all the locations (which you specified in the edit method of the controller), and selects the one that matches the one that reflects the model, shown in the variable trails.location.city.

Finally, you'll need to show the new data in the trail list, and in the show method. The idea is exactly the same. Add a line to the show.rhtml view right above the links on the bottom of the page:

    <p>
      <b>Location:</b> <%=h @trail.location.city %>
    </p>

That's pretty simple. You're just getting the location from the model passed in by the controller. The list view uses the same technique. You can edit the table from the app/views/trails/list view:

    <table>
      <tr>
        <th>Name</th>
        <th>Location</th>
      </tr>

    <% for trail in @trails %>
      <tr>
        <td><%= trail.name %></td>
        <td><%= trail.location.city %></td>
        <td><%= link_to 'Show', :action => 'show', :id => trail %></td>
        <td><%= link_to 'Edit', :action => 'edit', :id => trail %></td>
        <td><%= link_to 'Destroy', {:action => 'destroy', :id => trail}, :
    confirm => "Are you sure?" %></td>
      </tr>
    <% end %>
    </table>

Figure 7-2 shows you the result, with the location of each trail in the main list. Keep in mind that all trails have to have locations. If one of yours doesn't, you will get an error here.

This tutorial has already gone on long enough, but I hope you can appreciate the power and flow of Rails development. You can quickly get your application rolling, because Rails discovers your application structure from

Figure 7-2. This list comes from an application that allows you to view and update a database, with trails in one table and locations in another

This list comes from an application that allows you to view and update a database, with trails in one table and locations in another

the database design. You then turn changes around quickly, because the feedback cycle requires you only to code/reload. You're building quality beyond what PHP can give you, because you're building with a proven model/view/controller design pattern, with built-in features for logging, caching, and automated testing. Now that you've seen what Rails can do, take a look under the hood to see some of this magician's secrets.

Under the Hood

As you've seen, the Rails framework is also made up of several existing frameworks, including Active Record, Action Pack, and a few others. Active Record handles relational database access. Action Pack processes requests, and manages the model/view/controller separation. Rails provides the integration and the rest.

Active Record

Active Record implements the Active Record design pattern by Martin Fowler in Patterns of Enterprise Application Architecture (Addison Wesley). It's effectively a wrapper around a database table, with domain logic built into the wrapper. The Rails implementation adds two important innovations: you can do inheritance and manage relationships. These are some of the major features.

Automatic properties

Active Record automatically adds properties, with accessors, to model objects. It also adds methods for simple CRUD database methods automatically. For example, in the view you just wrote, the view accesses the name property in Trail, though the root model was empty:

    class Trail < ActiveRecord::Base
    end

Association management

Rails uses methods to add methods that manage associations, automatically. You saw this example where a location has many trails:

    class Location < ActiveRecord::Base
      has_many :trails
    end

As you have seen, has_many is a method, and :trails is a symbol, in this case, for the Ruby class Trails.

Composition

You can use Active Record to compose objects from multiple tables, like this:

    class Location < ActiveRecord::Base
      composed_of :street, :class_name => "Street",
                  :mapping => %w(street name)
    end

Inheritance

Inheritance works, putting all subclasses in a single table with the parents:

    class Product < ActiveRecord::Base
    end

    class Bike < Product
    end

Other features

Of course, a full Active Record discussion is beyond the scope of this book, but these are some of the other features you can use. You can build recursive relationships, like trees. You can use Active Record to validate certain types of rules (for instance, there must be an existing location for a new trail). Active Record can notify an email address when some significant event happens.

Active Record also has good plumbing. It supports transactions and error logging. You can look at the metadata for the columns for a table, and support multiple database types. It also provides support that makes it easy for you to build test fixtures. Active Record is a powerful framework and a credible competitor to Java's ORM frameworks.

Action Pack

Action Pack deals with requests in two parts: the controller and the view. Requests come into Action Pack through a dispatcher. The dispatcher routes the request to a controller, which invokes any model logic and sends the request to a template-driven view system. The template engine fires the Ruby template, which may execute Ruby code, and returns the resulting HTML to the browser. The flow, shown in Figure 7-3, is reminiscent of Struts. There are a few differences. For example, the controller has a group of actions, instead of encapsulating each action in a different class. If you wanted to refactor, you'd let actions share methods.

Figure 7-3. Ruby on Rails is actually made up of several existing frameworks, most notably Active Record and Action Pack

Ruby on Rails is actually made up of several existing frameworks, most notably Active Record and Action Pack

The Action Pack splits the request into a controller part and a view part. With Rails, a whole lot happens automatically. In some ways, that's bad. You can't see all the methods or the attributes on your class, and you don't even know what they are unless you look at the database. In other ways, it's a highly productive way to work. You can change your model, schema, and view in many cases just by adding columns to the schema. Let's take a fuller look at the capabilities of Action Pack.

Capabilities

Action Pack goes beyond simple request processing. It contains many capabilities that make it easier to develop web applications. I'll touch on some of the major capabilities here.

As you've seen, Action Pack uses Ruby as the scripting language. Java developers frown on embedding Java into a JSP, but I'd suggest that code will be in the view regardless of whether it's in Ruby. Early on, some vocal zealots overreacted to the early proliferation of Java scriptlets and decreed that MVC means "no code on the page." Many Ruby developers believe that code that is concerned with the view (and only the view) does belong on the page. Burying Java code in a custom tag only complicates and confuses the issue.

Ruby provides a far friendlier scripting language than JSTL tags, for example. Like servlets, Action Pack lets you attach filters for things like authentication. Action Pack also handles some convenience design elements, like automatically paginating your result sets and providing navigation links.

Action Pack also has some features that make it easier to build components, like helper classes (to render a date, for example), a layout sharing feature (similar to Tiles, if you're familiar with Struts), intracomponent communication, and pretty good Ajax integration. Like Struts and Spring, Action Pack provides good support for building and validating forms.

You'll need to manage your solution, and Action Pack builds in some features to help. It enables logging, caching at three levels (page, action, and fragment), and benchmarking support. Developers can use integrated support for unit testing and debugging. It's not as powerful as Struts in some ways, but it's much simpler, and highly customizable.

The Essence

So, Rails is not a toy, and it's not a gimmick. In my opinion, Rails represents a significant advance in the state of the art. You've probably seen frameworks like this one solve the database-with-UI problem in several different ways:

  • Object-oriented development frameworks are flexible and robust. They're usually at a lower abstraction level, so they may not be as productive. You can use them to create flexible, robust, and powerful applications, but you're going to pay for it with productivity.
  • Quick compromise frameworks trade conventional wisdom and sound design for implementation speed. PHP and Visual Basic, for example, compromise by trading design wisdom (frameworks should encourage separation of model and view logic) for development speed.
  • Code generation frameworks generate most of the code for such an application at compile time. They trade the feedback cycle, easy maintenance, and often, customization, for speed.
  • Customization point frameworks take a few parameters, like database tables or models, and build default implementations with a few well-defined anticipated customization points. These frameworks break down when the inventor's imagination doesn't anticipate important hook points.

Rails is none of these. It uses macros to help you quickly generate code based on the structure of the database and a few well-placed macros. Since you effectively get generated code at runtime without tangled source code, you don't have to maintain the added code. Rails avoids the trap of customization points through Ruby's extensive hook points. You start with a clean design. You can then extend it through subclassing, changing class definitions, or any of the other metaprogramming techniques we discussed. You can even replace major Rails components like Active Record.

Rails accelerates your development through meaningful conventions and defaults. By guiding your naming strategies in the database, Rails can save you lots of typing, and infer your intent by the consistent names that you provide.

Rails keeps development convenient by providing the scripts and debugging tools that you need to do the job right. You can run the server from a script, manage your active record classes and the database tables behind them from a console, use generated test fixtures, or run performance tests from generated scripts.

In Hackers and Painters (O'Reilly), Paul Graham suggested that great tools for good programmers are built by programmers to solve their own problems. I think he's on to something. Maybe Rails is so good because the authors built it to solve their own real-world problems first. As you've seen, Rails was created to help build the popular Base Camp and Back Pack projects.

Is Rails the Killer App?

Is Rails the catalyst that will take us beyond Java? I'm not sure. Ruby does not have strong commercial backing. There's no JVM implementation that will yet run Rails, and the existing project has had some false starts. Ruby doesn't have the rich frameworks or name recognition of Python and Java. But it is an important advancement in productivity, in an important niche. And unlike Python, Groovy, and Lisp, Rails has generated an incredible buzz in the Java community right now. Something like Rails may be what eventually replaces Java in this web development niche.

Or Is Metaprogramming the Killer Technique?

On another level, Rails may use a killer technique. Rails is one of the first commercially successful demonstrations of metaprogramming in Ruby, in combination with meaningful defaults. Let's dive a little deeper into metaprogramming.

In some ways, this programming technique reminds me of another buzzword, the domain specific language (DSL) . A DSL solves a domain problem with a language whose syntax and keywords match ideas in the domain. Look over Active Record again. That framework lets you express ideas about the relationship between a database and a model, with keywords for ideas like inheritance, relationships, and name mappings.

Rails may be the application that breaks the dam. Some of my mentors, like Stuart Halloway and Glenn Vanderburg, speak often about the importance of these techniques. By showing what's possible in Ruby, Rails may release a massive wave of metaprogramming frameworks custom built for a given domain. If we do see such a wave, it likely won't be in Java, because reflection is just too painful, and the wild mix of primitives and objects simply makes it too cumbersome.

Final Thoughts on Ruby and Rails

To me, Ruby smells and feels like a good language, and Rails feels special. That alone is not enough to make it succeed. In this industry, individuals often make the difference, and the Davids (Thomas and Hansson) may be special enough to take this language and framework into the mainstream. Dave Thomas is a tireless promoter of all things pragmatic, and he seems to be focusing his publishing business on Ruby. He's already locked down many of the top Ruby authors by treating them well and providing a publishing experience that larger publishers cannot duplicate. Printed books provide credibility and exposure that languages need to succeed. David Heinemeier Hansson has a unique combination of a technical vision, a flair for understanding the end user, and a zest for marketing that you rarely find in one person. Rails is at once theoretically sound enough to attract hard-core web developers, and approachable enough to attract the masses.

This kind of leadership often makes the difference between a successful technology, and a good technology that failed or never hit the mainstream. You don't often find technical passion and marketing vision wrapped up in a single mind, but when you do, great things can happen. Bill Gates built Microsoft from a BASIC shop operating out of a garage to the biggest software company in the world. Steve Jobs made Apple cool, left, and came back to overhaul its image and bring it back. Java, too, is full of technical visionaries. James Duncan Davidson fought the bureaucracy in Sun to break servlets into the mainstream by open sourcing Tomcat, and then did it again with Ant.

Java seems to be losing the visionaries and technologists that I respect the most. Glenn Vanderburg may pay some of his bills with Java, but his public persona now spends more time in the Smalltalk (Seaside) and Ruby communities, because of his interest in metaprogramming. James Duncan Davidson left the Java community years ago to focus on the Apple operating system, primarily on Objective C. Many of those who remain seem to be here because Java pays the bills.

Ruby and Rails seem to be going in the other direction. Increasingly, Rails finds itself in the middle of controversy. You've probably heard all the arguments:

  • Can it possibly scale?
  • Is it ready for the enterprise?
  • What will you do without all of those Java programmers and libraries?
  • Isn't Rails a toy?
  • Do you really want to run your business on a scripting language?

In the first half of 2005, I saw more than two dozen blogs attacking Rails. Some of the arguments are valid. Java can do some things that Ruby can't, yet. Other arguments are born out of ignorance or misconceptions. I'm intrigued, because more and more in the Java community are paying attention. The Davids certainly get the Rails message out there. We're about to see whether that spotlight will provide energy for growth, or a sweltering, destructive, withering heat.

Notes

  1. Justin Gehtland, Weblogs for Relevance, LLC (April 2005); http://www.relevancellc.com/blogs. I *heart* rails; Some Numbers at Last.
Personal tools