Rails in a nutshell Chapter 4. Active Resource

Translations of this material:

into Russian: Перевод "Rails in a nutshell Chapter 4. Active Resource". 15% translated in draft.
Submitted for translation by asplogika 01.11.2009 Published 2 years, 3 months ago.

Text

Chapter 4. Active Resource

Today more than ever, applications need to be able to interoperate not only with browser based clients, but with other applications. In order to accomplish interoperability web applications commonly offer a web service API. A web service API offers remote access to an application over a network, such as the internet. In the past web service APIs were commonly offered using complex and bulky SOAP based web services that were slow to implement and hard to understand. With the advent of Rails and the rise of REST based web services there has become a faster, simpler way to offer and consume web services.

Rails makes it easy to offer multiple different content types, or representations, of a particular resource through a single controller action. Being able offer multiple representations of a resource through a single controller action makes adding support for non browser based HTML clients as simple as adding a few lines of code that returns the resource in the representation the client requested. The controller layout and logic of the application is preserved with the added benefit of interoperability with new clients.

Active Resource provides the tools to quickly and easily consume REST based web services conforming to the Rails RESTful URI structure and protocol conventions. Active Resource automatically maps the response from any conforming service to rich Ruby objects. Active Resource also provides all the lifecycle methods needed to easily find, create, update, and delete resources without having to write any code.

Following is a simple definition for an Active Resource model:

class Person < ActiveResource::Base

self.site = "http://webservice.example.com"

end

With only this simple subclass of ActiveResource::Base and a site definition, which tells Active Resource the base endpoint URI of the remote web service, it is possible to find, create, update, and delete resources :

# Build and save a new person

person = Person.new(:name => "Cody", :title => "Developer")

person.save # => true

# Find an existing person

person = Person.find(1)

# Access a dynamically generated attribute

person.title # => "Developer"

# Update the attribute

person.title = "Unemployed"

# Save changes back to the remote service

person.save # => true

# Delete the person

person.destroy # => true

As you can see, the functionality of an Active Resource based model is very similar to that of an Active Record based model. The only difference being that the data source is a remote web service rather than a SQL database. This chapter provides coverage of all the main functionality of Active Resource and shows you how Active Resource works under the hood so that you can implement your own RESTful Rails web services.

Getting Started

Active Resource is included in Rails. If you'll be using Active Resource within a Rails project you need to ensure that :active_resource is not in the list of frameworks being excluded during initialization in your Rails config/environment.rb file:

# Ensure that :active_resource does not appear in the following line

config.frameworks -= [ :active_resource ]

Background

Active Resource is only a small piece of a much larger puzzle. Active Resource is dependent on the remote web service conforming to the Rails RESTful URL structure and data serialization conventions. The web service does not have to be provided by a Rails application, but this is the most common case because Rails makes it so easy to create a RESTful, web service enabled application.

There are two benefits to understanding Active Resource. The first benefit is the ability to quickly and easily create an Active Resource client for someone else's web service. The second benefit is being able to easily expose your own application to Active Resource clients. We're going to take a look under the hood at the communication between an Active Resource client and a Rails application in order to build a complete understanding of web services in Rails.

Active Resource is able to easily consume a web service offered by Rails application conforming to a RESTful architecture because of the consistent URL layout and behavior Representational State Transfer (REST) imposes on the application. REST works with the concept of resources, which are sources of information that can be uniquely addressed by a URI. Clients of an application exchange representations of resources via HTTP. Multiple different representations of a single resource may be available from a single URI. In terms of Rails, multiple different formats of a resource are available from a single controller action, based on the format requested by the client. Active Resource works by specifically asking the Rails application for an XML or JSON representation of a resource, whereas a client's browser would request the HTTP representation.

1 comment

1.

Glenn Goodrich Posted 4 days and 14 hours ago

HTTP or HTML?

Another principle of REST is the use of a constrained set of well defined operations. In Rails the well defined set of operations are based on the different HTTP verbs available. The HTTP verbs GET, POST, PUT, and DELETE map to the standard create, read, update, and delete (CRUD) operations offered by a Rails controller. Instead of specifying the operation to be performed through the request URI like show in /customers/show/15, a RESTful application would send an HTTP GET request to the resource's member URI /customers/15. A member URI, such as /customers/15, uniquely addresses an individual resource, whereas a collection URI, such as /customers, addresses the entire set of resources. Using the HTTP verb in conjunction with the resource URI to specify the desired operation eliminates redundancy and utilizes the well defined underlying HTTP protocol specifications.

