ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


What Is Ruby on Rails

by Curt Hibbs
10/13/2005
Ruby on Rails
Ruby on Rails is a web application framework written in Ruby, a dynamically typed programming language similar to Python, Smalltalk, and Perl.

In This Article:

  1. High productivity and reduced development time
  2. How does Rails do it?
  3. Rails components
  4. General features
  5. Active Record
  6. Action Pack
  7. Action Mailer
  8. Action Web Service
  9. Parting thoughts

It has been just over a year since the public debut of Ruby on Rails on July 25, 2004. In this short time, Rails has progressed from an already impressive version 0.5 to an awe-inspiring, soon-to-be-released version 1.0 that managed to retain its ease of use and high productivity while adding a mind-boggling array of new features. This article introduces the components of the upcoming Ruby on Rails 1.0 and shows you what the fuss is all about.

I don't intend to teach you how to use Rails to write web applications. For that, you probably ought to start with Rolling with Ruby on Rails Revisited and Rolling with Ruby on Rails Revisited, Part Two. This is an introduction and road map to the many features of Rails.

High Productivity and Reduced Development Time

At the feature level, Ruby on Rails doesn't offer anything new. Existing web application frameworks have done it all before. What's the big deal, then? The difference lies in how Ruby on Rails does it. When you can finish simple web apps in days instead of weeks and more complicated web apps in weeks instead of months, people notice!

This newfound attention would be short-lived if the resulting web apps were messy and hard to maintain or extend. Fortunately Ruby on Rails actually facilitates good programming practices, which leads to well-factored and easily maintained code.

Related Reading

RJS Templates for Rails
By Cody Fauser


Read Online--Safari Search this book on Safari:
 

Code Fragments only

The attention would also be short-lived if Ruby on Rails had no depth--that is, if once you tried to use it for anything beyond the simplest of web applications, you suddenly found yourself hitting a wall, unable to proceed due to inherent limitations. Experienced developers who know their way around the Web have repeatedly reported that this is not the case with Rails. For example, Tomcat, Ant, and the Servlet API author James Duncan Davidson recently wrote:

Rails is the most well thought-out web development framework I've ever used. And that's in a decade of doing web applications for a living. I've built my own frameworks, helped develop the Servlet API, and have created more than a few web servers from scratch. Nobody has done it like this before. That's not to say they got it all right. It's by no means "perfect". I've got more than a few nits and picks about how things are put together. But "perfect" isn't the point. The point is that it gets you up and going fast and has plenty of depth to keep you going. And Rails does that very well.

It may be hard to believe that this is possible without a significant downside. Fortunately, you don't have to take my word for it (or anyone else's). You can easily prove it to yourself in a day or less by going through a Ruby on Rails tutorial and then developing a modest web application of your own choosing. After all, seeing is believing! If you don't want see yourself be amazingly productive, you can always watch someone else do it in the new Rails video.

How Does Rails Do It?

Like a good recipe, Rails helps you achieve this new level of productivity by combining the right ingredients in the right amounts. Here are a few of the most important ingredients that make Rails what it is.

Ruby

Much of the power of Rails comes from the Ruby programming language. Ruby's unique design makes it easy to create domain-specific languages and to do metaprogramming. Rails takes full advantage of this.

Full-stack MVC framework

Rails is an MVC (model, view, controller) framework where Rails provides all the layers and they work together seamlessly. Other frameworks often implement only part of the solution, requiring the developer to integrate multiple frameworks into the application and then coerce them into working together. (For example, a Java developer might use Hibernate, Struts, and Tiles to get full MVC support.)

Convention over configuration

Convention over configuration means an end to verbose XML configuration files--in Rails, there aren't any! Instead of XML sit-ups, a Rails application uses a few simple programming conventions that allow it to figure everything out through reflection and discovery. For example, Rails uses intelligent reflection to automatically map database tables to Ruby objects. Your application code and your running database already contain everything Rails needs to know.

