ow! In a word, that was my first reaction to developing my first Web application using Ruby on Rails. This third of a series of articles on Ruby guides you through an exploration of the Ruby Web application framework called Rails, more formally, Ruby on Rails. If Ruby the programming language didn’t move your internal excitement meter, perhaps Ruby on Rails will. In the paragraphs to follow, you will develop the first part of a small, but complete, Web application in just one line of code! Don’t believe me? Read on and give it a try. Ruby is a metaprogramming language. Metaprogramming is a means of writing software programs that write or manipulate other programs thereby making coding faster and more reliable. The power of this technique shines through Rails and may give you a sense of the productivity gains offered through Ruby.
Setting up Ruby on Rails
In order to develop and run Web applications using Ruby on Rails, you will need Ruby. Ruby installation instructions were covered in Part 1 of this series. Once Ruby is installed, Ruby on Rails is most easily obtained through RubyGems. RubyGems is a Ruby package manager similar to package managers that you might have seen or used?like rpm, apt-get, and emerge. More information on RubyGems is available here. However, the Ruby on Rails download page, includes a link to get RubyGems and then you can use RubyGems to obtain Rails. In this article, I downloaded RubyGems version 0.8.11 offered from the Ruby on Rails Web site. Per the instructions, I simply downloaded the RubyGems zip file, extracted the zip file contents into my Ruby folder, and then ran the following Ruby script from the gems directory to install RubyGems:
C: uby ubygems-0.8.11> ruby setup.rb
With Gems installed, getting and installing Rails is a snap. Simply request Gems install Rails and all dependency packages as shown below:
C: uby ubygems-0.8.11> gem install rails --include-dependencies
Depending on the size and speed of your system and Internet connection, it will take a minute or two to get you up and running with Ruby on Rails using RubyGems. Using the same Gems command, you can also request Rails (or other Ruby package) updates as they come out too.
If you do not feel like using RubyGems, a standalone package of Ruby on Rails is available from the download site as well. Furthermore, for those on Windows platforms that want to get everything they need in one convenient download, a new download site has been setup for getting everything you need for Rails development with a single click. The Instant Rails Web site offers Ruby, Rails, Apache Web server, and MySQL database in a single “all preconfigured” download. This download allows you to unzip everything into a directory and run it. As it is already preconfigured, it does not require you to modify any environment variables or otherwise modify your system.
Rails and Databases
Ruby on Rails provides a model-view-controller (MVC) framework for Web application development. The “model” portion of the application can be persisted in a relational database. Rails supports a variety of RDBM systems including MySQL, PostgreSQL, SQLite, IBM DB2, Oracle, Microsoft SQL Server, and Sybase Adaptive Server (click here for more information on Rails supported databases and available drivers). Pure Ruby database drivers are provided with the Rails download for MySQL, PostgreSQL, and SQLite. For the purposes of this article, MySQL access was obtained by using the Rails’ provided Ruby driver. You will get more information on configuring Rails to use the database and the supporting Ruby on Rails API for wrapping the database in a bit. However, before beginning the development of your first Ruby on Rails application, you will need a database that contains a few database tables. Specifically, through this guide, you will build a personal contact application aptly called “Ruby Rolodex.” For this first Ruby on Rails application, you will need a set of database tables created from the model shown in Figure 1.
|Figure 1. Ruby Rolodex Database Model: Ruby on Rails provides a convenient means to access your relational database. In the sample application below, this model is used to persist contact information.|
I implemented the database in MySQL and the SQL code to restore the database using MySQL Administrator’s backup restore is included in the download zip file under the SQL folder.
With the Rails framework and a database in place, you are ready to create your first Ruby Web application. Don’t blink and miss all the fun!
Rails Project Creation and Rails Database Configuration
To say that you will be writing your first Rails Application isn’t quite accurate?Ruby on Rails provides a number of metaprogramming generators (scripts) that build a lot of your Web application for you. How much coding you do depends on how much you want to accept the default behavior and look/feel provided by the metaprogramming scripts.
Figure 2 shows the “Ruby Rolodex” application you’ll be creating will present users with a list of family, friend, business, and coworker contacts stored in a relational database. The application will also allow users to collect and store new contacts, remove old contacts, and even re-categorize the contacts.
|Figure 2. Ruby Rolodex Application: The Ruby Rolodex application allows users to create, look up, edit, or remove contacts and a contact’s address.|
To begin, execute the Ruby on Rails script that creates a new Web application project. Locate a folder on your system where you want to create the Rails project and store all of its associated files. Figure 3 shows a new “rolodex” project folder on the root C drive. From the directory in which you want to create the project folder, issue the following command to create the new “Ruby Rolodex” project: rails rolodex.
This command executes an already provided Ruby on Rails script that creates the entire Web application folder structure and necessary configuration files. Looking at the project folder, in this case the rolodex folder, you will find a set of folders and Rails script files as depicted in Figure 4.
With the application structure in place, you need to configure your Rails application to point to the database; in my case the database is in MySQL. Open the database.yml file located in the
olodexconfig folder with your favorite text editor. Note that this configuration file already has database bindings for MySQL, PostgreSQL, and SQLite. You might also take note of the fact that the configuration file has already been set up to utilize a “production,” “test,” and “development” database. To get the application to use the rolodex_development schema on a MySQL database with username and password of “root,” I had to modify the file as shown below:
development: adapter: mysql database: rolodex_development username: root password: root socket: /path/to/your/mysql.sock
With that small change, Ruby on Rails is ready to access your database and help you build other parts of your application.
|Figure 5. Creating the Contact Type Model Class: Running the Ruby on Rails script “scriptgenerate model ContactType” created a new Rails model class called ContactType in a file called contact_type.rb under the rolodexappmodels folder.|
Your First Rails Application
With the database configured in Rails, you are now ready to build your first application. Rails promotes the development of Web applications using MVC architecture. In this example, you’ll create your application by creating a model and working your way to the view. As you create this application, count the number of lines of code you have to write or even modify to get the application up and running.
To begin, let’s build simple some Web pages to create and modify contact types. Contact types, such as Friend, Coworker, Family, Business Associate, etc., are simple reference data used to type the contacts stored in the Ruby Rolodex database. The contact types will be stored in the contact_types database table. It is time to use one of Rails’ metaprogramming scripts to build the first components to display and edit contact types.
olodex directory in a command prompt, issue the following command:
ruby scriptgenerate model ContactType
The output of this script is shown in Figure 5. It results in a new Ruby “MVC” model class being created in the rolodexappmodels directory called contact_type.rb.
|Figure 6. Creating the Contact Type Controller Class: Running the Ruby on Rails script “scriptgenerate controller ContactType” created a new Rails controller class called ContactTypeController in a file called contact_type_controller.rb under the rolodexappcontroller folder.|
If you open contact_type.rb file with a text editor, you won’t find anything that looks too exciting. In fact, it doesn’t look like this model class does anything at all.
class ContactType < ActiveRecord::Baseend
Actually, this Ruby class now represents instances (rows) from the contact_types database table. You will see this more in a bit. By the way, in case you were not keeping track, that's zero (as in zip, nada, none) lines of code you have had to write so far.
|Figure 7. Starting the WEBrick Server: The WEBrick Web server is included with Ruby on Rails. Results of starting the server are displayed on the left. Results of hitting the Web server from a browser (http://localhost:3000) are displayed on the right.|
On to building the controller class in the MVC framework and again, time for another Rails script. At the command prompt, type the following command:
ruby scriptgenerate controller ContactType
Figure 6 depicts the results of running this command. You will note that, this time, the script generated a file (contact_type_controller.rb) in the rolodexappcontroller folder.You won't find much in the contact_type_controller.rb file either?it's pretty much just a class definition again. This time, however, you will have to add a line of code. Open the file and add the scaffold :contact_type line. The full class is listed below:
class ContactTypeController < ApplicationController scaffold :contact_typeend
Still keeping score? That's a whopping one line of code you had to write. Counting changes in the database configuration file, that's one line of code and a couple of configuration edits. And, my friend, that is it. You don't have to do anything for the view side of this MVC application.
|Figure 8. Rails Web Application for Contact Types: A complete Web site for adding, editing, showing, and removing contact types all developed with Rails script execution and one line of code.|
Running Your First Rails Application
Ruby on Rails is both a Web application development environment as well as a complete Web runtime environment. It ships with a built-in Web server called WEBrick. The WEBrick server is built in to each Rails project created using the rails script you used above to create your project. To start the Web server, open another command prompt and go to the rolodex directory. Then enter the following command to start the Web server:
Bring up a browser and enter http://localhost:3000 to test your server. You should see the Ruby on Rails welcome page as shown in Figure 7.Now, try out your contact type Web pages by entering the following URL: http://localhost:3000/contact_type/list. You can say it now…WOW! That's right. With one line of code, a couple of configuration changes and some execution of Ruby on Rails' metaprogramming scripts, you now have a little Web application to enter, modify, and remove contact types for your Ruby Rolodex application. If you preloaded the database from the SQL script provided, you should pages displayed that look similar to those displayed in Figure 8.The Magic Behind Rails
So how did this all happen with just one line of code change? In a word, it is metaprogramming. The model class you developed, or more precisely, generated from the script, inherits from Ruby on Rails' ActiveRecord class. ActiveRecord objects dynamically infer their attributes from the table definition that they are linked to and you linked the ContactType to the contact_type database table by using Rails naming conventions. Rails naming conventions work to bridge the plural/singular difference from a table containing many to a singular instance of the model. For example, note that the database table is contact_types while an instance of the model comes from ContactType class. Yep, Rails is smart enough to figure out that instances of ContactType should probably be stored in a contact_types database table.
|Figure 9. Rails Immediately Reflects Change: Changes to the database are immediately reflecting in Rails models and views as a result of using the Rails framework. Here, a new column called new_column is added to contact_type and it is immediately reflected in the view with no restart or code changes.|
The view and controller are no less remarkable in Rails. The ContactTypeController class generated by the Rails script descends from ActiveController. The one line of code you added to
Also, because of the dynamic nature of the Rails ActiveController and ActiveRecord, changes in the database are automatically reflected. Return to your database table and add a new column. For example, I added a new column called new_column as shown in Figure 9. If you refresh your browser, the new column will display in all the appropriate views?without even restarting the server or application.
Admittedly, more complex Rails applications are going to require more than one line of code to get them up and running. But with the default behavior in place, much of the code you write is to override the default behavior provided by Rails components.
For an example of overriding default behavior in Rails, modify the Web site you've created to remove the ability to delete contact types. Contact types are reference data for the soon-to-be-developed contacts application. If they were removed, then any contacts would reference an unknown contact type.
To begin, open the contact_type_controller.rb file in the rolodexappcontrollers folder. Add a new and empty implementation of the destroy method in your ContactType controller. The code, when complete, should look like that displayed below.
class ContactTypeController < ApplicationController scaffold :contact_type def destroy endend
With this code in place, return to your browser and attempt to delete or "destroy" one of the contact types. You will be greeted with the error message displayed in Figure 10.
|Figure 10. Attempt to Delete: With the destroy method overridden in the ContactTypeController, destroying contact types no longer works and this error message is displayed.|
The destroy method you provided overrides the default destroy method that Rails' scaffolding framework provides. The default method also uses a dynamically generated and provided "template" or view. So, when you override the default behavior in a Rails controller, you must also provide a new view.
To correct the current error of a missing template, go to the rolodexappviewscontact_type folder. In this directory, you will create a new view for the destroy functionality which you wish to override. Ruby on Rails uses HTML tagging that looks and behaves like Java Server Pages (JSP) or Active Server Pages (ASP). Create a new page called destroy.rhtml?notice the .rhtml file suffix. Enter the code shown below.
Removing contact types is not allowedBack
Other than formatting the response a little neater, this code does nothing, and that's the point. When the user attempts to destroy a contact type, they are told they can't do this. Also within the HTML code, notice the reference link that directs the user back to the contact type list page when clicked. Again, the Ruby on Rails URLs are simple and straightforward. Referencing or redirecting to them is easy to do?for both you and your users.
This is nice, but you probably also want to just remove the Destroy option on the contact type list view itself. For this, you need to also override the default list view of contact type. Create another page, list.rhtml, in the rolodexappviewscontact_type folder. The code for this page is below:
<% @contact_types.each do |contact_type| %> Name <% end %> <%= link_to contact_type.name, :action => "show", :id => contact_type.id %> <%= link_to "edit", :action => "edit", :id => contact_type.id %>
Add a new contact type
|Figure 11. Overriden List and Delete Pages: With the appropriate Rail scaffolding controller methods and template views overridden, destroying a contact type is no longer visible and no longer possible even if the user knows the appropriate URL.|
Again, this page helps to pretty up the default view, but it also removes the default CRUD behavior by removing the delete behavior. The <% %> and <%= %> tags allow Ruby code to be embedded in the HTML pages. The embedded Ruby code loops over a list of contact types. The code is making use of the instance variable @contact_types for the list of contact types. In the list method on the controller, the default behavior is to set this instance variable to the set of associated model objects, which, in this case, is a collection of ContactType objects. The link_to method calls create HTML references which take the user to the default show and edit pages for contact types and pass the appropriate contact type id to the controller to use to populate the page with data.
Return to the browser and try the Web application again. The "destroy" option is no longer available, but if a smart user attempts to call on the appropriate default scaffolding by entering http://localhost:3000/contact_type/destroy/
An Entire Rolodex App
Using the same Rails scripts and default implementation, default scaffolding, and CRUD activity can also be created for contacts and contact addresses. Simply run the four scripts below to set up the controller and models for contacts and contact addresses:
ruby scriptgenerate model Addressruby scriptgenerate model Contactruby scriptgenerate controller Addressruby scriptgenerate controller Contact
The code for the Address and Contact models gets a little more interesting because you need to associate the Contact to the ContactType you created above and you need to associate Contact instances with Addresses. Below is the code for both the Address and Contact models:
class Address < ActiveRecord::Base has_many :contactsendclass Contact < ActiveRecord::Base belongs_to :contact_type belongs_to :addressend
This code causes the generation of Rail Association methods that tie the objects together through associative relationships. Ruby generates methods to navigate from Contact to Address and ContactType and from Address to Contact using the foreign keys in the database. There are special methods for belongs_to, has_one, has_many, and has_and_belongs_to. With these code additions, you will find that a Contact's address is available through a call to @contact.address. To then get the Address's street, simply continue to follow the object/attribute chain: @contact.address.street.
On the Contact and Address controller side of things, there are a few more operations that you will want override. For example, when editing an existing Contact model object through the Contact controller, you want to set an attribute on the controller to the current Contact model object being edited. This can be found using a contact id parameter returned from the Contact's list view and the Contact model’s default find method. This allows the Contact controller to pass the currently select contact to the appropriate view. You will also want to set another attribute on the Contact controller to the list of current Contact Types to pass contact types to the edit view and fill a Contact Type drop down list on the view.
def edit @contact = Contact.find(@params['id']) @contact_types = ContactType.find_all end
Perhaps the trickiest of the controller override methods is the Address controller. When creating a new Address for a contact, the id for the Contact selected is used to find the contact and appropriately associate and save the Contact model object with the new Address model object when the Address is saved.
def create @address = Address.new(@params['address']) if @address.save @contact = Contact.find(@params['contact_id']) @contact.address_id = @address.id @contact.save end redirect_to :controller => 'contact', :action => 'list' end
The complete Contact and Address controller classes are shown in Listing 1.
The default Web interface provided by the scaffolding is a little stark. In order to dress it up?even a little bit?with drop down lists (for selecting the contact type for a Contact), HTML tables (for displaying the contact and address lists), or navigation to various parts of the Ruby Rolodex Web site, you need to override the edit, new, list, and show views for Contact and Address. Listing 2 shows the Contact list.rhtml view.
Most of that code goes to formatting the HTML output, but you will notice the fetch of the current contact information from the Ruby calls to