Recipe 6.6. Developing Your Rails Applications RESTfully


Problem

Contributed by: Christian Romney

You want to build your Rails application in a RESTful style.

Solution

Rails 1.2 has merged the code from Rick Olson's simply_restful plug-in, allowing you to build applications in a RESTful style. For this recipe, we'll create a new Rails project using Edge Rails. Enter the following commands at the console:

$ rails chess $ cd chess $ rake rails:freeze:edge             

There's a new scaffold for getting up and running quickly with REST-style resources. The following command generates a model, controller, and accompanying views and tests for this project:

$ ruby script/generate scaffold_resource Player             

As always, create a database for this application, and configure the database.yml file appropriately. Once that task has been completed, edit the generated create_players migration file:

db/migrate/001_create_players.rb:

class Player < ActiveRecord::Base; end class CreatePlayers < ActiveRecord::Migration   def self.up     create_table :players do |t|       t.column :title, :string       t.column :first_name, :string       t.column :last_name, :string       t.column :standing, :integer       t.column :elo_rating, :integer     end          Player.create(       :title      => 'GM',       :first_name => 'Veselin',       :last_name  => 'Topalov',       :standing   => 1,       :elo_rating => 2813     )          Player.create(       :title      => 'GM',       :first_name => 'Viswanathan',       :last_name  => 'Anand',       :standing   => 2,       :elo_rating => 2779     )       end   def self.down     drop_table :players   end end

Notice that scaffold_resource has added the following route for you:

config/routes.rb:

map.resources :players

Next, add a convenience method to the Player model:

app/models/player.rb:

class Player < ActiveRecord::Base   def display_name     "#{title} #{first_name} #{last_name} (#{elo_rating})"   end end

Now modify your index view so you can see something displayed right away:

app/views/players/index.rhtml:

<h1>Listing players</h1> <table> <% for player in @players %>   <tr>   <td><%= h(player.display_name) %></td>     <td><%= link_to 'Show', player_path(player) %></td>     <td><%= link_to 'Edit', edit_player_path(player) %></td>     <td><%= link_to 'Destroy', player_path(player),          :confirm => 'Are you sure?', :method => :delete %></td>   </tr> <% end %> </table> <br /> <%= link_to 'New player', new_player_path %>

Finally, migrate your database, and start up your development server (I use Lighttpd) with the following commands:

$ rake db:migrate  $ ruby script/server lighttpd -d             

You should now be able to point your browser to the /players path (http://localhost:3000/players on my machine). To really understand what's going on, you should have a peek at the PlayersController class (app/controllers/players_controller.rb). There's quite a bit going on in here, but the method names should be familiar if you've worked with Rails before. One of the new things you'll notice is the documentation of the HTTP verbs and paths that lead to the invocation of each controller action. For example, an HTTP GET request with a path of /players results in a call to the PlayersController#index method. If you click around the interface a bit, you'll notice that the show, edit, and new views don't display any fields in the player form. There are no fields because we generated the model, controller, and views in the same step. To remedy this, create a partial template to display the player form that will power the new and edit views:

app/views/players/_form.rhtml:

<div > <% form_for(:player, :url => path,      :html => { :method => method }) do |f| %>   <p>     <label for="player_title">Title:</label><br />     <%= f.text_field :title  %>   </p>       <p>           <label for="player_first_name">First Name:</label><br />     <%= f.text_field :first_name  %>   </p>       <p>       <label for="player_last_name">Last Name:</label><br />     <%= f.text_field :last_name  %>   </p>       <p>           <label for="player_standing">Standing:</label><br />     <%= f.text_field :standing  %>   </p>       <p>       <label for="player_elo_rating">ELO Rating:</label><br />     <%= f.text_field :elo_rating  %>   </p>   <p>     <%= submit_tag button_text %>   </p> <% end %> </div>

By parameterizing the URL, HTTP method, and submit button text, you've made the partial usable by multiple views. Update the new view first:

app/views/players/new.rhtml:

<h1>New player</h1> <%= render :partial => 'form',    :locals => {     :path => players_path,     :method => :post,     :button_text => 'Create'   } %> <%= link_to 'Back', players_path %>

Next, modify the edit view:

app/views/players/edit.rhtml:

<h1>Editing player</h1>    <%= render :partial => 'form',    :locals => {     :path => player_path(@player),     :method => :put,     :button_text => 'Update'   } %> <%= link_to 'Show', player_path(@player) %> | <%= link_to 'Back', players_path %>

Lastly, modify the show view to display the player information:

app/views/players/show.rhtml:

<div >   <p>Title: <%= h(@player.title)  %></p>       <p>First Name: <%= h(@player.first_name)  %></p>       <p>Last Name: <%= h(@player.last_name)  %></p>       <p>Standing: <%= h(@player.standing)  %></p>       <p>ELO Rating: <%= h(@player.elo_rating)  %></p>     </div> <%= link_to 'Edit', edit_player_path(@player) %> | <%= link_to 'Back', players_path %>

Refresh the page in your browser. Clicking around the application should now be more productive because the forms are fully functional.

Discussion

You've learned how to get up and running quickly with the new RESTful Rails approach. As you saw, most of the visible changes were to the generated controller code. A nice addition to this scaffold-generated code is the respond_to construct. Aside from the standard HTML representations that have always been a part of Rails, you now get an XML representation for free. In fact, one of the main ideas and benefits of the REST approach is the consistency in how resources are accessed and the ease with which different representations of the same resource are obtained. Not only do you get an HTML view of your models, but you get an XML-based API for free. The key to the workings of respond_to is the standard HTTP Accept header, which allows you to alter the representation according to the data format preferences expressed by the client.

Of course, most browsers only allow you to use the GET and POST HTTP verbs (the sole exception being Amaya, the W3C's browser/editor), so Rails includes some clever magic to simulate PUT and DELETE requests from the browser. This functionality necessarily extends into the Ajax helpers as well, rounding out the new REST features in the core of the Rails framework.

Using the new REST functionality in Rails, you can easily build RESTful web services that can be consumed by every major framework and programming language in use today. This eases the pain of interoperability, especially with the popular offerings from Microsoft and Sun (.NET and J2EE), without having to pay the hefty angle-bracket tax associated with WSDL and SOAP.

See Also

  • Amaya, http://www.w3.org/Amaya/Amaya.html

  • Section 6.2"

  • Section 6.5"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net