Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Support

Using Ruby on Rails for Web Development on Mac OS X

The Ruby on Rails web application framework has built up a tremendous head of steam over the last year. Fueled by some significant benefits and an impressive portfolio of real-world applications already in production, Rails is destined to continue making significant inroads in 2006. Simply put, Ruby on Rails is an open source tool that gives you the advantage of rapidly creating great web applications backed by SQL databases to keep up with the speed of the web. And with the release of Rails 1.0 kicking off the new year, there's never been a better time to climb aboard.

It should come as no surprise that Mac OS X is a favored platform for Rails development. Rails and its supporting cast of web servers and databases thrive on the rich Mac OS X environment. A popular text editor used by many Rails programmers is TextMate, a Cocoa application. And all members of the Rails core development team work with Macs.

This article introduces you to Ruby on Rails by building a trivial web application step by step. Consider it a ride on the express train—an overview of what Rails can do, including a look at features new to Rails 1.0. In the end you'll be better equipped to consider the advantages of powering your web application with Rails.

Why Ruby on Rails?

First, you might be wondering: Web application frameworks are a dime a dozen, so what's different about Rails? Here are a few things that make Ruby on Rails stand above the crowd:

  • Full-Stack Web Framework. Rails is an MVC web framework where models, views, and controllers are fully integrated in a seamless fashion. That means you automatically get all the benefits of an MVC design without the hassle of explicitly configuring each component to play well with the others.

  • Real-World Usage. The Rails framework was extracted from real-world web applications. That is, Rails comes from real need, not anticipating what might be needed. The result is an easy to use and cohesive framework that's rich in functionality, and at the same time it does its best to stay out of your way.

  • One Language: Ruby. Everything from business logic to configuration files (there aren't many) are written in the Ruby programming language. With just one language, you hope it's a good one, and Ruby doesn't disappoint. Ruby is a full object-oriented language with clean syntax and it has a way of making programming truly fun. Using one language means you don't have to juggle between multiple languages and dialects as you're building your application.

  • Convention over Configuration. Rails works hard to take care of all the repetitive and error-prone chores associated with starting to build a web application, and maintaining it over time. Rails uses simple naming conventions and clever use of reflection to make your work easier with near-zero configuration.

  • It's Productive! At the end of the day, Rails is all about helping you stay productive. And in a world where being the first to market and keeping customers happy adds up to increased revenues for you, it pays to pick a tool aligned with those goals. Many real-world applications are already reaping the benefits.

All that being said, the best way to judge Rails is to experience it while building an application. So let's get right to it.

Installing Rails

The version of Ruby that shipped on Mac OS X Tiger prior to v10.4.6 did not work well with Rails.† †If you're running an earlier version of Tiger, you'll need to either upgrade to 10.4.6 or upgrade your copy of Ruby to version 1.8.4 or later using the open source distribution.

And to do any serious Rails development you'll want to install a production-quality web server, database server, and a few other goodies. Thankfully, a golden-path installation guide is already available.

NOTE: If you already have MySQL, you can get everything else by using Locomotive, a†drag-install of all the relevant pieces.

Walk through the step-by-step instructions outlined in "Building Ruby, Rails, LightTPD, and MySQL on Tiger". It starts by installing Ruby 1.8.4 without overwriting the system-installed Ruby (it puts the new version in /usr/local). That way, the default system works as expected for other users or programs already coded for the system-installed ruby. It also installs the RubyGems package manager, Rails, the LightTPD web server, and the MySQL database server (with native bindings), all of which we'll use in this tutorial. And the instructions have even been tuned to work well for those of you that are using the latest Intel-based Macs. By the end you'll have an ideal development and production environment.

Once you have a working version of Ruby and RubyGems installed, you can upgrade to future versions of Rails simply by typing

$ gem install rails --include-dependencies

Finally, we recommend that you download and install the 30-day trial of TextMate to use throughout this tutorial. It's a popular editor for Rails developers on Mac OS X.

Creating a New Rails Application

We're going to build an online expense tracking application. You could use this for tracking expenditures for your personal budget, a small company, a group or club, etc. Say, for example, our club organizes fund-raising campaigns and we need to make sure expenses are in line with the budget for each event. The old paper and pencil system broke down last month, so we've decided that we need an online web application that everyone can share.

First we need to create the application. All Rails applications have a consistent directory structure so that Rails can find stuff without needing to be told where to look. Create a skeleton directory structure and a set of files for our expenses application by typing

$ rails expenses

Next, open the new project directory with TextMate by typing

$ mate expenses

In the TextMate Project Drawer, shown in Figure 1, you should see the directory structure that the rails command created.

DirectoryStructure.jpg

Figure 1: Application Directory Structure (as shown in TextMate)

We'll peek inside these directories a bit later. But first, we're ready to run our application for the first time. Change directory to the expenses directory, then type:

$ script/server

You've just started a web server. If you have the LightTPD web server installed, Rails will attempt to use it by default. Otherwise WEBrick, the pure-Ruby web server that comes with Ruby, will be used. Either web server works well for development purposes.

Now point your web browser at http://localhost:3000. You should see a web page welcoming you aboard Rails. That tells us that the application is running, but it doesn't help our fund-raising campaign. Let's fix that.

Jump-Starting the Application

Now we're ready to put some meat on the bones of the generated directory structure. Ultimately (in the next few minutes) we want to end up with a web interface that lets us track expenditures for accounts. For starters, we need to create and configure a database to hold those accounts and expenses.

Setting Up the Database

Telling Rails which database to use and how to connect to it is one of the few times you'll need to touch a configuration file. In the config/database.yml file you'll notice that Rails has pre-configured three database connections—development, test, and production—that correspond to three runtime environments. We're doing development now, so have a look at that particular section of the config/database.yml file.

development:
  adapter: mysql
  database: expenses_development
  username: root
  password:
  socket: /tmp/mysql.sock

By default, Rails assumes we're using the MySQL database server with a database called expenses_development. If you set a MySQL password for the 'root' user to something other than the default (which is always a good idea), then set that password as the value of the password field. Also, if your mysqld.sock isn't in the usual place, you can explicitly set it in the socket field.

The default database.yml file includes example configurations for other databases. The following database servers are currently supported by Rails: MySQL, PostgreSQL, SQLite, SQL Server, DB2, Firebird, and Oracle.

Now we need to actually create the expenses_development database. Feel free to use whatever tool you're comfortable with. Here's how to create the database using the mysqladmin command-line tool:

$ mysqladmin -u root -p create expenses_development

We'll test the database connectivity a bit later.

Creating the Database Schema

We've created the database, but it doesn't have any tables yet. We need to store accounts in an accounts table, so let's start there. Rather than using raw DDL or a SQL administration tool, we'll use a Rails migration to create the accounts table. Migrations are database-agnostic representations of database schemas, which means we can easily switch to another database server later, if necessary. Better yet, migrations let us conveniently evolve a schema over time.

First, create the migration using the Rails migration generator.

$ script/generate migration accounts

That command creates a db/migrate/001_accounts.rb file that defines an empty migration. We need to fill it in to create an accounts table with two columns: a name that identifies the account (a text string) and a budget (we'll use a float for the sake of simplicity). Update the db/migrate/001_accounts.rb file as follows:

class Accounts < ActiveRecord::Migration

  def self.up
    create_table :accounts do |table|
      table.column :name, :string
      table.column :budget, :float
    end
  end

  def self.down
    drop_table :accounts
  end
end

This may be your first look at Ruby code. The file defines a class with two class-level methods—up and down. The up method moves the migration forward by creating the accounts table and its columns; the down method rolls the migration backward by dropping the accounts table. Note that unless told not to, create_table will implicitly create a primary key column.

Now let's apply the migration. We'll use Rake (Ruby's equivalent of Make) to run a task that's available in every Rails application.

Simply type:

$ rake migrate

Our expenses_development database should now have an accounts table. (Running 'rake migrate' is also a handy way to test database connectivity, so if the migration failed it's probably because your database isn't configured properly.)

Migrations are also reversible. For example, if you wanted to migrate the schema back to its original form, you'd simply type 'rake migrate VERSION=0'.

Putting Up Scaffolding

Now that we have a database and a schema for storing accounts, we need to start creating accounts. The fastest way to get from a database schema to a web interface is to use what Rails calls scaffolding. Scaffolding is the initial support for building an application—models, controllers, and views that handle the basic CRUD (create/read/update/delete) operations of any database-backed web application. Unlike tools that generate code that's never intended to be modified by humans, Rails scaffolding is generated with the express intent of encouraging you to tweak it. And just like scaffolding used to support the construction of a building, gradually the application scaffolding disappears.

Generate the scaffolding by typing

$ script/generate scaffold account expenses

That command generates a number of files for us; files we'd rather not create by hand. The first parameter (account) specifies the name of the model, which is generated in the app/models/account.rb file. The second parameter (expenses) specifies the name of the controller, which is generated in the app/controllers/expenses_controller.rb file. The scaffold generator also created template files for the views of our application. We'll look at how all these components work together a bit later.

Let's see how close scaffolding get us to a web interface for managing accounts. If the application is still running, press CTRL-C in the terminal where it's running. You may have changed the database connection, which requires a restart, so restart the application by typing

$ script/server

Then point your web browser at http://localhost:3000/expenses. You should see the web page that lets you create new accounts. Go ahead: create an account, click the "Create" button, and the new account will show up in the account listing. From there you can show, edit, or delete the account. Figure 2 shows example accounts for our fund-raising campaign.

ListingAccounts.jpg

Figure 2: Using scaffolding to manage accounts

This interface won't win any web design awards, but it's functional and gives us a jump-start on our application. With no explicit configuration, the model, views, and controller work together to give us a quick and dirty web interface.

Tiptoeing Through the Generated Code

How does all this work? At the core it uses simple naming conventions to eliminate most configuration. Take, for example, the "Show" hyperlink for the account with an id of 1. The URL for this link is:

http://localhost:3000/expenses/show/1

The first part of the URL path, expenses, is the name of the controller responsible for handling the incoming request. The second part of the path, show, is the name of an action defined by that controller. The last part of the path, 1, is the id (in this case, the database primary key) of the account to be shown. Figure 3 shows the MVC components in play, and their file equivalents.

MVC.jpg

Figure 3: MVC components and their files

So, using those URL path parts (called a route) as a guide, Rails will invoke the show method of the ExpensesController, supplying the account id in the HTTP request parameters. The controller code is found in the app/controllers/expenses_controller file.

class ExpensesController < ApplicationController
  
  def show
    @account = Account.find(params[:id])
  end

  # plus other action methods for creating, reading,
  # updating, and deleting accounts
end

The show method uses the account id supplied in params[:id] to find and populate an Account model object containing the values of the row in the accounts database table corresponding to the account id. The resulting Account model object is referenced by the @account instance variable. (Ruby identifies instance variables as variables starting with an @ symbol.) The Account model class itself lives in the app/models/account.rb file.

class Account < ActiveRecord::Base
end

At first glance, this class appears to do nothing, but in fact it already has a lot of functionality inherited through its parent class ActiveRecord::Base. Rails uses reflection and naming conventions to transparently map relational tables into model objects. The Account model encapsulates access to the accounts table in our database. An instance of an Account model class represents a row in the accounts table, with each table column mapping to an attribute of the Account instance.

After fetching and populating an Account model object and assigning it to the @account instance variable, the show action renders the template found in app/views/expenses/show.rhtml. Rendering this template is all implicitly done with naming conventions. If an action doesn't explicitly render a named template, then a template matching the action's name is used.

<% for column in Account.content_columns %>
<p>
  <b><%= column.human_name %>:</b> 
  <%=h @account.send(column.name) %>
</p>
<% end %>

<%= link_to 'Edit', :action => 'edit', :id => @account %> |
<%= link_to 'Back', :action => 'list' %>

The template is a mix of HTML and Ruby code. Any expression between <%= and %> is evaluated as Ruby code and the result is substituted for the expression. Notice that the show template has access to the @account instance variable that was set in the show action of the controller. The show template simply outputs the value of each attribute of the @account object with its associated column name. Hyperlinks to the edit and list actions are generated using the built-in link_to helper. The resulting web view is shown in Figure 4.

ShowAction.jpg

Figure 4: The 'show' action renders the 'show' template

Again, this template doesn't output the most visually appealing interface, but it's easy enough to change to your liking.

Customizing: Validating Models

You may have noticed that the web form used to create and edit accounts will let you enter anything in the fields, and even let you leave required fields blank. Maintaining the consistency of the application's data is a model's job, so let's add some validations to ensure the account has a name and a valid budget amount.

Thankfully, running the scaffold generator created a set of working files that we can incrementally tweak. Update the app/models/account.rb file as follows:

class Account < ActiveRecord::Base
  validates_presence_of :name
  validates_numericality_of :budget
end

Now try to get past the web form by leaving the name field blank or using anything but a number in the budget field. You should see something similar to the web form shown in Figure 5.

Validations.jpg

Figure 5: Model validations at work

Linking Models Together

Account management is working well, but we're missing the ability to add expenses to accounts. An expense is comprised of the date on which it was paid, a string identifying who it was paid to, and the amount paid. We need to store expenses in their own database table, which calls for another migration. Create the expense migration by typing

$ script/generate migration expenses

Then fill it in by updating the resulting db/migrate/002_expenses.rb file as follows:

class Expenses < ActiveRecord::Migration

  def self.up
    create_table :expenses do |table|
      table.column :paid_on, :date
      table.column :payable_to, :text
      table.column :amount, :float, :default => 0.0
      table.column :account_id, :integer
    end
  end

  def self.down
    drop_table :expenses
  end
end

Notice that in addition to expense data, rows in the expenses table have a foreign key reference (account_id) that links the expense to its account. Apply the migration by typing

$ rake migrate

Next we need an Expense model to wrap the expenses table. Use the generator to create the model by typing

$ script/generate model expense

That command gives us an empty Expense model class. Now we have to tell Rails that a relationship exists between the accounts and expenses tables. (Rails can't accurately derive relationships simply by looking at the database schema.) Specifically, an expense belongs to an account. Declare that relationship in the Expense model by updating the app/models/expense.rb file as follows:

class Expense < ActiveRecord::Base
  belongs_to :account
end

The belongs_to declaration dynamically adds methods to the Expense class, including methods for assigning an account to the expense and returning the account that the expense belongs to. For that to work, Rails assumes that the account_id column in the expenses table references the id column of the accounts table. It's another example of how Rails uses naming conventions to keep external configuration at a minimum, and you can always override the default behavior.

That takes care of one side of the relationship. We also need to tell Rails about the link in the opposite direction: an account has many expenses. Declare that relationship in the Account model by updating the app/models/account.rb file as follows:

class Account < ActiveRecord::Base

  validates_presence_of :name
  validates_numericality_of :budget
  
  has_many :expenses, :order => "paid_on"
end

The has_many declaration dynamically adds several methods to the Account class for managing an account's expenses. Note, however, that it's not required that we add relationships to both the Account and Expense models. Setting up relationships in both directions just means that we can access and operate on the relationships from either side.

Accessing Your Application Through the Back Door

$ script/console

That command loads the application into an interactive environment, and prompts you to enter Ruby code. Let's add two expenses to an existing account. Using one of the account names you created through the web interface, type the following lines, with each line followed by a Return:

>> account = Account.find_by_name("Chili Cookoff")
>> account.expenses.create(:paid_on => Time.now, :payable_to => 'Parties R Us', :amount => 75.00)
>> account.expenses.create(:paid_on => 1.day.ago, :payable_to => 'Fire Department', :amount => 25.00)
>> account.expenses(true)

(After entering each line and pressing Return, it will show the value of each expression. Use quit to exit the session.)

First we use a dynamic finder (find_by_name) to find an Account model object in the database. Dynamic finder methods work for any database column (e.g., find_by_budget(150)). Then we create and add two Expense model objects to the expenses relationship of the account. Finally, we call account.expenses(true) to get a collection of all expenses belonging to the account, ordered by the date the expenses were paid. (Using true forces the expenses to be reloaded.)

Now that we have an account with expenses, it would be helpful if we listed the expenses on the account detail page.

Dressing Up the View

The account detail page is still being generated by the show template that was created by the scaffolding. So when we show an account, we don't see any expenses. But we can easily change that by customizing the show template slightly. Add the following snippet to the bottom of the app/views/expenses/show.rhtml file:

<% if @account.has_expenses? %>
  <h3>Itemized Expenses</h3>
  <table>
  <% for expense in @account.expenses %>
    <tr>
      <td><%= expense.paid_on %></td>
      <td><%= expense.payable_to %></td>
      <td align="right"><%= number_to_currency(expense.amount) %></td>
    </tr>
  <% end %>
  </table>
<% end %>

The template loops through all the expenses for the account, generating an HTML table row for each expense. Notice the use of the built-in number_to_currency helper to display the expense amount as dollars. Navigating to the account detail page for the account you added expenses to using script/console previously, you should now see a page similar to the one shown in Figure 6.

AccountExpenses.jpg

Figure 6: An account has many expenses

Working with Web Forms

We don't yet have a way to record expenditures for an account using the web interface, so let's add an HTML form that captures the expense. We'll have the form post the expense to the record action in the ExpensesController.

First, add the following snippet to the bottom of the app/views/expenses/show.rhtml file:

<%= start_form_tag :action => 'record', :id => @account %>
  <p>
    On <%= date_select 'expense', 'paid_on', :order => [:month, :day, :year] %>
    to <%= text_field 'expense', 'payable_to', :size => 25 %>
    in the amount of $<%= text_field 'expense', 'amount', :size => 9 %>
  </p>
  <%= submit_tag 'Record!' %>
<%= end_form_tag %>

This creates an HTML form using built-in form helpers. The start_form_tag helper generates an HTML form that, in this case, posts to the record action of the ExpensesController with the current account id assigned to the :id URL parameter. The date_select helper generates a date selection widget bound to the paid_on attribute of the "thing" named expense. (Stay tuned for what we mean by "thing".) The text_field helper generates an <input> tag of type text bound to the payable_to attribute of the thing named expense. Finally, the submit_tag generates a submit button.

Reload a show page and you should see a page similar to the one shown in Figure 7.

WebForm.jpg

Figure 7: Recording expenses through a web form

If you try to submit the form, Rails complains that it can't find the record action to handle the form post. We need to define that action in the ExpensesController. Add the following action method to the app/controllers/expenses_controller.rb file:

def record
  Account.find(params[:id]).expenses.create(params[:expense])
  redirect_to :action => 'show', :id => params[:id]
end

Remember we said that the date and text were bound to a thing called "expense"? Just what is that "thing?" Well, in this case, it simply means that we can reference all the expense fields in the form by referencing params[:expense]. First we find the account corresponding to the account id supplied by the form (in params[:id]). Then we create an Expense model object from the form data (available in params[:expenses]) and add the expense to the account's collection of expenses. Finally, we redirect the browser to the show action to display the updated account details, including the newly-added expense.

Go ahead and use the web form to add expenses to your accounts. You can get insight into what's going on behind the scenes (including what SQL is being run) by watching the log file. In a separate terminal, type:

$ tail -f log/development.log

Note that if script/server started with the LightTPD web server (rather than falling back onto WEBrick), the log will be tailed automatically for you.

Adding Business Logic

We have an account budget and a list of expenses, now all we need to do is total the expenses to see if we're within budget. That calculation is business logic that belongs in the Account model. So add the following method to the app/models/account.rb file:

def total_expenses
  expenses.inject(0) {|total, expense| total + expense.amount }
end

We use the expenses relationship to iterate through the account's expenses and accumulate the total. Don't worry about the syntax used here. The inject method is a Ruby idiom for accumulating while stepping through a collection of objects. The total_expenses method then returns the value of the last expression, which is the total of the expenses.

Using Helpers

At this point we need to update the view to show the total. But there's a bit of view logic that has to happen: If the expense total is greater than the account budget, then we want to display the total in red. Otherwise, we'll display the total in black. (The club treasurer likes it better this way.) We could put this logic in the template itself, but it's wise to keep logic out of the template. Instead, view-specific logic is best kept in a custom helper method. Add the following method to the app/helpers/expenses_helper.rb file:

def total(account)
  style = account.total_expenses > account.budget ? 'color: red' : 'color: black'
  %(<span style="#{style}">#{number_to_currency(account.total_expenses)}</span>)
end

This helper method simply generates styled HTML to show the total expenses for an account, depending on whether the account is within budget or not.

Finally, modify the template in the app/views/expenses/show.rhtml file to display the total using the total helper by adding a row to the bottom of the itemized expenses table, as follows:

<tr>
  <td align="right" colspan="3">
    <strong>Total</strong>: <%= total(@account) %>
  </td>
</tr>

Reload a show page, add enough expenses to bust the budget, and you should see a page similar to Figure 8.

Helper.jpg

Figure 8: Using business logic and a helper to show total expenses

Now that we have business logic, trivial as it may be, we're wise to test it.

Writing Tests

Rails makes doing the right things easy. One of those things is testing. In fact, when we generated the scaffolding Rails went ahead and created unit tests for our models and functional tests for our controllers. But the tests won't run until we create the test database.

Remember that Rails has three default runtime environments. When you run tests, they're run with Rails configured according to the test environment. In our case, the default database configured in the config/database.yml file for the test environment is called expenses_test. To create that database using the mysqladmin command-line tool, type

$ mysqladmin -u root -p create expenses_test

Now let's add a new unit test to cover the total_expenses method we added to the Account model. Add the following test method to the test/unit/account_test.rb file:

def test_total_expenses
  account = Account.new
  account.expenses.create :amount => 10.00
  account.expenses.create :amount => 20.50

  assert_equal 30.50, account.total_expenses
end

The test adds two expenses to an account, then checks that the total expenses is the sum of both expenses. Run all the unit tests by running 'rake test_units'.

$ rake test_units
...
Finished in 0.084544 seconds.

3 tests, 3 assertions, 0 failures, 0 errors

Rails encourages you to test more by making it easy to write and run tests. In addition to unit tests, functional tests let you easily generate simulated HTTP requests against a controller, then assert that the controller works as you'd expect. To run all the unit and functional tests in one fell swoop, simply run rake. (As it stands, the functional tests fail because we've modified the ExpensesController and its test is now out of step with the code.)

Ship It!

We're done with version 1.0 of the application. Now it's time to deploy it onto a production server and share it with the world. We need to do at least two things: configure a web server on the production machine and deploy our code onto that machine.

Configuring a Web Server

The easiest path is to use the LightTPD (or "lighty") web server: a production-quality web server that's very popular in the Rails community. In fact, Rails 1.0 will use the LightTPD web server by default if it's installed on the local machine.

The first time you start the application by typing script/server, a LightTPD configuration file is created in the config/lighttpd.conf file. By default, your Rails application will run in the development runtime environment. To switch to the production environment, simply update the config/lighttpd.conf file and set the RAILS_ENV environment variable as follows:

"RAILS_ENV" => "production"

The production environment changes the runtime characteristics of the application to suit production needs: the application runs faster, logging is less verbose, users see friendlier error pages, etc.

Deploying the Application

When it comes to deploying the code to the production server, and re-deploying your application with each new release, you're in for a treat. Capistrano (formerly SwitchTower) is an automated deployment utility that makes deploying Rails applications a breeze. To summarize, after installing Capistrano, deploying a new version of your application to one or more deployment machines is as simple as typing

$ rake deploy

If there's a problem with that version, you can easily roll back to the previous version by typing

$ rake rollback

When you run a SwitchTower task such as deploy or rollback, it's run in parallel (and atomically) on all machines that are assigned to that task in the Capistrano recipe file. Think about that: As the number of machines and processes in your deployment environment increases, your deployment procedure remains constant at a single command.

For complete instructions on how to configure LightTPD and use Capistrano, see the online recipe Deploying Rails with LightTPD.

Features for Version 2.0

In a short amount of time we've built a functional expense-tracking application. Our treasure's job is already made easier. Indeed, we're off to a good start, but here are just a few ideas we could implement by the end of the day:

  • Add Expense Categories. We may well want to associate expenses with expense categories. For example, an expense for printing flyers might go in the "Advertising" category. To do that, we'll need a relationship between the Expense model and a new Category model, for example.

  • Make the Interface More Dynamic. Currently, recording a new expense forces a reload of the page. Using the Ajax libraries and helpers integrated with Rails, when a new expense is added we could dynamically add the expense to the list of itemized expenses, update the total, and highlight the new item. All that would happen without needing to reload the page.

  • Export Accounts and Expenses. To integrate with other systems, users may want to export their data as XML, CSV, PDF, etc. To do that, we'll need to use builder-style templates supported by Rails. Instead of generating HTML, builder templates use Ruby code to generate output in arbitrary formats.

  • Support Multiple Users. If we want to bring this application to the masses, we'll need to add the concept of an account owner. Users should only be able to add expenses to accounts they own. To do that, we'll need to use sessions and filters to support logging in users and restricting their access.

Conclusion

Ruby on Rails is a highly-productive and industrial-strength web application framework. It scales from the simplest expense tracking application we built to full-featured applications with respectable numbers of users. As with any useful tool, it's not suited to handle every job, but it's a great complement to your Mac OS X development environment.

For More Information

This article has introduced you to Ruby on Rails. The following references will help you dig in deeper:

Updated: 2006-06-08