Less code

Following the simple Rails programming conventions does more than just eliminate the need for configuration files. It also means that Rails can automatically handle myriad lower-level details without you having to tell it to do so. This means that you write fewer lines of code to implement your application. Keeping your code small means faster development and fewer bugs, which makes your code easier to understand, maintain, and enhance.

Generators

Rails' use of runtime reflection and metaprogramming eliminates much of the boilerplate code that you would otherwise have to create. You can often avoid what little boilerplate code remains by using the built-in generator scripts to create it for you. This leaves you with more time to concentrate on the code that really matters--your business logic.

Zero turnaround time

The typical development cycle for testing a change to a web app has steps such as configure, compile, deploy, reset, and test. This is very time consuming. The Rails development environment has none of this. You simply make a change and see it work. Don't make the mistake of dismissing this as a minor point. It's hard to overstate how much this improves productivity and helps you maintain a creative flow without interruption.

Scaffolding

Rails can automatically create a full set of CRUD (Create, Retrieve, Update, and Delete) operations and views on any database table. This scaffolding can get you up and running quickly with manipulating your database tables. Over time, you can incrementally replace the generated CRUD operations and views with your own--presumably much prettier and more functional.

Rails Components

Rails itself consists of several components, which you can install and use separately. They are designed to work together seamlessly, though, and developers almost always use them together:

General Features

Rails has some general and some specific characteristics.

Web servers

Rails can run on just about any web server that implements CGI. However, the performance of CGI is notoriously bad, so the preferred deployment of a Rails application is to use FastCGI. There have been extensive tests of Rails application deployments with both Apache and LightTPD. There is also a newcomer, SCGI, which rivals the performance of FastCGI without the complicated setup.

During development, it is usually easiest just to use the WEBrick web server that comes built-in to Ruby.

Databases

Rails currently contains support for the following databases:

It takes approximately 100 lines of Ruby code to implement a database adapter, so adding to this list is not particularly onerous.

Debugging

When something goes wrong inside your Rails web app, you normally get a pretty detailed error display in your browser (when running in development mode). Often this is enough to diagnose the problem. When it's not, you have other options:

Custom (pretty) URLs

The default Rails mapping of URLs to controller actions is very simple and easy to understand. Rails tries very hard to present the user with pretty URLs. Rails URLs are simple and straightforward, not long and cryptic.

Even so, you can still customize your URLs by using the Rails routing facility. Rails' URL routing is flexible enough to allow you to create virtually any URL mapping scheme.

The Rails routing facility is pure Ruby code that even allows you to use regular expressions. Because Rails does not use the web server's URL mapping (like mod_rewrite in Apache), your custom URL mapping will work the same on every web server.

Unit testing

Rails actively facilitates unit testing:

You can find more details in A Guide to Testing Rails.

Active Record

Active Record is the part of Rails that handles the automatic mapping of your database tables to your runtime model objects. It's the M in MVC, and it is Rails' implementation of an ORM layer.

For all the common uses (and some of the not-so-common ones), you'll never need to see or use SQL when accessing or updating your database. Active Record's goal is specifically to work with relational databases; it does not try to abstract away its SQL usage. Active Record makes it easy to use your own custom SQL for those complicated cases where it is necessary. Even so, it is rarely needed.

Automated mapping

Active Record automatically maps tables to classes, rows to objects (instances of the model classes), and columns to object attributes. For example:

class Product < ActiveRecord::Base
end

automatically maps to the table named products, such as:

CREATE TABLE products (
  id int(11) NOT NULL auto_increment,
  name varchar(255),
  PRIMARY KEY (id)
);

which also automatically creates a name attribute that you can use like this:

my_product = Product.find(:first)
STDOUT.print my_product.name
my_product.name = "New Product Name"

Active Record uses English pluralization rules to map classes to tables. The model class name is singular and capitalized, while the table name is plural and lowercased. Examples include:

