Recipe 6.4. Moving Beyond Simple CRUD with RESTful Resources


Problem

Contributed by: Diego Scataglini

You want to implement a REST API for your web application that goes beyond simple CRUD. For example, you want to add search functionality.

Solution

In this recipe, you'll be creating an interface to search for users. Generate and empty Rails application with the rails command and configure it to access your database. Next, run the scaffold_resource generator to create a User model:

$ ruby script/generate scaffold_generator User             

Now define some fields for the User model:

db/migrate/001_create_users.rb:

class CreateUsers < ActiveRecord::Migration   def self.up     create_table :users do |t|       t.column :login, :string       t.column :email, :string     end     User.create(:login => "diego",        :email => "diego@example.org")     User.create(:login => "rob",        :email => "rob@example.org")     User.create(:login => "chris",        :email => "chris@example.org")   end   def self.down     drop_table :users   end end

Now migrate your database:

$ rake db:migrate             

At this point, you have a wealth of helpers and named routes at your disposal. These can be used anywhere url_for is normally used, including as a parameter to: form_for, redirect_to link_to, link_to_remote, and many other helpers.

The named routes produced by the call to map.resources are user_url, users_url, new_user_url, and edit_user_url. To view the generated routes and associated helpers, start the development console:

$ ruby script/console development             

Now, enter the following command:

>> puts ActionController::Routing::Routes.draw do |map| ?>   map.resources :users >> end.map(&:to_s).sort edit_user_path edit_user_url formatted_edit_user_path formatted_edit_user_url formatted_new_user_path formatted_new_user_url formatted_user_path formatted_user_url formatted_users_path formatted_users_url hash_for_edit_user_path hash_for_edit_user_url hash_for_formatted_edit_user_path hash_for_formatted_edit_user_url hash_for_formatted_new_user_path hash_for_formatted_new_user_url hash_for_formatted_user_path hash_for_formatted_user_url hash_for_formatted_users_path hash_for_formatted_users_url hash_for_new_user_path hash_for_new_user_url hash_for_user_path hash_for_user_url hash_for_users_path hash_for_users_url new_user_path new_user_url user_path user_url users_path users_url

Next, modify the route to allow for searching:

config/routes.rb:

map.resources :users, :collection => {:search => :get}

Now the application has a way to search for users. In this case, the relative path /users;search is mapped to the search action in UsersController. Simply define a method called search within the controller:

app/controllers/users_controller.rb:

def search   @users = User.find(:all,               :conditions => ["login like ?", "#{params[:q]}%"])   respond_to do |format|     format.html  { render :action => "index" }     format.xml   { render :xml    => @users.to_xml }   end end

Lastly, you'll need to modify your view to display users:

app/views/users/index.rhtml:

<h1>Listing users</h1> <table>   <tr>   </tr>    <% for user in @users %>   <tr>     <td><%= auto_link(user.email) %></td>     <td><%= link_to 'Show', user_path(user) %></td>     <td><%= link_to 'Edit', edit_user_path(user) %></td>     <td><%= link_to 'Destroy', user_path(user), :confirm => 'Are you sure?', :method =>          :delete %></td>   </tr> <% end %> </table> <br /> <%= link_to 'New user', new_user_path %>

To test your new search method, start your development server:

$ ruby script/server             

Finally, navigate to http://localhost:3000/users;search?q=diego. Change the value of the q parameter to find other users.

Discussion

Rails' RESTful routes support several configuration options, including:


:controller

The name of the controller to use.


:singular

The name to be used for singular object paths; for example, 'user'


:path_prefix

Sets a prefix to be added to the route, which is useful when creating nested routes:

map.resources :subscriptions, :path_prefix => "/users/:user_id" 


:name_prefix

Used to disambiguate routes whenever a model is nested under multiple associated models. A common case is a polymorphic association:

map.resources :phone_numbers, :path_prefix => "companies/:company_id",                                :name_prefix => "company_phone_" map.resources :phone_numbers, :path_prefix => "people/:person_id",                                :name_prefix => "person_phone_"

The three most useful options are :collection, :member, and :new. They specify whether the route being defined should be used with collections, a single model, or just the new action, respectively. Each option accepts a single hash parameter that maps actions to HTTP verbs. You can use the option :any if you want an action to respond to any HTTP request method. Test them in your Rails console, and check the available helpers:

>> puts ActionController::Routing::Routes.draw do |map| ?>   map.resources :users, :new => {:new => :any,                                    :confirm => :put,                                    :save => :post} >> end
.map(&:to_s).sort
confirm_new_user_path confirm_new_user_url edit_user_path edit_user_url formatted_confirm_new_user_path formatted_confirm_new_user_url formatted_edit_user_path formatted_edit_user_url formatted_new_user_path formatted_new_user_path formatted_new_user_url formatted_new_user_url formatted_save_new_user_path formatted_save_new_user_url ...

As you can see, additional helpers are available because of the three custom HTTP verb mappings passed to the :new option. In fact, eight new helpers were added for each new handler. It's these highly configurable routing options that empower you to extend the REST API beyond simple CRUD actions in your application.

See Also

  • 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