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.
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.
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?
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.
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-JDBC gem 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.
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
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.
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
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.
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
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
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.
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 modelclass 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 modelclass 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.
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.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Example source code | os-ad-prototype-jruby-deadly.zip | 110KB | HTTP |
Information about download methods
Learn
-
Read "Fast-track your
Web apps with Ruby on Rails" to learn what makes Ruby on Rails so appealing to developers.
-
Dive into the Ruby programming language by reading "Ruby off the
Rails."
-
What could be better than Rails? Why Rails and Ajax, of course. Find out about the two
hottest technologies in this "Crossing borders: Ajax
on Rails" article.
-
Read "Web
Development with Eclipse, Part 3: Ruby Development Toolkit and RadRails" learn all
about using RadRails to assist your Rails development.
-
Observe some other ways you can use JRuby and Derby together by reading
"Two ways to build
Apache Derby database applications in JRuby."
-
JRuby home is the best place to find out anything you need to know about JRuby.
-
A good place to find out the future direction of JRuby is on the blog of Charles Nutter, the lead developer for JRuby.
-
RubyonRails.com is the place to get started
with introductory tutorials, guides, and downloadable source and documentation.
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
-
Participate in developerWorks blogs and get involved in the developerWorks community.
Comments (Undergoing maintenance)