This singular/plural convention results in code that reads fairly naturally. Notice how this mapping is intelligent in its use of English pluralization rules. Also note that the class names use CamelCase (a Ruby convention), while the table names are all lowercase with underscores between words.

In cases where this does not work (such as interfacing with a legacy database with which you have no control over the names), you can also explicitly tell Active Record what name it should use.

The ActiveRecord::Base documentation explains more about Active Record's automatic mapping.

Associations

No table stands alone. Well, not usually, anyway. Most database applications use multiple tables with specific relationships between those tables. You can tell Active Record about these relationships in your model classes, and Active Record will generate a slew of navigation methods that make it easy for your code to access related data. The following models:

class Firm < ActiveRecord::Base
  has_many   :clients
  has_one    :account
  belongs_to :conglomorate
end

allow you to write code such as this:

my_firm = Firm.find(:last)
STDOUT.print my_firm.account.name
STDOUT.print my_firm.conglomerate.employee_count
for c in my_firm.clients
  STDOUT.print "Client: " + c.name + "\n"
end

This code will work correctly when the database has a clients and accounts table, of which each has a name column, and a conglomerates table that has an employee_count column.

The ActiveRecord::Associations documentation explains more about associations.

Validation

Because you don't want to store just any old thing in your database, you probably want to validate your data before you store it. Active Record contains a suite of macrolike validators that you can add to your model.

class Account < ActiveRecord::Base
  validates_presence_of     :subdomain, :name, :email_address, :password
  validates_uniqueness_of   :subdomain
  validates_acceptance_of   :terms_of_service, :on => :create
  validates_confirmation_of :password, :email_address, :on => :create
end

If the built-in validation macros can't do what you need, you can always write your own validation methods.

class Person < ActiveRecord::Base
  protected
    def validate
      errors.add_on_empty %w( first_name last_name )
      errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
    end

    def validate_on_create # only runs the first time a new object is saved
      unless valid_discount?(membership_discount)
        errors.add("membership_discount", "has expired")
      end
    end

    def validate_on_update
      errors.add_to_base("No changes have occurred") if unchanged_attributes?
    end
end

person = Person.new("first_name" => "David", "phone_number" => "what?")
person.save                         # => false (and doesn't do the save)
person.errors.empty?                # => false
person.count                        # => 2
person.errors.on "last_name"        # => "can't be empty"
person.errors.on "phone_number"     # => "has invalid format"
person.each_full { |msg| puts msg } # => "Last name can't be empty\n" +
                                            "Phone number has invalid format"
person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
person.save # => true (and person is now saved in the database)

If the validate method exists, Rails will call it just before writing any object to the database. If validation fails, it does not write the object to the database. validate_on_create and validate_on_update are similar, except that the first is called only before Rails creates a new record in the database, while the second is called only when Rails is about to update an existing record.

You can also validate a particular attribute only when some condition is true.

# Conditional validations such as the following are possible:
  validates_numericality_of :income,   :if => :employed?
  validates_confirmation_of :password, :if => :new_password?

# Using blocks:
  validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }

The ActiveRecord::Validations documentation explains more about validation.

Callbacks

As Active Record creates and destroys model objects and creates and updates them in the database, you can monitor these events in the object's life cycle using callbacks. You can use callbacks to handle complex business logic, modify data before Rails writes it to the database (or after Rails reads it from the database), or just about anything else you like.

For example, the save method that saves a model object's data to the database has eight callbacks defined:

  1. before_validation
  2. before_validation_on_create
  3. after_validation
  4. after_validation_on_create
  5. before_save
  6. before_create
  7. after_create
  8. after_save

This gives you fine-grained control over your model objects when you need it.

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  def before_validation_on_create
    self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

The ActiveRecord::Callbacks documentation covers callbacks.

Transactions

A transaction is necessary when you have multiple database operations that all must succeed before the data in the database can change. If any one of them fails, the data in database should not change. Use transaction blocks to ensure this.

