Improved Scaffolding for Ruby on Rails

Using the ActiveScaffold plugin to improve the default Ruby on Rails layouts.
Creating the Database Tables with Rails

Rails can help with the creation of our database tables, and we need three: one to hold information on our soccer players, another for squad data and another to maintain medical conditions. For the sake of simplicity, let's assume that each player belongs to one squad and can have a single medical condition (or none at all). Let's tell Rails about the tables:

ruby script/generate model player
ruby script/generate model squad
ruby script/generate model condition

Models in Rails let us talk to our data from our Web application. Each of the above commands produces eight lines of output while Rails does its thing. Note that each contains a file generated in the db/migrate directory. These are our database migrations. At this point, things get less SQL-centric and more Rails-like, as Rails provides a database-independent way to define our tables. To see this in action, edit the db/migrate/xxxxxxxxx_create_players.rb file (where xxxxxxxxx is a unique date/time string generated by Rails), changing the self.up method to look like this:

def self.up
  create_table :players do |t|
    t.integer     :squad_id, :condition_id
    t.string      :name, :address, :contact_tel_no
    t.date        :date_of_birth
    t.timestamps
  end
end

This is the high-level Rails way of telling your database to create a table. Each column in the table gets a unique name and a data type. Note that in addition to the columns you might expect each player to have (name, address and so on), we add in two integer columns that will link to the squad and condition tables. What's cool about using migrations is that it does not matter which database you are using, Rails generates the correct database-specific SQL statements as required and when needed. Let's define the other two tables. Edit db/migrate/xxxxxxxxxx_create_squads.rb, changing the self.up method as follows:

def self.up
  create_table :squads do |t|
    t.string          :name
    t.timestamps
  end
end

And, finally, change db/migrate/xxxxxxxxxx_create_conditions.rb to have a self.up method that looks like this:

def self.up
  create_table :conditions do |t|
    t.string          :name
    t.timestamps
  end
end

Now for the fun part, type the following at the command-prompt:

rake db:migrate

Output similar to the following should scroll by on screen:

(in /home/barryp/rails/soccer_club)
== CreatePlayers: migrating =====================
-- create_table(:players)
   -> 0.1916s
== CreatePlayers: migrated (0.1918s) ============

== CreateConditions: migrating ==================
-- create_table(:conditions)
   -> 0.0183s
== CreateConditions: migrated (0.0185s) =========

== CreateSquads: migrating ======================
-- create_table(:squads)
   -> 0.0309s
== CreateSquads: migrated (0.0311s) =============

What's happened is that Rails has connected to the back-end database and created the three required tables. Note that there's no programmer-written SQL code in sight! Rails handles all the down-and-dirty SQL details. For those readers who don't believe me, log in to PostgreSQL as soccer_manager and bask in the glory of the table schema that Rails has created for you.

The Default Rails Layouts

At this point, it would be normal to use Rails to generate some scaffolding code, then reach for a CSS reference to pretty up the whole thing. This is doable, but it takes time. For now, let's use Rails to generate empty controllers with these three commands:

ruby script/generate controller player
ruby script/generate controller squad
ruby script/generate controller condition

Each of these commands produces seven lines of output. Note that a Ruby file is generated in the app/controllers directory. These are source code files that will contain any business logic we want to add to our Rails application. We will do this in a little while. To complete the default Rails setup, we need to specify our table relationships. Edit app/models/player.rb to look like this:


class Player < ActiveRecord::Base
  belongs_to  :condition
  belongs_to  :squad
end

One Little Edit: ActiveScaffold Goodness

ActiveScaffold is written and maintained by a dedicated group of Rubyists who live at activescaffold.com/team. ActiveScaffold is a Rails plugin, and as such, gets installed into an existing Rails project, so let's do that first. From the top-level directory of your Rails application, type the following (which should be entered on a single line):


git clone git://github.com/activescaffold/active_scaffold.git \
          vendor/plugins/active_scaffold &&                   \
          rm -rf vendor/plugins/active_scaffold/.git

This command fetches ActiveScaffold and installs it into your Rails application. When this process completes, a new directory has been created within the vendor/plugins/ directory of your Rails application called activescaffold. For the plugin to work its magic, we need to create an application-level layout that will be used throughout our Rails application. Here's a bare-bones layout, which we need to create in the app/views/layouts directory and which is called application.rhtml:


