Back to square one with Ruby on Rails

This article was originally published in VSJ, which is now part of Developer Fusion.
An entire generation of web application developers has adopted development platforms such as Java Enterprise Edition, and Microsoft’s ASP.NET. Both of these platforms have matured over the last decade, adding features, refining and tuning, while catering for their audience of developers. The development frameworks for these platforms have grown to a massive size, each with a swiss-army-knife arsenal of intermingled base APIs. There now exists a significant accumulation of legacy code relying on older versions of these platforms. Backwards compatibility assurance and the conservative attitudes of the (now senior) developer audience have often become a ‘baggage’, and deterrent to non-evolutionary changes in the underlying framework or methodology. And so for what seems to be a very long period of time in the history of web application development, developers are busy coding to these massive and complex ‘frameworks’, trying to satisfy the ever-changing customer requirements without themselves asking the question: “How can I make my web development framework simpler?”

Few have challenged the wisdom and relevance of these complex frameworks, for most developers have grown up with the technology. Even though these frameworks occasionally force developers to go through unnatural contortions to get things working, these frameworks are architecturally sound, academically approved, and, for the most part, work as advertised.

Meanwhile, a new generation of developers has arrived. Having the benefit of hindsight, and a distilled list of the industry’s best practices, they embark on re-building a web development framework that is unencumbered by legacy baggage – and one with features that match a “dream requirements list” for a web development framework.

Ruby on Rails is one such end product.

This article introduces Ruby on Rails from a Java developer’s perspective. A hands-on example features the same VSJ service centre application that has appeared in previous Java development framework articles, so that readers can see how Ruby on Rails differs from traditional Java frameworks.

Ruby and Rails

Ruby is an interpreted programming language created over a decade ago in Japan. The language itself mixes elements of Python, Smalltalk and Ada. Like many other scripting languages, and unlike Java, Ruby is dynamically typed (you do not have to define the type of a variable before use), and takes great advantage of it in meta-programming (dynamic and adaptive generation of code at runtime). Unlike many other scripting languages, Ruby is completely object-oriented, and the syntax is both expressive and clean.

Rails is a server-side framework, written in Ruby, that uses Ruby’s agile dynamic typing to create an easy to learn, simple to use, testable and maintainable framework for the creation of web applications. The Rails framework does not attempt to solve the world’s problems. It focuses on only one: the creation of web-based applications with a backing RDBMS.

Rails solves this single problem very well, and because a very large percentage of web-based applications require such a solution, Ruby on Rails has (in a very short time) became very popular amongst web developers and designers-turned-developers.

Attractive feature set

Attractive features of Ruby on Rails include:
  • Its use of the proven and well-understood Model-View-Controller architectural pattern
  • It significantly reduces the code you have to write to implement a web application
  • It eliminates complex XML configuration script for server and server components
  • It allows rapid iteration during development – first try out skeletal code and then iteratively flesh out the implementation
  • The initial learning curve is shallow; you can get a lot done in a very short time with Ruby; this is especially true if your application falls within one of the existing template applications
  • Its higher-level Object to Relational mapper syntax that completely eliminates the need to work on a database’s SQL level when using Rails
  • An integrated unit testing framework similar to JUnit
If this sounds like an ideal set of features for web application development, you will like Ruby on Rails.

Rail’s MVC Core

At the core of Rail is an HTTP request-processing engine designed in the familiar Model-View-Controller pattern. Of course, popular Java frameworks such as Struts and JavaServer Faces are also based on an MVC pattern. As a quick recap, in MVC the model encapsulates the data and the operations on them, the view contains the presentation layer, and the controller is the front-end redirector that forward requests to your business logic code.

Figure 1
Figure 1: Rails’ operation model

Rails’ operation model is shown in Figure_1, where Webrick is the web server written in Ruby, and can be thought of as a “master container” for the Rails components. As the requests come in, they are dispatched through dispatch.servlet in Webrick, and handed to the appropriate ActionController based on a routing map. A later section shows how to configure the routing map. You can write your own application specific controller by subclassing from ActionController. The ActionController is a front-controller and can direct requests to business logic code, or to an ActionView. The ActionView is the presentation layer of the application. It can be Ruby embedded in an HTML file, with an rhtml extension; or it can be Ruby embedded in XML file, with an rxml file extension. eRB, an embedded Ruby preprocessor, is used to process ActionViews (very similar to how a JSP compiler works). The data in the application are all accessed through the modeland handled by ActiveRecord. ActiveRecord is Rails’ persistence mechanism, and is a very flexible object to relational data mapper. ActiveRecord knows a lot about how the Rails application works and makes data handling and manipulation (typically painful in legacy frameworks) quite simple.

You will see how the entire architecture works together shortly. The VSJ service centre example, described later, encompasses every one of the above-described components.

Downloading and Installing Ruby on Rails

To get a Ruby on Rails development environment going, follow these steps:
  1. Download and install Ruby for your platform from www.ruby-lang.org. If you are running Windows, download the Windows Installer bundle – which includes many useful extras. You will need 1.8.4 or later for the example in this article.
  2. Download and install Ruby Gems, a package manager
  3. Install Rails using Ruby Gems; use the command:
  4. gem install rails -include-dependencies
  5. Download and install RADRails for development from www.radrails.org, the latest version at the time of writing is 0.5.3. At this time, it is best to download and install the standalone version of RADRails, instead of the Eclipse plug-in.
Note: Due to space constraints, this article does not describe the basic syntax of the Ruby programming language. Interested readers should visit www.ruby-lang.org for information about the language’s syntax plus available APIs.

Creating the VSJ Service application with Rails

You can follow the instructions provided in this article, using the distribution source code as a reference. As not all the project’s code is covered in this article, it is important that you download the source code if you want to try out the example.

One of the more popular IDEs for creating and testing applications for Ruby on Rails is called RADRails, which is based, somewhat ironically, on the Java standard Eclipse IDE. If you are an Eclipse user, you will feel right at home with RADRails. This example will use RADRails throughout for development.

In RADRails, start a new Rails project by using File->New and then select Rails Project.

The wizard prompts for the name of the application next – call it vsjservice. Make sure you have checked the generate code skeleton framework. This will save you a lot of work making the directory and creating the foundation classes for the application. Figure 2 shows the selection of the generate code skeleton. The option Create a WEBrick server allows you to stage and test your application using the IDE. Click the “Finish” button and a bare bones Ruby on Rails project will be created for you.

Figure 2
Figure 2: Rails Options

The VSJ service application and MySQL database

The VSJ service centre application tracks customers, and the electronics repair work orders associated with each customer.

Before Rails can do its magic, you will need to create the database tables used by the application. The VSJ service centre database contains two tables, shown in Table 1.

Table 1: Database tables

Table Description
Customers contains information relating to a customer, including name and phone number
Workorders a service workorder, describing the problem that needs fixing, has a reference to the customer who reported the problem

A MySQL script to create these tables is contained in createtb.sql in the code distribution.

Assuming you have a MySQL account set up with user name vsjuser, the user does not require a password, with privileges to create database and table. You can create the vsjservice database using the command:

mysqladmin -uvsjuser create vsjservice
Then you can create the tables using:
mysql -uvsjuser < createtb.sql
Next, you need to inform Rails of the database location.

Providing database information to Rails

In the generated project, look for the config directory. Under the config directory, you will find databases.yml. This file tells Rails how to connect to the database. Edit this file to put it your connection information. Use the host and port field to specify the location of your MySQL server – the default is localhost and port 3306 on Windows. Enter your database name, user name, and password if any. There are actually three sections in this file, one for development, one for testing, and one for production. For now, you only have to add information to the development section. Figure 3 shows the database.yml page – remember to save your modifications.

Figure 3
Figure 3: Viewing the development database in RADRails

In Rails, select Window->Show View…, and then select Others…. In the popup menu, select Data, and then Data Nagivator. This pane will show and verify the Rails connection to the database. You should see Rails picking up your customers and workorders tables immediately, as shown in Figure 3.

Generating CRUD

CRUD is short form for Create/Read/Update/Delete, the generic operations that one performs on database records. These operations are also typically the ones that require the most tedious coding. With Rails, you can literally generate a web application that handles basic CRUD for your tables in one click. CRUD code generation is called scaffold in Rails.

You will next generate the scaffold for the two tables. The scaffold consists of a Controller for a directing a set of actions you can perform on the objects and model code that maps objects to and from the relational database.

Figure 4
Figure 4: Generating the scaffold for the Customers table

Use the Generators pane, on the bottom right, to generate the scaffold. Figure 4 shows the generation of the scaffold for the Customers table. Note that you type in customer, without the ‘s’. Rails is smart enough to know that a collection of customer objects can be found in the table named customers.

Repeat the operation to generate scaffold for workorder as well.