transaction do
  david.withdrawal(100)
  mary.deposit(100)
end

The database-level transaction shown above will prevent the withdrawal from David's account if the deposit into Mary's account should fail. It will not, however, protect the david and mary objects from being modified. To do that, you must use object-level transactions.

Account.transaction(david, mary) do
  david.withdrawal(100)
  mary.deposit(100)
end

Any failure in this code will roll back the value of the objects as well as the database.

The ActiveRecords::Transactions documentation explains more.

Much, much more

There is a lot more to Active Record than I can cover here. To learn more, consult the Active Record API.

Action Pack

Action Pack implements both the view and controller part of Rails.

View templates

View templates specify the HTML to return in response to a browser request. View templates are rhtml files (HTML with embedded Ruby) that are very similar to ASP or JSP files. Text within <% %> is Ruby code to execute, and text within <%= %> is also Ruby code to execute and substitute the results back into the HTML.

<html>
  <head>
    <title>Invoices for <%= @name
%></title>
  </head>
  <body>
    <% render_partial "invoices_by_customer"
%>
  </body>
</html>

By default, Rails will try to find a template whose name matches the currently executing action. If, for example, Rails executes an edit action in your InvoiceController, then it will attempt to find and render the view template .../app/views/invoices/edit.rhtml.

You can also build up XML (or HTML) output programmatically in your controller action. This is useful, for example, for building RSS feeds or responding to XML-RPC requests. In the following example, xm is an XmlMarkup object.

xm.em("emphasized")  # => <em>emphasized</em>
xm.em { xmm.b("emp & bold") }   # => <em><b>emph & bold</b></em>
xm.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A
Link</a>
xm.div { br }                   # => <div><br/></div>
xm.target("name"=>"compile", "option"=>"fast")
                                # => <target option="fast" name="compile"\>
                                # NOTE: order of attributes is not specified.

xm.instruct!                    # <?xml version="1.0" encoding="UTF-8"?>
xm.html {                       # <html>
  xm.head {                     #   <head>
   xm.title("History")          #     <title>History</title>
 }                              #   </head>
  xm.body {                     #   <body>
    xm.comment!  "HI"           #     <!-- HI -->
   xm.h1("Header")              #     <h1>Header</h1>
   xm.p("paragraph")            #     <p>paragraph</p>
 }                              #   </body>
}                               # </html>

URL routing

An incoming URL always maps to some action within a controller. A controller is simply a Ruby class, and each action implemented by the controller is a public method within the controller class. The default mapping from URL to action method is (in "Rails-speak"):

/:controller/:action/:id

This is easiest to explain by an example. If Rails received the URL http://myapp.com/invoices/show/37, Rails would route this to a controller class named InvoicesController and within that class to a method named show. Rails would also pass the 37 to the method as the id member of the parameter hash that also holds the values of query parameters and form fields. The code might look like this:

class InvoicesController
  def show
    @invoice = Invoice.find(params[:id])
  end
end

Because actions are methods grouped within a controller (instead of separate command objects), they can easily share helper methods.

If the default URL routing does not meet your needs, you can easily specify your own routing rules, even using regular expressions. Because Rails implements its own URL routing, you don't need to mess with Apache's mod_rewrite, and your routing rules will work the same under all web servers.

Rails routing rules are Ruby code. Here is an example:

map.connect 'date/:year/:month/:day',
     :controller => 'blog',
     :action => 'by_date',
     :month => nil,
     :day => nil,
     :requirements => {:year => /\d{4}/,
                       :day => /\d{1,2}/,
                       :month => /\d{1,2}/}

With this routing rule, the following URLs are valid:

http://myblog.com/date/2005
http://myblog.com/date/2005/08
http://myblog.com/date/2005/08/01

This rule decomposes a URL containing a date that, perhaps, a blog might use to display the postings for a particular date. A URL that matches this form will map to the BlogController class and the by_date method. The parameter hash will contain values for a four-digit year (/\d{4}/ is a Ruby regular expression), a two-digit month, and a two-digit day. Further, the month and day are optional; if no values are present, the parameter hash will contain the default value of nil.

