Putting REST on Rails
by Dan Kubb
|
Pages: 1, 2, 3, 4, 5, 6
The RESTful Rails Plugin
The RESTful Rails plugin adds several capabilities to normal Rails controllers that allow them to more effectively use HTTP. The focus of our example will be its most obvious feature, the per-HTTP method dispatching system. With it you will be able to run different code depending on the HTTP request method. A nice extra benefit is that it also takes care of handling the OPTIONS method, which will come in handy in our tests.
To install the plugin, type the following from the command line:
svn co svn://rubyforge.org/var/svn/restful-rails/trunk vendor/plugins/restful-rails
Or if your project is under version control with Subversion already, install it using the Rails plugin system instead:
ruby script/plugin install -x svn://rubyforge.org/var/svn/restful-rails/trunk
Both of these commands will retrieve the latest snapshot of the RESTful Rails plugin and place it in the directory vendor/plugins/restful-rails/.
Now that the plugin is installed, let's create the controller.
The Controller
To create the BookController controller, run the following command:
ruby script/generate controller Book
Test the new controller by running its functional tests:
rake test:functionals
This should pass.
Restful Routes
The BookController controller is going to handle requests for two URIs:
| /books/ | A collection of books |
| /books/{id} | A book identified by an ID |
Let's test that the URIs are being handled properly.
Replace the default test_truth method in test/functional/book_controller_test.rb with the following:
def test_routing
with_options :controller => 'book' do |test|
test.assert_routing 'books', :action => 'collection'
test.assert_routing 'books/1', :action => 'by_id', :id => '1'
end
end
This tests that the URIs route to the correct controller and action. Note the use of the new with_options method which saves you some typing by allowing you to specify the default options supplied to all the methods in the block.
rake test:functionals should fail because we haven't set up how URIs are routed to controllers yet.
To set up the routes we need to add a line to config/routes.rb inside the ActionController::Routing::Routes.draw block:
map.connect_resource :book
The connect_resource method is from the RESTful Rails plugin and it will set up routes that match our expected URI structure.
rake test:functionals should now pass.
Now that the routes are set up, we'll move on to the controller.
Making a RESTful Controller
At this point our controller doesn't have any knowledge of the RESTful Rails plugin. To add method dispatching and other features, you need to include RestController::Base at the top of app/controllers/book_controller.rb so it becomes:
class BookController < ApplicationController
include RestController::Base
end
We're now ready to add the :collection resource to the controller.
The collection Resource
The :collection resource represents a list of books. We want to be able to view the list with the GET method, and add to the list using POST. Let's add a test to test/functional/book_controller_test.rb that ensures these methods are handled:
def test_options_collection
options :collection
assert_response HTTP::Status::NO_CONTENT
assert_no_entity
assert_allowed_methods :get, :post
end
This tests that when an OPTIONS request is performed on the :collection, it should return a 204 No Content status code. Also the Allow header should identify GET and POST as allowed methods. The options, assert_no_entity and assert_allowed_methods methods are from the RESTful Rails plugin.
rake test:functionals should fail because there is no :collection resource in the controller yet.
First we'll stub out the :collection resource to make the functional tests pass for now. Add the following to app/controllers/book_controller.rb:
resource :collection do |r|
r.post do
end
end
rake test:functionals should now pass.
We only specified a stub handler for POST, but didn't say anything about GET. A RESTful Rails resource will handle GET just like a normal Rails action: if you don't use redirect or render, a template with the same name as the resource (if available) will be displayed.
Fixtures
We're almost at the point where we need test data to work with. We'll be reusing the Books fixture we created when testing our model. Simply add the following line above the setup method in test/functional/book_controller_test.rb:
fixtures :books