After the code generation, you will see these new Ruby classes:

  • customer.rb and workorder.rb under the models subdirectory
  • customer_controller.rb and workorder_controller under the controllers subdirectory
At this point, the basic CRUD code generation is complete. In some simpler cases, the application can be used immediately. However, for the VSJ service system, we need to touch up and customise the generated code.

Adapting Rails to use an existing database table

By default, Rails scaffold generated code that assumes a primary key with the field name ‘id’ for every table. If you are creating new tables for your application, you can make sure you use ‘id’. However, if you’re adapting Rails for an existing database, you must tell Rails the field name of the primary key. In our case, the primary key has the name ‘custid’. Add the set_primary_key method to the model. In the models/customer.rb file, add the highlighted line:
class Customer < ActiveRecord::Base
	set_primary_key “custid”
end
You need to do the same for models/workorder.rb, setting the key to ‘woid’. Next, you need to remove the data entry field associated with these ids, since they are auto increment fields.

Removing data entry field for the id column

The action ‘new’ allows a user to create a new customer. Under the views/customers directory, _form.rhtml contains the form used for data entry. If you are familiar with JSP, you will be very comfortable with rthml files. These rthml files forms the presentation layer. They are HTML files with special tags and embedded Ruby code.

By default, Rails will generate an entry for the custid field, which it did not know is the primary key during scaffold generation. You can remove the custid entry, leaving _form.rthml as shown in the listing below. Some of the textual field labels have also been changed to make them more user-friendly.

<%= error_messages_for ‘customer’ %>
<!--[form:customer]-->
<p><label for=”customer_firstname”>
	First name</label><br/>
<%=text_field ‘customer’, ‘firstname’ %>
</p>

<p><label for=”customer_lastname”>
	Last name
</label><br/>
<%=text_field ‘customer’, ‘lastname’ %>
</p>

<p><label for=”customer_phone”>
	Phone
</label><br/>
<%=text_field ‘customer’, ‘phone’
	%></p>

<p><label for=”customer_company”>
	Company
</label><br/>
<%=text_field ‘customer’, ‘company’
	%></p>
<!--[eoform:customer]-->
The <%= %> bracketing encloses a Ruby expression that will be evaluated. Like JSP, there is frequently a need to emit the value of expression in-line with the underlying HTML. Like JSP, you can use the <% %> bracketing to embed Ruby code.

Starting WEBrick and trying out CRUD code

You can start the server on the Servers pane by clicking on the green arrow start button.

You can try out the generated CRUD code using the URLs:

http://localhost:3000/customers/new
http://localhost:3000/workorders/new
Rails know to send the customers/new request to the CustomerControllerb class with an action of “new”. This will cause the new method to be called.

Stylising pages

The CRUD generated code has very plain appearance. For the VSJ service system, a CSS stylesheet is used to jazz up the look of the displayed pages.

Adding this CSS to the Ruby on Rail application is straightforward, place the vsjservice.css stylesheet in the /public/stylesheets directory. You should find the default stylesheet used by the scaffold, called scaffold.css, already located there.

After placing the CSS in the /public/stylesheets directory, you need to make sure your application pages refer to this stylesheet. This can be done through the layout template. This template allows you to specify the look of all your pages, similar to Tiles. Modify the generated layout file, /views/layouts/customers.rthml, to include this stylesheet instead of the default one. The line to modify is red in the following listing.

<html>
	<head>
		<title>
			Customers:
		<%= controller.action_name %>
		</title>
		<%= stylesheet_link_tag
			‘vsjservice’ %>
	</head>
	<body>

		<p style=”color: green”>
			<%= flash[:notice] %></p>
	<%= @content_for_layout %>
	</body>
</html>

Note: If you examine the /app/public directory, under the application’s root, you will see images and javascript subdirectories. This is the location to put images used in your application, as well as external Javascript code that may be shared amongst many pages.

Customising a layout

While you’re modifying layout, you can customise the template to reflect the “look” of the VSJ service application. In rthml layout templates, the <%= @content_for_layout %> tag marks the spot where the specific view component (add, edit, etc) will be rendered. The completed modified template is in the code distribution, renamed to baselayout.rthml.

Figure 5
Figure 5: Framing provided by the baselayout template

Figure 5 show the framing provided by the baselayout template. The template renders the application title at the top, links on the left hand side, and the footer with the Rails logo at the bottom.

You can set both customers_controller.rb and workorders_controller.rb to use the same baselayout.rhtml ActionView. All you have to do is add a call to the layout method in the controller, as shown in the red code of following listing.