<html>
<head>
 <title>Soccer Club Database System</title>
 <%= javascript_include_tag :defaults %>
 <%= active_scaffold_includes %>
</head>
<body>

<%= yield %>

</body>
</html>

This is a straightforward, essentially empty, HTML page. Take note of the code included within the <%= and %> tags. These tags allow us to execute Ruby code from within an HTML template. The first set of such tags adds a set of JavaScript routines to our page; the second pulls in the ActiveScaffold goodness, and the third executes the Ruby yield method. Any layouts that are created within our application (whether manually by us or dynamically by Rails or ActiveScaffold) will be wrapped in the application.rhtml layout, with their content replacing the invocation of yield as required. With the default layout created, we need to edit each of our existing controllers to switch on ActiveScaffold. Here's how the app/controllers/player_controller.rb file should appear after this edit:


class PlayerController < ApplicationController
  active_scaffold    :player
end

Add a similar line of code to the app/controllers/squad_controller.rb and app/controllers/condition_controller.rb files, then start your Rails application:

ruby script/server

Fire up your browser and load the http://localhost:3000/player page. Take a look at Figure 1, which shows the default ActiveScaffold player listing—it looks great. Note that ActiveScaffold has spotted the links between the three tables and pulled in the appropriate data values. Note also that I've added some sample data to my Web app. Unfortunately, the ordering of the columns leaves a little to be desired, and this is no more evident than when we view the default ActiveScaffold player form, as shown in Figure 2. This form displays the table columns in alphabetical order, which is not what we want. In addition, the subforms that provide access to the squad and medical condition data are cool, but what we want is a simple drop-down list for our application. Thankfully, adjusting ActiveScaffold's default behaviors is not difficult, as we shall see in a few moments.

Another problem (which you may have noticed if you've been following along) is that the date range associated with the date_of_birth value is very restrictive, using 1997 as the earliest start year. As all of our soccer players were born in the early 1990s, we need some way to adjust the start year for any entered dates. ActiveScaffold (together with Rails) can help here too.

Figure 1. Default Player Listing as Generated by ActiveScaffold

Figure 2. Default Player Data-Entry Form as Generated by ActiveScaffold

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Getting the article code to work with PostgreSQL.

barryp's picture

A reader, Ken Shaffer, emailed with an issue he had getting the example code (above) to work with his version of PostgreSQL. I initially suggested he try it with MySQL just to get things going but, to Ken's credit, he soldiered on and sorted out his problems. Here's a copy of Ken's e-mail to me letting me know what he did. Both Ken and I hope this information will be of use to other readers experiencing similar problems.

----------- start of Ken's email -----------
Hi Paul,
I did succeed in getting a compatible set of gems and activescaffold
for running the soccer club demo. Open source is so dynamic
that it's sometimes tricky getting compatible versions of things --
my hat's off to the people putting Linux distributions together.

For the Ubuntu 8.10 rails 2.1 (rake, rubygems) installed via the
Synaptic Package Manager, use the ruby_pg gem instead of the postgres
gem.
I understand the postgres gem has not been supported since 2006.
Also avoid the pg gem, which is not necessary. The activescaffold
install now needs to pick out the rails 2.1 (previous) version.
The following failed to get the rails 2.1 version (got the default 2.2
ver):
script/plugin install \
git://github.com/activescaffold/active_scaffold.git \
-r rails-2.1
Found a code snippit (attached below) which worked.

Another (more painful) approach which worked is to update all the
rails 2.1.0 versions to the 2.2.2 versions (and also update rubygems).
The default activescaffold install will then work.

The below code snippit succeeded in getting the activescaffold version
compatible with rails 2.1

from http://code.google.com/p/activescaffold/issues/detail?id=626
Comment 1 by mr.gaffo, Nov 18, 2008

---snip -----
Either (in vendor plugin):
git clone git://github.com/activescaffold/active_scaffold.git
[cd to active_scaffold ]
git checkout origin/rails-2.1
rm -rf .git
Or, pull down the newest rails-2.1 tarball from github.
--snip------

Again, thanks for a great article. It was the combination of postgresql
and rails that caught my interest. Feel free to pass along any info, no
attribution needed.
Ken
----------- end of Ken's email -----------

Thanks for that, Ken!

Paul.

Paul Barry

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix