Rapid prototyping with Apache Derby and JRuby on Rails

Ruby on Rails has raised the bar in terms of rapid development of data-driven Web sites. The JRuby project is making Ruby faster and more scalable than ever. One of the great advantages to running Rails on the Java™ Virtual Machine is that you can leverage other Java libraries, like the Apache Derby embedded database. The combination of Derby, JRuby, and Rails allows for rapid prototyping of dynamic Web applications. Learn how to use these technologies together to help you prototype your next great idea.

Michael Galpin (mike.sr@gmail.com), Software architect, eBay

Michael Galpin's photoMichael Galpin has been developing Java software professionally since 1998. He currently works for eBay. He holds a degree in mathematics from the California Institute of Technology.



26 August 2008

Also available in Japanese

System requirements

This article makes use of several cutting-edge technologies to allow for rapid Web development. Links for all of these can be found in the Resources section. We use JRuby V1.1.3 and Apache Derby, which are Java-based technologies and require the Java Development Kit (JDK) V1.4 or higher. It is highly recommended that you use a V1.6 JDK because Apache Derby is bundled with JDK V1.6. If you use a V1.4 or 1.5 VM, you can download Derby separately. V10.4.1.3 was used in this article, in combination with a V1.5.0_13 JDK. This article also uses Rails V2.1. Prior knowledge of Ruby and Ruby on Rails is assumed to get the most from this article.


Setup

Many modern Web application frameworks emphasize developer productivity, which is good because developer time is precious, right? However, one of the things often missed is setup time, including how complicated it is to go from a clean machine to a place where a developer can write and run code. This is not just important to a developer sitting at home playing around with a new technology but also to organizations that frequently hire new developers and want to see quick returns on their investments. Rails excels in this area — and with JRuby and Derby, things get even better.

Installing JRuby

We assume that you've installed the V1.6 JDK. JRuby needs to know where your JDK is, so it follows the common convention of looking for an environment variable called JAVA_HOME that points to your JDK. You should also make sure that $JAVA_HOME/bin is in your path. Now you just need to download JRuby and unzip it to wherever you like. It is recommended that you create an environment variable for this location — call it JRUBY_HOME— and put $JRUBY_HOME/bin on your path, as well. That's all you need to do for JRuby, but what about Rails?

Installing Rails

JRuby is a 100-percent implementation of Ruby that simply uses Java for its implementation instead of native code. It has everything that Ruby has and that includes gems. To install Rails, you simply use gems, as shown in Listing 1.

Listing 1. Installing Rails with gems
$ jruby -S gem install rails
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Successfully installed activesupport-2.1.0
Successfully installed activerecord-2.1.0
Successfully installed actionpack-2.1.0
Successfully installed actionmailer-2.1.0
Successfully installed activeresource-2.1.0
Successfully installed rails-2.1.0
6 gems installed
Installing ri documentation for activesupport-2.1.0...
Installing ri documentation for activerecord-2.1.0...
Installing ri documentation for actionpack-2.1.0...
Installing ri documentation for actionmailer-2.1.0...
Installing ri documentation for activeresource-2.1.0...
Installing RDoc documentation for activesupport-2.1.0...
Installing RDoc documentation for activerecord-2.1.0...
Installing RDoc documentation for actionpack-2.1.0...
Installing RDoc documentation for actionmailer-2.1.0...
Installing RDoc documentation for activeresource-2.1.0...

Notice the only real difference between using JRuby and native Ruby is that the gem command is prefixed with a JRuby -S. This simply tells JRuby to first look for the script in the $JRUBY_HOME/bin directory. This makes sure that you get JRuby's gem script.

It is possible to have JRuby use an existing gem repository (i.e., share a gem repository with a (native) Ruby installation). All you need to do is set an environment variable. However, this is not advised. Most gems are written in Ruby and are compatible with JRuby. However some are written in C++, and these are not compatible with JRuby. Similarly, some JRuby gems are written in the Java language and are not compatible with native Ruby.

Running the above command will take a while, and it will be largely dependent on your network connection speed. Rails comes with a Web server, WEBrick, that is certainly not a production-quality Web server, but is perfect for rapid prototyping and development. Now we just need a database — namely, Apache Derby.


Installing Apache Derby

Rails uses the ActiveRecord library to handle database access and object-relational mapping between database tables and Ruby object models. This is one place where things are a little different because we are using JRuby instead of Ruby. In the Java language, we have the Java Database Connectivity (JDBC) API for communicating with the database, and we want to leverage this in Rails. So we need an additional gem that will allow ActiveRecord to use JDBC. This includes database-specific information, so we need to install the gem specific to Derby.

Listing 2. Installing ActiveRecord-JDBCgem for Derby
$ jruby -S gem install activerecord-jdbcderby-adapter
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Successfully installed activerecord-jdbc-adapter-0.8.2
Successfully installed jdbc-derby-10.3.2.1
Successfully installed activerecord-jdbcderby-adapter-0.8.2
3 gems installed
Installing ri documentation for activerecord-jdbc-adapter-0.8.2...
Installing ri documentation for jdbc-derby-10.3.2.1...
Installing ri documentation for activerecord-jdbcderby-adapter-0.8.2...
Installing RDoc documentation for activerecord-jdbc-adapter-0.8.2...
Installing RDoc documentation for jdbc-derby-10.3.2.1...
Installing RDoc documentation for activerecord-jdbcderby-adapter-0.8.2...

If you are a veteran of Rails, you probably realize that the above is not much different from native Ruby. Rails always needs a database-specific adapter. However, Rails comes bundled with the adapters for MySQL, PostgreSQL, and SQLite. If you have used Rails with another type of database, such as Oracle or IBM® DB2®, you have done something similar to the above.

As mentioned, with JDK V1.6, Derby is already bundled and you are ready to code (or generate code). If you are using JDK V1.5 or V1.4, you have a couple more steps. You need to download Derby and unzip it, then find the derby.jar and copy this to $JRUBY_HOME/lib. Now you have caught up to those using JDK V1.6 and may proceed.


Create an application

Ruby on Rails practically invented the term "convention over configuration," and it makes it so easy to create an application. Using JRuby does not affect this equation at all. You get to use the Rails generators to rapidly create your application stub.

Listing 3. Generating an application
>rails deadly -d sqlite3
      exists  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      create  config/environments
      create  config/initializers
      create  db
      create  doc
      create  lib
      create  lib/tasks
      create  log
      create  public/images
      create  public/javascripts
      create  public/stylesheets
      create  script/performance
      create  script/process
      create  test/fixtures
      create  test/functional
      create  test/integration
      create  test/unit
      create  vendor
      create  vendor/plugins
      create  tmp/sessions
      create  tmp/sockets
      create  tmp/cache
      create  tmp/pids
      create  Rakefile
      create  README
      create  app/controllers/application.rb
      create  app/helpers/application_helper.rb
      ...

Much of the output has been deleted, as this is all typical Rails output. One thing you will notice is that we used -d sqlite3, which tells the generator script to use SQLite as the database. This is an embedded database similar to Derby, and it is included with Rails V2.0 or higher, making it a "safe" option here. We will set up the Derby configuration in the next section. Just like with any Rails application, you can go ahead and start it up.

Listing 4. Starting the application
=> Booting WEBrick...
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2008-07-25 17:18:31] INFO  WEBrick 1.3.1
[2008-07-25 17:18:31] INFO  ruby 1.8.6 (2008-05-30) [java]
[2008-07-25 17:18:31] INFO  WEBrick::HTTPServer#start: pid=363524275 port=3000

And just like any other Rails application, we can bring it up in a browser by going to http://localhost:3000, as shown below.

Figure 1. Running the Rails application
Running the Rails application

This is all standard Rails fare. As the welcome screen suggests, it is time to set up a database so we can start adding something to our application. Of course, this is where we specify that we are using Derby. Look at Listing 5 and see what a Derby configuration for Rails looks like.

Listing 5. database.yml
development:
  adapter: jdbc
  driver: org.apache.derby.jdbc.ClientDriver
  url: jdbc:derby://localhost/deadly_development;create=true
  username: app
  password: app
  
test:
  adapter: jdbc
  driver: org.apache.derby.jdbc.ClientDriver
  url: jdbc:derby://localhost/deadly_test;create=true
  username: app
  password: app
  
production:
  adapter: jdbc
  driver: org.apache.derby.jdbc.ClientDriver
  url: jdbc:derby://localhost/deadly_production;create=true
  username: app
  password: app