See the Routing in the Rails manual to learn more.

Filters

Filters allow you to run preprocessing code before Rails executes an action and post-processing code after it completes an action. They are useful for such things as caching or authentication before calling the action, and compression or localization of the response after calling an action. The before_filter processing can either allow the action to be called normally by returning true, or abort the action by returning false (or by performing a render or redirect operation).

For example:

class BankController < ActionController::Base
  before_filter :audit

  private
    def audit
      # record the action and parameters in an audit log
    end
end

class VaultController < BankController
  before_filter :verify_credentials

  private
    def verify_credentials
      # make sure the user is allowed into the vault
    end
end

The ActionController::Filters documentation has more details.

Helpers

Helpers are smart methods (functions) that help your view templates generate HTML. They know to use your model objects and controller classes to create just the right HTML and, in the process, save you a lot of time and effort. Of course, this also means you write fewer lines of code, but I bet you already guessed that.

You can write your own helpers, but as you might expect, several come built into Rails. The link_to helper, for example, generates anchor tags that create links to controllers and actions. For example:

<%= link_to "Help", { :action => "help" } %>

creates a link to the help action (method) in the current controller (whatever controller is handling the current request). The text of the link (what the user sees) is Help.

<%= link_to "Master Help Index",
           { :controller => "help", :action => "index" }
%>

This creates a link to the index method in the HelpController class. The text of the link will be Master Help Index.

<%= link_to "Delete this invoice",
           { :controller => "invoices",
             :action => "delete",
             :id => @invoice.id },
             :confirm => "Are you sure you want to delete this?"
%>

This creates a link to the delete method in the InvoicesController class, and passes an id parameter (presumably the ID of the invoice to delete). This also uses a special confirm option that creates JavaScript to pop up a confirmation dialog letting the user continue or abort.

There is a substantial set of helpers for creating form fields to display and update values in your Active Record model objects, which effectively means values in your database. Assume that your database has a people table with columns for the name, the password, a description, and a Boolean value to indicate whether the person is single or married (OK, so this is a strange table--just humor me). Here's a partial view template with a sampling of form tags that could be used. (Assume that the variable @person contains a person object read from a row of the people table.)

<form action="save_person" method="post">
  Name:
  <%= text_field "person", "name", "size" => 20 %>

  Password:
  <%= password_field "person", "password", "maxsize" => 20 %>

  Single?:
  <%= check_box "person", "single" %>

  Description:
  <%= text_area "person", "description", "cols" => 20 %>

  <input type="submit" value="Save">
</form>

That code generates the following HTML:

<form action="save_person" method="post">
  Name:
  <input type="text" id="person_name" name="person[name]" size="20" value="<%= @person.name %>" />

  Password:
  <input type="password" id="person_password" name="person[password]"
         size="20" maxsize="20" value="<%= @person.password %>" />

  Single?:
  <input type="checkbox" id="person_single" name="person[single] value="1" />

  Description:
  <textarea cols="20" rows="40" id="person_description"
           name="person[description]">
    <%= @person.description %>
  </textarea>

  <input type="submit" value="Save">
</form>

There are helpers to create the option tags for a select list from a collection of rows read from the database; helpers for working with dates, numbers, and currency; and much more.

There are different categories of built-in helpers, in several actual files. To learn more about them, see:

Ajax & JavaScript helpers

Rails integrates the Prototype JavaScript library to implement its browser-side Ajax support, visual effects, and drag-and-drop abilities.