class CustomersController
	< ApplicationController
	layout “baselayout”
	def index
		list
		render :action => ‘list’
	end
	...

Offering a drop-down list

You can add a work order associated with a customer by clicking on a Create Work Order link next to the customer’s name. This is illustrated in Figure 6.

Figure 6
Figure 6: The Customer list provides a Create Work Order action

When the link is clicked, you want to display a form for work order creation that has a drop-down list of customers – with the associated customer pre-selected as shown in Figure 7.

Figure 7
Figure 7: The create workorder form with a pre-selected customer

To implement this behaviour, first modify the new method in the workorder_controller.rb controller to fetch the list of customers that will be displayed by the drop-down list. The following is the new method of the WorkorderController. The id is fetched from the params[] collection; this collection is created by Rails automatically based on the incoming request parameters – very similar in concept to implicit objects in JSP.

def new
	@workorder =
		Workorder.new
	@customers =
		Customer.find_all
	@curcust = params[:id]
end
To make sure that customer associated is selected in the drop-down list, you need to check the @curcust member against the customer ids as you render them. The following code, in views/workorders/new.rthml accomplishes this.
<select name=
	”workorder[custid]”>
	<% @customers.each do
		|cust| %>
	<option <%= “selected”
		if (Integer(@curcust) ==
			Integer(cust.custid)) %>
	value=”<%= cust.custid %>”>
	<%= [cust.firstname,’ ‘
		,cust.lastname, ‘ - ‘,
		cust.company] %>
	</option>
	<% end %>
</select>

Working with many-to-one relationships

When work orders are listed, it is better to display the customer information associated with the work order – instead of a meaningless ID. Figure 8 shows the list work orders page, with the customer details.

Figure 8
Figure 8: The list work orders page

To implement this, the customer data must be looked up based on the custid field value in a workorder record. Thankfully, this is almost automatic with Rails.

Rails has built-in capabilities to model complex object relationships, and this capability ties-in closely with the underlying RDBMS data.

To tell Rails that a workorder belongs to a customer, you only need to add the following highlighted line to models/workorder.rb :

class Workorder < ActiveRecord::Base
	set_primary_key “woid”
	belongs_to :customer,
		:foreign_key => “custid”
end
Here is Rails magic at work. The belongs_to is actually a method that will generate new methods for a Workorder dynamically at runtime. Other data modelling methods include has_many, has_one, and has_and_belongs_to_many. Together, they almost appear to be a high-level data modelling language. This is something that a statically typed compiled language like Java simply cannot achieve.

One of the generated methods in the Workorder allows us to access a customer record related to the foreign key, as if it is a member of the Workorder class. Allowing us to have the following rendering code in views/workorders/list.rhtml:

<% for workorder in @workorders %>
	<tr>
		<td class=”wocell”>
			<%= workorder.woid %></td>
		<td class=”wocell”>
			<%= workorder.item %></td>
		<td class=”wocell”>
		<%= workorder.problem %></td>
		<td class=”wocell”>
	<%= workorder.repairnote %></td>
		<td class=”wocell”>
<%=workorder.customer.firstname %></td>
		<td class=”wocell”>
<%=workorder.customer.lastname %></td>
		<td class=”wocell”>
<%= workorder.customer.phone %></td>
		<td class=”wocell”>
<%= workorder.customer.company %></td>
	</tr>
<% end %>

Replacing the default index page by mapping a route

Lastly, you will make the list action of the customers controller the default action when you access the URL:
http://localhost:3000/
This can be accomplished by first looking up the public/index.html file, and renaming or deleting it. Next, find the routes.rb file under the config directory, and add the following entry. Make sure you add it as the first entry in the set of routes. Think of this as a servlet mapping in Tomcat, except that order of definition does matter.
ActionController::Routing::Routes.draw
	do |map|
	map.connect ‘’, :controller =>
		 ‘customers’, :action => ‘list’
	map.connect
		‘:controller/service.wsdl’,
			 :action => ‘wsdl’
	map.connect
		‘:controller/:action/:id’
end
The route mapping tells Rails to send requests for the ‘’ context, which is essentially the application root, to the customers controller, with action list.

A Built-in Testing Framework

Most production developers can see right through a thinly-disguised code generator by asking about its unit testing support. Ruby on Rails welcomes such challenges. Having a decade of hindsight and accumulated best practices on its side, Ruby on Rails leverages the Test::Unit unit testing framework built into Ruby. This framework is part of the Ruby distribution, you do not have to download it separately. If you are already familiar with JUnit, you will be right at home – because the general concept is the same.