There are a few things you will want to notice about this configuration. First, the adapter is just JDBC. Typically, you would specify a database-specific adapter here, like MySQL or Derby. Instead, we will go through a JDBC-based adapter. The URL specifies that we are using Derby. Notice the create=true parameters that will tell Derby to create the database if it does not already exist. Finally, notice the username and password. These are simply the defaults for Derby. Now that the database connections are configured, it's time to write some code. Well actually, it's time to let Rails write some code for us.


Scaffolding

If you have used Rails, chances are you have used its scaffolding or at least seen it in action. It is probably the main reason for the "wow" factor people associate with Rails, and why it has been adopted and copied by countless other frameworks. Most Rails experts are quick to tell you that scaffolding is not meant to be a production feature, but its usefulness for prototyping is unquestionable. It has evolved over the years, especially in Rails V2.0. Let's take a look at the generation command we will use in Listing 6.

Listing 6. Generating scaffolding
>script/generate scaffold boat name:string captain:string crew:integer capacity:integer
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/boats
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      exists  public/stylesheets/
      create  app/views/boats/index.html.erb
      create  app/views/boats/show.html.erb
      create  app/views/boats/new.html.erb
      create  app/views/boats/edit.html.erb
      create  app/views/layouts/boats.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/boats_controller.rb
      create  test/functional/boats_controller_test.rb
      create  app/helpers/boats_helper.rb
       route  map.resources :boats
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/boat.rb
      create    test/unit/boat_test.rb
      create    test/fixtures/boats.yml
      create    db/migrate
      create    db/migrate/20080727053100_create_boats.rb

This generates an ActiveRecord model, a controller with an appropriate route mapped to it, and several view templates. It also creates an initial database-migration script based on the parameters in the command (the name-value pairs corresponding to attributes of the model and the types of those attributes). To create our database, we simply use Rake to execute the database migration, as shown below.

Listing 7. Using Rake to create a database table
>rake db:migrate
(in /Users/michael/code/aptana/workspace/deadly)
== 20080727053100 CreateBoats: migrating ======================================
-- create_table(:boats)
   -> 0.0395s
   -> 0 rows
== 20080727053100 CreateBoats: migrated (0.0407s) =============================

The database and the boats table are created at once, just like that. Notice that we did not have to start up a database server. When the JDBC driver tries to connect to Derby, it starts Derby and creates the database, the script creates the table. This is not just some transient in-memory database; it is persisted to disk. We can fire up the application again and the table will be there. If we go to http://localhost:3000/boats, we should see something like Figure 2.

Figure 2. Boats scaffolding
Boats scaffolding

Click around and create some boats. Everything will be stored in the Derby database. You can stop and restart the server, reboot your machine, or whatever. When you start the server again, all of your data will be there. Scaffolding can seem mysterious to people, but it's not. It is mostly just generated code you can modify. Next, let's take a look at a simple example of modifying scaffolding code.


Custom scaffolding

Prototyping lends itself well to many Agile development methodologies. You can get something up and running quickly, then get immediate feedback from product managers, users, etc. For example, we could hand off the scaffolding-based prototype to a product manager, who could then explore the application. After they had used it for awhile and added some boats, perhaps it would look like Figure 3.

Figure 3. Boats scaffolding with test data
Boats scaffolding with test data

Listing 8 shows the index method of the BoatsController class.

Listing 8. The index method
class BoatsController < ApplicationController
  # GET /boats
  # GET /boats.xml
  def index
    @boats = Boat.find(:all, :order => "capacity DESC")

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @boats }
    end
  end
end

There are actually five more methods (show, new, edit, create, update, and destroy), but we did not need to modify those. Actually, all that was changed here is the first line in the index method: the finder query. The original was just Boat.find(:all). Here, we simply added an order clause. You can make this change and refresh the browser to see the changes, as shown below.

Figure 4. Boats list, ordered by capacity
Boats list, ordered by capacity

Of course, there are many more customizations possible. You could let the user pick what to sort by, change the look and feel, etc. The point is that scaffolding code is not only useful for prototyping by itself, but it can be easily modified to allow for feedback as well. Still, there are limits to what is appropriate for scaffolding. Sometimes you will need to prototype various models and controllers that would not come from scaffolding. Luckily, Rails has more generators that can help with this, too.


More generators