Rails has a simple, consistent model for how it implements Ajax operations. Once the browser has rendered and displayed the initial web page, different user actions cause it to display a new web page (like any traditional web app) or trigger an Ajax operation:

  1. A trigger action occurs. This could be the user clicking on a button or link, the user making changes to the data on a form or in a field, or just a periodic trigger (based on a timer).
  2. The client sends data associated with the trigger (a field or an entire form) asynchronously to an action handler on the server via XMLHttpRequest.
  3. The server-side action handler takes some action based on the data, and returns an HTML fragment as its response.
  4. The client-side JavaScript (created automatically by Rails) receives the HTML fragment and uses it to update a specified part of the current page's HTML, often the content of a <div> tag.

The real beauty is how easy Rails makes it to implement all of this in your web application. The following simple example adds new items to a list:

<html>
  <head>
    <title>Ajax List Demo</title>
    <%= javascript_include_tag "prototype" %>
  </head>
  <body>
    <h3>Add to list using Ajax</h3>
    <%= form_remote_tag(:update => "my_list",
                           :url => { :action => :add_item },
                           :position => "top" ) %>
      New item text:
      <%= text_field_tag :newitem %>
      <%= submit_tag "Add item with Ajax" %>
    <%= end_form_tag %>
    <ul id="my_list">
      <li>Original item... please add more!</li>
    </ul>
  </body>
</html>

The article Ajax on Rails and the JavaScript Helpers API explain more about Rails' Ajax support.

The Prototype library also provides the Rails developer with a wealth of browser-side visual effects. Script.aculo.us provides documentation and live demos of the Prototype library effects. Prototype also lets you easily add drag-and-drop features to your web application. Script.aculo.us also has live demos and documentation for Prototype's drag-and-drop support.

Layouts

Layouts let you specify a common set of display elements for every page rendered by a controller. This is typically useful for common headers, footers, and sidebars. By default, Rails looks in its layouts directory for an rhtml file whose name matches the controller's name. A layout template might look like this:

<html>
  <head>
    <title><%= @page_title %></title>
  </head>
  <body>
    <div>The header part of this layout</div>
    <div><%= @content_for_layout %></div>
    <div>The footer part of this layout</div>
  </body>
</html>

Rails will substitute the HTML that an action renders into the above layout where it says @content_for_layout.

The controller can also directly specify the name of the layout template to use for all its actions. This makes it easy to use the same layout for multiple controllers. You can even dynamically choose a layout template at runtime. For example, you could use one layout for logged-in users and a different one for anonymous users.

To learn more about layouts, see the ActionController::Layout documentation.

Components and partials

Components and partials allow you to modularize your view templates.

The simplest are partials, which allow you to extract a common piece of a template into a separate file and then render it from many other templates (or many times within a single template). Partial templates always have a leading underscore on their filenames to distinguish them from full templates.

A typical use of a partial is for rendering a collections of items.

<% for ad in @advertisements %>
  <%= render :partial => "adview", :locals => { :item => ad } %>
<% end %>

This renders the partial template _adview.rhtml multiple times (once for each ad in the collection @advertisements). For each rendering, Rails will pass _adview.rhtml a local variable named item that contains the ad object to use.

The ActionView::Partials documentation explains more. Pointstorm has a more in-depth presentation of Ruby partials, but it is a little out of date because it uses the older, obsolete syntax for rendering partials.

Components are similar to partials in that they embed the rendering of another template within the current template. The difference is that you specify the name of a controller and action, and its template is the one to render and insert into the current template.


<%= render_component :controller => "calendar", :action => "today" %>

See Components in the Rails Manual to learn more.

Scaffolding

Scaffolding allows you to get an instant implementation of CRUD (Create, Retrieve, Update, and Delete) operations on any database table. They're not pretty, but they do give you immediate web-based access to your tables. Over time, you can incrementally replace the generated CRUD operations and views with your own.

Rails supports static or dynamic scaffolding. Static scaffolding physically generates model, controller, and template files. This lets you see how it works and start tweaking this existing code. To generate static scaffolding, navigate in the file system to the root of your Rails web application and run a command similar to:

$ ruby script/generate scaffold invoices