Active Resource relies on the server providing the appropriate HTTP status code with each response. This allows Active Resource to accurately communicate any failure conditions that might occur. All status codes between 200 and 399 are considered successful, except for 301 and 302, which are redirects. One other exception is when the server returns the 422 Unprocessable Entity status code, which indicates that validation of the resource failed. In this case, Active Resource rescues the exception and parses the errors returned in the response body so that they are accessible to the client code. We'll take a closer look at validation and errors later in the chapter. Table 4.1, “Active Resource Exceptions” shows the exceptions raised for each different HTTP status code.

Table 4.1. Active Resource Exceptions

HTTP Status Code Exception

301, 302 ActiveResource::Redirection

400 ActiveResource::BadRequest

401 ActiveResource::UnauthorizedAccess

403 ActiveResource::ForbiddenAccess

404 ActiveResource::ResourceNotFound

405 ActiveResource::MethodNotAllowed

409 ActiveResource::ResourceConflict

422 ActiveResource::ResourceInvalid

401 - 499 ActiveResource::ClientError

500 - 599 ActiveResource::ServerError

Other ActiveResource::ConnectionError

In the following sections we'll take a look at the code for the client and server, as well as how they interoperate with one another. This will help to solidify your understanding of Active Resource and your knowledge of how Rails uses REST and HTTP protocol conventions to simplify web services. For more thorough coverage of REST in Rails see .

Client and Server

Let's construct a client for a RESTful Rails web service providing customer information. The web service application will be a simple Rails application consisting of a CustomersController and a Customer model. The client of the web service will be a simple Active Resource client named Api::Customer. The overall architecture of the client and server discussed in this section is shown in Figure 4.1, “High level overview of a RESTful Rails web service.”.

Figure 4.1. High level overview of a RESTful Rails web service.

High level overview of a RESTful Rails web service.

Web Service Client

Since the Active Resource client will most likely be written for someone else's web service, it is unlikely that the client and server will be contained within the same Rails project. You can place the code for Api::Custer into app/models/api/customer.rb in a Rails application, or if you are using Active Resource standalone, you can place the code anywhere you want. The Api::Customer Active Resource model looks as follows:

Example 4.1. Simple Active Resource model

module Api

class Customer < ActiveResource::Base

self.site = 'http://localhost:3000'

end

end

Note

By default, the enclosing element of a serialized Active Resource model is the underscored class name of the model. If the name of your class and the name of the element required by the remote web service do not match up then you can change the element name with the ActiveResource::Base.element_name class accessor:

class PersonResource < ActiveResource::Base

self.element_name = "person"

end

The module namespace Api has been added to the model in order to disambiguate it from the Active Record model of the same name in the web service's application. This allows the discussion of each model without confusion and allows for both models to exist in the same project. Having two Customer classes in the same project would not work because the two classes would generate a TypeError due to a superclass mismatch at runtime. Enclosing the Active Resource model within a module does not have any effect on the way an Active Resource model interacts with the remote web service.

Active Resource needs to be provided with the URI location of the web service, which is called the site. You can set the site for all subclasses by setting ActiveResource::Base.site. You can also set or override the site for any particular subclass by setting the site on the subclass. Active Resource uses the configured site along with conventions based on REST and the name of your Active Resource model's class name to resolve the correct URIs for the remote resource. Now we'll take a look at the implementation of the web service itself.

Web Service Server

The web service used for the examples in this chapter is provided by a simple Rails controller called CustomersController that conforms to the Rails RESTful URI structure. For the sake of simplicity the controller will only return XML representations of customer resources.

In order for Rails to properly route requests for a resource to the proper controller and action, the application needs a resource definition in config/routes.rb:

ActionController::Routing::Routes.draw do |map|

map.resources :customers, :collection => { :oldest => :get, :adults => :get }

end

The map.resources :customers call indicates that there is a CustomersController providing access to customer resources. The call defines the standard Rails routes for the customer resource and also defines two custom routes: oldest, and adults. The custom routes will be used for retrieving resources based on custom URIs in the next section. Both custom routes operate on the collection and only accept GET requests. The combination of HTTP verb and request URI is shown in Table 4.2, “RESTful Rails HTTP to controller action mapping” and is also shown in the comment above each controller action in Example 4.2, “A simple controller providing a RESTful web service.”.

Table 4.2. RESTful Rails HTTP to controller action mapping

HTTP Verb Request URI Controller Action

The id in URI would be replaced by the ID of the resource in a request.

GET /customers index

GET /customers/id show

POST /customers create

PUT /customers/id update

DELETE /customers/id destroy

GET /customers/oldest oldest

GET /customers/adults adults

The implementation of the controller used throughout the chapter can be seen in Example 4.2, “A simple controller providing a RESTful web service.”.

Example 4.2. A simple controller providing a RESTful web service.

class CustomersController < ApplicationController

# GET /customers.xml

def index

@customers = Customer.find(:all)

respond_to do |format|

format.xml { render :xml => @customers }

end

end

# GET /customers/1.xml

def show

Pages: ← previous Ctrl next
1 2 3 4