Let's say we want to create a catch model that represents a catch made by a boat. There is a one-to-many relationship between a boat and catches. We will start with another generator for the new model, as shown below.

Listing 9. Generating the catch model
>script/generate model catch quantity:integer
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/catch.rb
      create  test/unit/catch_test.rb
      create  test/fixtures/catches.yml
      exists  db/migrate
      create  db/migrate/20080727060346_create_catches.rb

Notice that we only specified a quantity attribute (column) in the generation command. We have to do some hand editing to add the relationship. First, we modify the migration script that was generated.

Listing 10. The CreateCatches migration script
class CreateCatches < ActiveRecord::Migration
  def self.up
    create_table :catches do |t|
      t.integer :quantity
      t.integer :boat_id
      
      t.timestamps
    end
  end

  def self.down
    drop_table :catches
  end
end

Everything here was generated except the boat_id we added. Now we can execute the migration by using Rake, as shown below.

Listing 11. Using Rake to create the catches table
>rake db:migrate
(in /Users/michael/code/aptana/workspace/deadly)
== 20080727060346 CreateCatches: migrating ====================================
-- create_table(:catches)
   -> 0.0291s
   -> 0 rows
== 20080727060346 CreateCatches: migrated (0.0299s) ===========================

We need to modify the models. Open the Boat model and add a single line of code to it, as shown below.

Listing 12. The Boat model
class Boat < ActiveRecord::Base
  has_many :catches
end

The class is empty by default, as Rails infers the attributes of the class based on table metadata from the database. We simply add the meta-programming command to indicate that a single boat has many catches. Similarly, we complete the relationship by modifying the catch model, as shown below.

Listing 13. The Catch model
class Catch < ActiveRecord::Base
  belongs_to :boat
end

Again, we simply add a meta-programming command to indicate that a catch belongs to a boat. We followed the Rails naming convention when we used boat_id to indicate that a catch belongs to a boat. Now we can create a new controller.

Listing 14. The DeliveryController
class DeliveryController < ApplicationController
  def index
    @boats = Boat.find :all
    @catch = Catch.new
    respond_to do |format|
      format.html
    end
  end
  
  def record
    @catch = Catch.new(params[:catch])
    
    respond_to do |format|
      if @catch.save
        flash[:notice] = 'Delivery recorded successfully'
        format.html { redirect_to :action => "list"}
      end
    end
  end
  
  def list
    catches = Catch.find(:all)
    @totals = {}
    catches.each{|catch|
      if @totals[catch.boat]
        @totals[catch.boat] += catch.quantity
      else
        @totals[catch.boat] = catch.quantity
      end
    }
    @totals = @totals.sort {|a,b| b[1] <=> a[1]}
    respond_to do |format|
      format.html
    end   
  end
end