This will look for an invoices table in your database and use its schema to generate the model, the controller, all of the view templates, the unit test skeletons, and more. The command displays a list of all the files that it generates.

The advantage of static scaffolding is that you get to see and modify the generated code. The disadvantage is that if you change you database table, the scaffolding will not reflect those changes. However, if you haven't modified the scaffolding, you can just regenerate it.

Dynamic scaffolding does not generate any files. Rails simply creates what it needs dynamically as your web app runs. This means that every time you change the database table, the scaffolding's CRUD views immediately show those changes. To request dynamic scaffolding, place a single line of code in the controller:

class AccountController < ActionController::Base
  scaffold :account
end

Much, much more

As with Active Record in the previous section, there is a lot more to Action Pack than I can cover here. To learn more, look at the Action Pack API.

Action Mailer

Action Mailer is a simple facility for sending and receiving email in your web application. Here's a method that sends an email with an attachment:

# send email with attachment
def signup_notification(recipient)
  recipients recipient.email_address_with_name
  subject "New account information"
  from "system@example.com"

  attachment :content_type => "image/jpeg", :body => File.read("an-image.jpg")

  attachment "application/pdf" do |a|
    a.body = generate_your_pdf_here()
  end
end

To learn more, see the Action Mailer API, and Chapter 19 of the book Agile Web Development with Rails.

Action Web Service

Action Web Service implements server-side support for the SOAP and XML-RPC web service protocols and makes it easy for you to create web service APIs and publish them via WSDL.

Here is part of the MetaWeblog API as implemented by Typo (open source weblog software written in Rails):

class MetaWeblogApi < ActionWebService::API::Base

  api_method :getRecentPosts,
    :expects => [ {:blogid => :string},
                  {:username => :string},
                  {:password => :string},
                  {:numberOfPosts => :int} ],
    :returns => [[MetaWeblogStructs::Article]]

  api_method :deletePost,
    :expects => [ {:appkey => :string},
                  {:postid => :string},
                  {:username => :string},
                  {:password => :string},
                  {:publish => :int} ],
    :returns => [:bool]
end

class MetaWeblogService < TypoWebService
  web_service_api MetaWeblogApi

  def getRecentPosts(blogid, username, password, numberOfPosts)
    articles = Article.find_all(nil, "created_at DESC", numberOfPosts)
    articles.to_a.collect{ |c| article_dto_from(c) }
  end

  def deletePost(appkey, postid, username, password, publish)
    article = Article.find(postid)
    article.destroy
    true
  end
end

This snippet shows only two of the seven API methods defined in this class by Typo.

To learn more, see the Action Web Service Manual.

Parting Thoughts

You can usually divide web application frameworks and the developers who use them into two distinct categories. At one end of the spectrum, you have the heavy-duty frameworks for the "serious" developers, and at the other end you have the lightweight, easy-to-use frameworks for the "toy" developers. Each of these groups generally regards the other with disdain.

One of the most interesting things is that Rails is attracting developers from both camps. The high-end developers are tired of the repetitive, low-productivity routine that they have been forced to endure, while the low-end developers are tired of battling a mess of unmanageable code when their web apps move beyond the simple. Both of these disparate groups find that Rails provides sustainable relief for their pain. I don't know about you, but I find this quite remarkable!

At the moment, Ruby on Rails barely captures a tiny percentage of web development projects. Yet it is rapidly gaining mind share, and many respected software development leaders have been testing the waters with Rails and publicly singing its praises.

Perhaps it's time that you too checked out Rails to see firsthand what the fuss is all about.

Acknowledgments

Most of the sample source code shown in this article came from the Rails API documentation, with permission.

Resources

Curt Hibbs has been a consultant to well-known companies like Hewlett Packard, Intuit, Corel, WordStar, Charles Schwab, Vivendi Universal, and more. He now works as a Senior Software Engineer for The Boeing Company in St. Louis.


Return to ONLamp.com.

Copyright © 2009 O'Reilly Media, Inc.