This tutorial covers what in my opinion is the most essential rails concept. Representational State Transfer, or REST, is a client-server web architecture in which clients can issue an HTTP request to the server, and the server will interpret the request and issue an HTTP response back to the client. The REST server holds the application’s “resources” (data), and provides REST actions for clients to interact with those resources. The only way the client can access the application’s resources is by using the provided REST actions; this translates to a more secure application, since clients shouldn’t be able to mess with the data in any way o ther than how the developers intended. This is often referred to as a REST API.
For example, in Twitter, Tweets are a REST resource, as are users and trends. When you go to your profile page on Twitter at a URL like twitter.com/codepron, you’re actually issuing an HTTP Get request asking the twitter servers for the latest tweets belonging to the user with username “codepron”. The string “codepron” is sent as a parameter, so that the server knows which user’s tweets to fetch. The server then issues an HTTP response, which you see as an HTML page with codepron’s latest tweets.
In this tutorial we’ll build an app that indexes books. We’ll scaffold a Book class with title and author attributes. Rails scaffolding will automatically include the basic CRUD actions (create, read, update, destroy) in the controller, along with the Book model and the index, new, show, and edit views. Then we’ll add a custom search action and add parameters to it in order to have an advanced search option where we can filter by author or title.
1. Create a rails app named ‘books’
$ rails books
2. Change directory into the app’s directory
$ cd books
3. Generate the book scaffolding
$ script/generate scaffold Book title:string author:string
3. Migrate database
$ rake db:migrate
4. Test app so far
$ script/server
You should now be able to see the books app index page at http://localhost:3000/books, and add, edit, and delete books using the generated forms. We will now add a search REST action to the app.
5. Add the search action definition to the books controller. We’ll show the search results in the index view rather than creating its own search results view.
def search
@books = Book.search(params[:query])
respond_to do |format|
format.html { render :template => 'books/index' }
format.xml { render :xml => @books }
end
end
6. The actual search algorithm will reside in the Book model as a class method.
class Book < ActiveRecord::Base
def self.search(query)
Book.all(:conditions => ['lower(title) LIKE :q or lower(author) LIKE :q', {:q => "%#{query}%"} ])
end
end
7. In config/routes.rb, add the search action declaration to the books resources declaration:
ActionController::Routing::Routes.draw do |map|
map.resources :books, :collection => { :search => :get }
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
Notice we specify to use the HTTP Get method near the end of the route declaration, so our search URLs are easily sharable and crawlable by search engines. You should always use the HTTP Get method for REST actions that are passive, that is, they don’t alter the database in anyway. Examples are the index and show actions, as well as the edit and new actions, since they only show you a page. The actual changes are made with the update and create methods, which, as you would expect, use the HTTP post method to send their data. You can tell when a browser uses the post method because it asks yow if you want to “re-send the data” when you press your browser’s Back button.
8. Now let’s add the search form to the index page right under the heading:
<h1>Listing books</h1>
<% form_tag search_books_path, :method => :get do -%>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag 'Search' %>
<% end -%>
<table>
<tr>
<th>Title</th>
<th>Author</th>
</tr>
<% @books.each do |book| %>
<tr>
<td><%=h book.title %></td>
<td><%=h book.author %></td>
<td><%= link_to 'Show', book %></td>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Destroy', book, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New book', new_book_path %>
If we run the app now, we should be able to search for with title or author like the search query.
To implement an advanced search option we will add 2 optional parameters to the search action: author and title.
9. Add the extra parameters to the search algorithm in the books model:
class Book < ActiveRecord::Base
def self.search(query, author, title)
conditions = ''
unless query == ''
conditions += '(lower(title) LIKE :q or lower(author) LIKE :q)'
end
unless author == ''
unless conditions == ''
conditions += ' and '
end
conditions += 'lower(author) LIKE :a'
end
unless title == ''
unless conditions == ''
conditions += ' and '
end
conditions += 'lower(title) LIKE :t'
end
if conditions == ''
Book.all
else
Book.all(:conditions => [conditions, {:q => "%#{query}%", :a => "%#{author}%", :t => "%#{title}%"}])
end
end
end
10. Edit the books controller search action to take the new parameters into account:
def search
@books = Book.search(params[:query],params[:author],params[:title])
respond_to do |format|
format.html { render :template => 'books/index' }
format.xml { render :xml => @books }
end
end
11. Now let’s create the new advanced options below our search bar in the books index view:
<h1>Listing books</h1>
<% form_tag search_books_path, :method => :get do -%>
search: <%= text_field_tag :query, params[:query] %><br /><br />
Advanced Options<br />
title: <%= text_field_tag :title, params[:title] %><br />
author: <%= text_field_tag :author, params[:author] %><br /><br />
<%= submit_tag 'Search' %>
<% end -%>
<table>
<tr>
<th>Title</th>
<th>Author</th>
</tr>
<% @books.each do |book| %>
<tr>
<td><%=h book.title %></td>
<td><%=h book.author %></td>
<td><%= link_to 'Show', book %></td>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Destroy', book, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New book', new_book_path %>
That’s it. You should now be able to run the app and use the optional advanced search parameters. As always, check out the demo app and the source code.