This defines three actions. The first is the index action — the default when /delivery (Rails routing convention will map /delivery to the DeliveryController class — is requested by a user. Here, we do a little extra work to get all the boats first, so we can use them in the view.

Listing 15. Default delivery view
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; 
charset=iso-8859-1" />
        <title>Record a Delivery</title>
    </head>
    <body>
        <h1>Record a Delivery</h1>
        <% form_for @catch, :url => { :action => "record" } do |f| %>
          <%= f.error_messages %>
        
          <p>
            <%= f.label :quantity %>
            <%= f.text_field :quantity %>
          </p>
          <p>
                  <%= f.label :boat %>
                  <%= select("catch", "boat_id", @boats.collect{|b| [b.name, b.id] }, 
{ :include_blank => true })%>
          </p>
          <p>
            <%= f.submit "Record" %>
          </p>
        <% end %>    
    </body>
</html>

This is similar to the generated code we would get from using scaffolding, as we leverage Rails features to allow for rapid prototyping. For example, we will use a FormHelper object by using the form_for API from the ActionView class. There are a couple things we use that you would not see from generated code. First, we set the action URL for the form to go to the record method, as shown in Listing 14. We will take a look at this method shortly. Next, we use select helper to create a custom HTML select tag with option values. We use the boats we retrieved in the index method of the DeliveryController class, as shown in Listing 14. We use some idiomatic Ruby and create a collection of arrays, where each array has the name of a boat and its ID. These will become the labels and values of the options in the HTML that will be generated. This code could have been put in the controller, but it demonstrates the expressiveness of Ruby and that expressiveness is part of what makes Ruby so well suited to rapid prototyping and development.

The form in Listing 15 submits to the record action of the DeliveryController class, back in Listing 14. This method simply creates a new Catch instance and saves it. It then forwards to the list action, also from Listing 14. This action queries the database to retrieve all Catch records. It then aggregates the records to sum up the total catches for each boat in the database. You could perform this calculation using a custom query, as well. This aggregate then gets sorted into an array of two-element arrays, where the first element is a Boat object and the second element is the total catches for that boat. This is then passed to the view shown below.

Listing 16. The list view
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
        <title>List Totals</title>
    </head>
    <body>
        <h1>Totals</h1>
        
        <table>
          <tr>
            <th>Boat</th>
            <th>Total</th>
          </tr>
        
        <% for total in @totals %>
          <tr>
            <td><%=h total[0].name %></td>
            <td><%=h total[1] %></td>
          </tr>
        <% end %>
        </table>
        
        <br />
        <%= link_to 'Add catch', :action => "index" %>
    </body>
</html>

This is all pretty standard Rails templating for creating a table of all of the boats and their totals. We also use one last helper to create a link back to the index action for the user. Now we have a complete prototype of a mini-application. This concludes the application, but there is one more thing we will add: a brief note about using an IDE with JRuby and Derby.


IDE support

For many developers, rapid prototyping and development means using an IDE. Ruby is not the easiest language to build an IDE for, and this is just as true for JRuby. However, there are several good IDEs available, including the Eclipse-based RadRails. There are other articles covering this great tool (see Resources), but it can be a little tricky to use with JRuby and Derby. Most IDEs, including RadRails, need a Ruby debug gem so the IDE's debugger can hook into the Ruby VM. This gem is a native gem in that it is written in C++. Such gems cannot be used (yet) with JRuby. Instead, a Java version must be written, but fortunately, this has been done for this very important gem. You need to download one Java-based gem directly, then install it an a couple of other standard Ruby-based gems. This process is shown below.

Listing 17. Adding debugging support to JRuby
$ jruby -S gem instal -l ruby-debug-base-0.10.1-java.gem 
Successfully installed ruby-debug-base-0.10.1-java
1 gem installed
Installing ri documentation for ruby-debug-base-0.10.1-java...
Couldn't find file to include: 'VERSION'
Installing RDoc documentation for ruby-debug-base-0.10.1-java...
Couldn't find file to include: 'VERSION'
$ jruby -S gem install --ignore-dependencies -v 0.10.1 ruby-debug
Successfully installed ruby-debug-base-0.10.1-java
1 gem installed
Installing ri documentation for ruby-debug-base-0.10.1-java...
Couldn't find file to include: 'VERSION'
Installing RDoc documentation for ruby-debug-base-0.10.1-java...
Couldn't find file to include: 'VERSION'
$ jruby -S gem install --ignore-dependencies ruby-debug-ide
Successfully installed ruby-debug-ide-0.2.0
1 gem installed

You can add breakpoints and debug your application easily from any IDE that uses the standard Ruby debugging gems. You can also use the debugger to step through some of the Rails code and get a much better understanding of some of its magic.


Summary

In this article, we saw how the combination of JRuby, Rails, and Apache Derby can produce a perfect environment for rapid prototyping and development. With Rails, we're able to use generators to produce boilerplate code, or we can create more custom applications with little effort, as well. With the help of JRuby and Derby, we have a transactional, persistent database embedded with our application; it runs whenever our application runs. From here, you can use more Rails features to add more models, controllers, and views. You can add Ajax to the applications easily using Ajax helpers in Rails. You can also use an IDE, such as the Eclipse-based RadRails to further speed your prototyping and development.


Download

DescriptionNameSize
Example source codeos-ad-prototype-jruby-deadly.zip110KB

Resources

Learn

Get products and technologies

  • Get JRuby from the project Web site.
  • Get the Java SDK V1.6.
  • Get Apache Derby; this article uses V10.4.1.3.
  • Download Ruby; this article uses V2.1.
  • Innovate your next open source development project with IBM trial software, available for download or on DVD.
  • Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2, Lotus, Rational, Tivoli, and WebSphere.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=331854
ArticleTitle=Rapid prototyping with Apache Derby and JRuby on Rails
publish-date=08262008