In a nutshell, the following are the steps to use this testing framework (this will be deja-vu for JUnit users):

  • create individual test cases, each one is a Ruby class
  • a test case must inherit from Test::Unit::TestCase
  • a naming convention, including the word “Test”, allows the unit tester to find test cases
  • each test case can contain multiple individual tests
  • each test in a test case can test a specific condition, using assert methods to validate the outcome
  • a large set of assert methods are available for you to validate the expected output versus the actual
  • a setup() and teardown() method exists for executing pre-test and post-test logic
  • multiple related test cases can be placed into test suites, and all be executed in a single operation
Using Rails’ object to relational mapper and code generation capabilities, Ruby on Rails takes testing support a little further with the following features:
  • the ability to define specific database contents that will be loaded automatically into your RDBMS before a test is run (this content is called a fixture, not to be confused with the same term in JUnit)
  • a rich set of methods to navigate, extract, and scrape HTML content, when a test is performed against HTML generating components
Seasoned JUnit users will understand the often-tedious code that these features will eliminate during the creation of unit tests.

When you generated the scaffold earlier, a skeleton set of unit tests was generated for you. This set of bare-bone unit tests will test the integrity of the generated code handling CRUD. Since you’re likely to modify the generated code in even the smallest application, these tests provide useful sanity-checks after any modifications. You should add to these tests as you create new code.

To try out the testing framework in RADRails, lookup the files under test/functional directory. There is a test for customers_controller, and another one for workorders_controller. Open up the customers_controller_test.rb by double-clicking on it. Now, with your mouse cursor over the content of this file in the editor pane, right click and select Run as… -> Test::Unit Test.

You should see the familiar JUnit test result panel, as shown in Figure 9.

Figure 9
Figure 9: Unit testing in action

Rails reality check

So is Ruby on Rails the perfect web application development environment, ready to displace J2EE and .NET? The answer, as of the first quarter of 2006, is likely to be no. The following will give you a feel for some issues that the Rails community is currently working to solve.

Integration of Ruby on Rails with industry workhorse web servers such as the Apache Web Server and Microsoft’s IIS is a work-in-progress. While the WEBrick server written in Ruby is great for development, currently it simply does not have the facility to deal with production scenarios involving a large number of concurrent requests. The current integration effort is centred around CGI-based integration – both standard and fast variants. Ironically, the skill-set required in this integration exercise is distinctly different from those required for an expert Rails designer. There are both open source and commercial efforts working in this space, and you should see competing solutions emerging throughout 2006.

Tools support for Rails is still very weak. While it is great in a sales seminar to tout the significantly reduced code-lines count required to implement a solution, it remains a fact that most production systems, even modest ones, will inevitably evolve to tens of thousands of lines of code. In order to be effective, the IDE and modelling tools for Rails must be able to assist the developer in navigating, managing and maintaining such a large body of code. Due to the dynamic and meta-programming nature of the Ruby language, this is not a trivial problem to solve – as there is little prior experience to draw from. There exist multiple efforts going on in both the open source and commercial world to address this problem. Expect to see competing products in 2006.

Unquestionably, the Ruby on Rails momentum is unstoppable, and the technology has won a lot of influential supporters. Engineers are hard at work making real the vision of simple web application development.

Ruby on Rails has raised the stakes in the world of practical and usable web development frameworks. Developers’ expectations and demands on their frameworks have irreversibly changed forever. It matters little that Rails may not be the most efficient, robust, or scalable solution for creation of web applications today. After all, J2EE didn’t start out that way either.

What does matter is that web developers have now actually experienced a new level in ease of use and adaptability, and there is no going back. Pressure is now on competing technologies to deliver on their long-unfulfilled promise – that of keeping the framework and development cycle simple and agile enough – so that the developers can actually focus on solving the application problem and adapting to changing user requirements.


Sing Li is a consultant, system architect, open source software contributor, and freelance writer specialising in Java technology, embedded and distributed systems design. He has authored or co-authored several books on the topic, including Professional Apache Tomcat (Wrox), Professional JSP 2 (Apress), Early Adopter JXTA (Wrox), and Professional Jini (Wrox).

You might also like...

Comments

About the author

Sing Li United States

Sing Li has been writing software, and writing about software for twenty plus years. His specialities include scalable distributed computing systems and peer-to-peer technologies. He now spends ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“To iterate is human, to recurse divine” - L. Peter Deutsch