9780596521424
activeresource_id59243.html

Chapter 12. Active Resource

Rails makes it really easy to provide and consume RESTful web APIs by way of a module called Active Resource.

Note

In Rails 4, Active Resource has been extracted out of the core framework and into a gem you can bundle into your application should you choose to include it.

Rails makes it easy to offer multiple different content types, or representations, of a particular resource through a single controller action. Being able to 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 in the remote application:

# 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.

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 HTML representation.

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 12.1, “Active Resource Exceptions” shows the exceptions raised for each different HTTP status code.

Table 12.1. Active Resource Exceptions

HTTP Status CodeException
301, 302ActiveResource::Redirection
400ActiveResource::BadRequest
401ActiveResource::UnauthorizedAccess
403ActiveResource::ForbiddenAccess
404ActiveResource::ResourceNotFound
405ActiveResource::MethodNotAllowed
409ActiveResource::ResourceConflict
410ActiveResource::ResourceGone
422ActiveResource::ResourceInvalid
401 - 499ActiveResource::ClientError
500 - 599ActiveResource::ServerError
OtherActiveResource::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.

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 12.1, “High level overview of a RESTful Rails web service.”.

Figure 12.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::Customer 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 12.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. First we'll configure logging in the client so that each request and response the client makes is recorded in the configured log file. Then we'll take a look at the implementation of the web service itself.

Logging Requests

Logging is a great way to record a history of events that occur in an application. By default, a logger is not configured for Active Resource, but it is easy to configure one. A logger is configured in Active Resource by setting the ActiveResource::Base.logger class method with an instance of the logger you want to use.

If you're using Active Resource from within a Rails project to communicate with a remote API then you just need to assign the Rails logger to also handle Active Resource logging. This is best done in an initializer, such as config/initializers/active_resource.rb, and looks as follows:

ActiveResource::Base.logger = Rails.logger

Now with you'll get entries in your logs for each request made. The log entries will look something like:

GET http://localhost:3000/customers.xml
--> 200 OK 301 (93.2ms)
GET http://localhost:3000/customers/1.xml
--> 200 OK 141 (51.1ms)
POST http://localhost:3000/customers.xml
--> 201 Created 143 (164.5ms)
PUT http://localhost:3000/customers/4.xml
--> 200 OK 1 (94.6ms)

Each log entry contains the HTTP verb and URI endpoint of the request on the first line and the HTTP status code, HTTP status phrase, content length on the second line. In brackets on the second line is also the entire elapsed time of the remote request in milliseconds.

Setting up a logger is slightly more involved if your Active Resource client is not part of a Rails application, but it still isn't too complex. The code would look something like the following:

require 'rubygems'
require 'logger'
require 'active_resource'
require "active_resource/railties/log_subscriber"

ActiveResource::Base.logger = Logger.new(STDOUT)
ActiveResource::Base.logger.level = Logger::DEBUG

Rails::LogSubscriber.add(:active_resource, ActiveResource::Railties::LogSubscriber.new)

First, all the files needed for the example are required. Then a new instance of Ruby's Logger is created and set to the Logger::DEBUG log level. Then the logger is assigned to the ActiveResource::Base.logger class accessor. Finally, a new instance of the ActiveResource::Railties::LogSubscriber class, which uses the logger assigned to ActiveResource::Base.logger for logging, is created and added as a subscriber to Active Resource log events.

Defining a Schema

Since Active Resource is so dynamic, it can be useful to define a schema for your resource. Defining a schema is completely optional, but allows you to define the expected attributes you Active Resource model has. This is useful when your Active Resource objects are quite rich in behavior and are not just simple data objects. To fully utilize client-side validations, which we'll take a look at in a later section, you'll also need to define a schema in order to ensure that the attributes you're validating exist at the time of validation. Without defining a schema your validations would raise NoMethodError exceptions because the attributes you're trying to validate don't exist.

You define a schema using the schema class method on your Active Resource model class. There are three types of attribute you can define: string, integer, and float. You can define each attribute separately:

class Customer < ActiveResource::Base
  schema do
    attribute 'name', 'string'
    attribute 'age', 'integer'
  end
end

Defining the attributes means that the object will respond_to? the attribute:

customer = Customer.new
customer.respond_to?(:name)  # => true
customer.respond_to?(:age)   # => true
customer.respond_to?(:title) # => false

You can also define a list of attributes at once:

class Customer < ActiveResource::Base
  schema do
    string 'name', 'company'
    integer 'age'
  end
end

As you can see from the example, you just call a method for the attribute's type, such as integer, instead of the attribute method.

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:

Example::Application.routes.draw do |map|
  resources :customers do
    collection do
      get :oldest, :adults
    end
  end
end

The 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 12.2, “RESTful Rails HTTP to controller action mapping” and is also shown in the comment above each controller action in Example 12.2, “A simple controller providing a RESTful web service.”.

Table 12.2. RESTful Rails HTTP to controller action mapping

HTTP VerbRequest URIController Action
The :id in URI represents the ID of the resource in a request.
GET/customersindex
GET/customers/:idshow
POST/customerscreate
PUT/customers/:idupdate
DELETE/customers/:iddestroy
GET/customers/oldestoldest
GET/customers/adultsadults

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

Example 12.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
    @customer = Customer.find(params[:id])
    
    respond_to do |format|
     format.xml { render :xml => @customer }
    end
  end

  # POST /customers.xml
  def create
    @customer = Customer.new(params[:customer])
  
    respond_to do |format|
      if @customer.save
        format.xml do
          render :xml => @customer, :status => :created, :location => @customer
        end
      else
        format.xml do
          render :xml => @customer.errors, :status => :unprocessable_entity
        end
      end
    end
  end

  # PUT /customers/1.xml
  def update
    @customer = Customer.find(params[:id])

    respond_to do |format|
      if @customer.update_attributes(params[:customer])
        format.xml { head :ok }
      else
        format.xml do
          render :xml => @customer.errors, :status => :unprocessable_entity
        end
      end
    end
  end

  # DELETE /customers/1.xml
  def destroy
    @customer = Customer.find(params[:id])
    @customer.destroy

    respond_to do |format|
      format.xml { head :ok }
    end
  end
  
  # GET /customers/oldest.xml
  def oldest 
    @customer = Customer.find(:first, :order => 'age DESC')
    
    respond_to do |format|
      format.xml { render :xml => @customer }
    end
  end
  
  # GET /customers/adults.xml
  def adults
    @customers = Customer.find(:all, :conditions => 'age >= 18')
    
    respond_to do |format|
      format.xml { render :xml => @customers }
    end
  end
end

As mentioned before, the controller only returns responses in XML format. Most controllers would also support other representations of the resources, such as HTML, for non web service based clients. The details of the responses and codes returned by each action will be explained in more details in the following sections.

Example 12.3. Active Record Customer class on the server

class Customer < ActiveRecord::Base
  validates_presence_of :name
  validates_numericality_of :age, :greater_than => 0
end

The model used by the server in the examples is seen in Example 12.3, “Active Record Customer class on the server”. The model defines a couple of validations, which will be used to illustrate how validation and exceptions work when using Active Resource.

Finding Resources

Active Resource reads resources by sending GET requests to the collection or member URLs of a resource. Any call to find that acts as a query will be sent to the collection URI of the resource because the exact resource isn't yet known. Finding a resource by ID sends the request to the member URI for the resource, since the exact desired resource is known.

Finding all resources

Active Resource makes it easy to find:

customers = Api::Customer.find(:all)

Active Resource sends a GET request to the resource's collection URL:

GET /customers.xml HTTP/1.1

Active Resource appends .xml to the request path to indicate that the desired response format is XML, which is the default. As you can see from Example 12.2, “A simple controller providing a RESTful web service.”, this request routes to the index action of the CustomersController on the remote web service. The index action returns all customers in the database serialized as XML with the HTTP status code 200 OK. The response body looks as follows:

<?xml version="1.0" encoding="UTF-8"?>
<customers type="array">
  <customer>
    <age type="integer">30</age>
    <id type="integer">1</id>
    <name>Cody</name>
  </customer>
  <customer>
    <age type="integer">28</age>
    <id type="integer">2</id>
    <name>Tobi</name>
  </customer>
</customers>

The returned XML response is automatically mapped by Active Resource into objects complete with dynamic accessors for all of the attributes of the object. Inspecting the collection of customers would then look as follows:

[#<Api::Customer:0x105d2d9e8
  @attributes={"name"=>"Cody", "id"=>1, "age"=>30},
  @prefix_options={}>,
 #<Api::Customer:0x105d2d920
  @attributes={"name"=>"Tobi", "id"=>2, "age"=>28},
  @prefix_options={}>]

Each element in the array is an instance of the Api::Customer class complete with dynamically generated accessors for each attribute:

customer = customers.first
customer.id   # => 1
customer.name # => "Cody"
customer.age  # => 30
customer.new? # => false

Finding a single resource by ID

The code for finding a single resource by ID is similar to finding all resources:

customer = Api::Customer.find(1)

The GET request is sent to the member URI of the customer resource with ID 1:

GET /customers/1.xml HTTP/1.1

Based on Table 12.2, “RESTful Rails HTTP to controller action mapping” you can see that the request routes to show action of the controller. If the customer exists, the server returns a successful response with a single customer serialized as XML:

<?xml version="1.0" encoding="UTF-8"?>
<customer>
  <age type="integer">30</age>
  <id type="integer">1</id>
  <name>Cody</name>
</customer>

If the resource cannot be found, the server will return an HTTP 404 Not Found status code. From Table 12.1, “Active Resource Exceptions” you can see that a 404 status code causes an ActiveResource::ResourceNotFound exception to be raised. An exception is raised, instead of returning nil, because the resource is being looked up by ID and is assumed to exist. The resource not being found is therefore and exceptional state and an exception is raised.

Finding resources from custom paths

Active Resource supports finding resources from custom paths or actions that don't map precisely into the standard RESTful paradigm. A collection of resources can be found from a custom path using the standard call to find(:all) along with a custom :from option. Here we use the custom collection path adults to retrieve all customers over the age of 18.

customers = Api::Customer.find(:all, :from => :adults)

The :from option causes the request to be sent to the custom /customers/adults.xml path of the web service instead of the standard collection path /customers.xml. From Example 12.2, “A simple controller providing a RESTful web service.” you can see that the adults action is simply a custom query that returns a collection of all customers over the age of 18.

Active Resource also supports finding a single resource from a custom path or controller action. Here we select the oldest customer from the database by using the custom oldest path for the customer resource:

customer = Api::Customer.find(:one, :from => :oldest)

Passing :one as the first argument to the find method tells the Active Resource that the web service will only be returning a single resource. The :from option is always required when using find(:one). In this case Active Resource sends a GET request to the /customers/oldest.xml, which in our case maps to the custom oldest action of the web service, shown in Example 12.2, “A simple controller providing a RESTful web service.”, that selects the oldest customer in the system.

Creating Resources

Active Resource creates a resource by sending a POST request containing the resource's attribute data to the collection URI of the resource. Creating a new resource looks very similar to the creation of a new record using Active Record. Like with Active Record, you can either build a new object with new and then call save on it, or make a single call to create.

Creating a resource

A new resource can be created using either the create class method, or by building a new object and then calling save on it. Here we construct a new object and then save it:

customer = Api::Customer.new(:name => "Daniel", :age => 29)
customer.save

The call to save causes Active Resource to serialize the dynamically generated attributes of the object and send POST them to the collection URI of the resource:

POST /customers.xml HTTP/1.1

The request body contains the serialized customer data:

<?xml version="1.0" encoding="UTF-8"?>
<customer>
  <name>Daniel</name>
  <age type="integer">29</age>
</customer>

When the request is successful the server returns the HTTP status code 201 Created, which indicates that the resource was successfully created on the server. The server also returns an HTTP Location header containing the URI location of the newly created resource:

Location: http://localhost/customers/3

Active Resource parses the object's id from the Location header of the response. Knowing the id of the resource allows the client to find, update, or delete the resource in the future by sending requests to the resource's member URI.

When creating a resource the server has the choice of whether or not return a response body. It is perfectly fine to return an empty response body if there is no additional information generated at the time of save that the client should know about. This would be accomplished by calling render in the create controller action as follows:

render :nothing => true, :status => :created, :location => @customer

However, there are many instances where attributes are set or updated at the time of save. An example is when the resource has created_at and updated_at timestamp attributes that are updated at the time of the save. If the server doesn't return the updated representation of the resource in the response body then the Active Resource client won't know about the new or changed attributes until resource is fetched again using find or the object is reloaded with a call to reload.

In Example 12.2, “A simple controller providing a RESTful web service.” the controller does return the customer data in the response body when the creation of the resource is successful because the call to render is passed the :xml => @customer option. The response body looks as follows:

<?xml version="1.0" encoding="UTF-8"?>
<customer>
  <age type="integer">29</age>
  <id type="integer">3</id>
  <name>Daniel</name>
</customer>

You can see that the customer resource now has an id attribute that was not present in the data sent by the Active Resource client.

Validation and Errors

Active Resource supports both client-side validations and server-side errors. Client-side validations are validations defined on the Active Resource class, just like you're used to defining in your Active Record models. Server-side errors are errors returned in the web service response of the server. Both client-side validations and server-side errors allow you to display the validation failures your objects encounter in a manner that is compatible with Active Record objects.

Client-Side Validations

Client side validations allow you check if your object is valid before making a request to the server. This is useful when you know what values are default in advance, as it saves you an entire remote HTTP request to the remote server, which will keep your application more responsive to your users. As mentioned earlier in the chapter, you need use client side validations in conjunction with a schema definition in order to ensure that the attributes you're validating are in existence at the time the validations are run.

You can utilize all of the validation methods that you're used to using with Active Record. See the section called “Validations” for more information on the types of validations that are possible. Let's take a look at a simple example:

class Customer < ActiveResource::Base
  schema do
    string 'name'
    integer 'age'
  end
  
  validates_presence_of :name
  validates_numericality_of :age, :greater_than_or_equal_to => 0
end

Here we defined two validations with on the name attribute and the other on the age attribute. If we then instantiate an instance of the Customer class in the console and call some methods on it we get the following output:

customer = Customer.new
customer.valid? # => false
customer.errors.full_messages
# => ["Name can't be blank", "Age is not a number"]

customer.name = "Max Fightmaster"
customer.age = 47
customer.valid? # => true

Notice how this is identical to how you'd work with an Active Record object. The ability to perform these types of client side validations and to utilize Active Resource objects in form fields is very powerful. It allows you to work with both local and remote objects in a similar fashion and to utilize the same helpers and logic for both types of objects.

Server-Side Errors

Active Resource has support for parsing any server side error messages that are returned by the web service into an errors collection on the model. The web service is expected to return an HTTP 422 Unprocessable Entity status code along with a collection of error elements when validation fails. Active Resource then parses the errors out of the response and return false from the method making the request. The errors are then accessible through an errors collection.

The Customer model on the server, shown in Example 12.3, “Active Record Customer class on the server”, has validations to ensure that the customer has a name and that the customer's age is greater than 0. Let's attempt to create an invalid resource:

customer = Api::Customer.new(:name => "", :age => 0)
customer.save

When the controller on the server tries to save the model the validation will fail. As you can see from the create action in Example 12.2, “A simple controller providing a RESTful web service.”, the controller renders the errors collection of the model with the :unprocessable_entity HTTP status code when customer cannot be saved. The HTTP 422 Unprocessable Entity status code is used by Rails to indicate that validation has failed. It is expected that the body of the response will contain the errors that were generated by the request:

<?xml version="1.0" encoding="UTF-8"?>
<errors>
  <error>Name can't be blank</error>
  <error>Age must be greater than 0</error>
</errors>

Rails then parses each error message and tries to associate the message with the attributes of the Active Resource object at the time of the request. Any errors that cannot be associated with an attribute will appear on the base object. The errors can then be iterated through and interacted in similar way to Active Record errors:

customer.valid?                 # => false
customer.errors.empty?          # => false
customer.errors.size            # => 2
customer.errors.invalid?(:name) # => true
customer.errors.on(:name)       # => "can't be blank"
customer.errors.invalid?(:age)  # => true
customer.errors.on(:age)        # => "must be greater than 0"
customer.errors.full_messages  
# => ["Name can't be blank", "Age must be greater than 0"]

Updating Resources

Active Resource updates a resource by sending a PUT request to the member URI of the resource.

Unlike with an Active Record object, it is possible to mass assign the ID of an Active Resource object. This allows a client to update a resource that it knows the ID of without having to first retrieve the resource from the remote server.

customer = Api::Customer.new(:id => 1, :age => 98)
customer.save

However, you'll probably find a resource first and then update it:

customer = Api::Customer.find(1)
customer.age = 98
customer.save

In the call to save, Active Resource sends a PUT request with the updated information for the resource to the resource's URI:

<?xml version="1.0" encoding="UTF-8"?>
<customer>
  <name>Cody</name>
  <id type="integer">1</id>
  <age type="integer">98</age>
</customer>

If the update is successful then the server will return the HTTP status code 200 OK with an empty response body.

If the update to the resource fails validation then the server will return the errors causing the validation failure along with the HTTP status code 422 Unprocessable Entity.

Deleting Resource

Active Resource deletes a resource by sending a DELETE request to the member URI of the resource. Deleting a resource can be done by calling the delete class method of the Active Resource model with the ID of the resource to be deleted:

Api::Customer.delete(1)

You can also call the destroy method on an instance of an Active Resource model:

customer = Api::Customer.find(1)
customer.destroy

Either way you choose to delete the resource, the same HTTP request will be made, which is a DELETE request to the resource's member URI:

DELETE /customers/1.xml HTTP/1.1

The destroy action shown in Example 12.2, “A simple controller providing a RESTful web service.” is the standard way of deleting a record. The action returns a 200 Ok with an empty response body.

Making Custom Requests

The case may be that the standard Active Resource lifecycle methods are not sufficient for calling a custom operation on a resource. Fortunately, Active Resource models are able to make custom GET, POST, PUT, and DELETE requests to both member and collection URIs of a resource. ActiveResource::Base is extended with both class and instance methods for each HTTP verb: get, post, put, and delete.

The custom URI is formed by appending the name of the desired custom action to the collection or member URI. For a custom action on the resource's custom collection action oldest:

customer = Api::Customer.get(:oldest)

As we already saw in the section called “Finding resources from custom paths”, Active Resource sends an HTTP GET request to the /customers/oldest.xml URI. The same response is returned by the web service as before, however, the response is not automatically mapped to an instance of the Api::Customer class. Normally the response from an Active Resource lifecycle method is automatically mapped to an instance, or instances, of an Active Resource model, but this is not the case for custom requests. A response to a custom request is converted to a hash using Hash.from_xml. In this case the response is a single hash:

{"name"=>"Cody", "id"=>1, "age"=>30}

HTTP Authentication

Active Resource supports HTTP basic access authentication. HTTP basic authentication allows the web service to authenticate the Active Resource client based on a set of credentials with each request to the web service. See ??? for more detailed information on adding HTTP basic authentication to your application. Any use of HTTP basic authentication needs to be performed over a secure SSL connection in order to protect the credentials and data transmitted.

Setting up the server to perform HTTP basic authentication can be as simple as adding a before filter to the controller:

class CustomersController < ApplicationController
  before_filter :authenticate

  private
  def authenticate
    authenticate_or_request_with_http_basic do |user_name, password| 
      user_name == "cody" && password == "secret"
    end
  end
end

The controller will attempt to authenticate the user with the user name cody and password secret or return an HTTP 401 Unauthorized status code.

To use Active Resource with a server that uses HTTP basic authentication you need to configure the credentials on the Active Resource model. As with most other configuration settings, the credentials are inherited to subclasses of ActiveResource::Base. This allows you to set the credentials once on ActiveResource::Base or on a particular subclass.

The credentials used by HTTP basic authentication are a user name and a password. There are two ways to provide the user name and password to be used. The first is to place the user name and password into the site using the http://user:password@host:port format:

module Api
  class Customer < ActiveResource::Base
    self.site = 'http://cody:secret@localhost:3000'
  end
end

The second way is to set the user and password class accessors. This method is useful for code clarity, but also if the user name you're using is an email address. The @ symbol in an email address will result in an invalid URI.

module Api
  class Customer < ActiveResource::Base
    self.site = 'http://localhost:3000'
    self.user = 'cody@example.com'
    self.password = 'secret'
  end
end

Once configured, Active Resource will provide the credentials to the server on each request. As was shown above, the server returns an HTTP 401 Unauthorized status code when the client cannot be authenticated. The 401 Unauthorized status code causes an ActiveResource::UnauthorizedAccess exception to be raised in the Active Resource client.

Site last updated on: December 11, 2012 at 10:21:28 PM PST
Cover for Rails 3 in a Nutshell

View 1 comment

  1. hydra – Posted Sept. 8, 2010

    Correction on line 2: Being able TO offer multiple...

Add a comment

View 2 comments

  1. ruprict – Posted Oct. 23, 2009

    HTTP or HTML?

  2. codyfauser – Posted Nov. 20, 2009

    Thanks Glenn, I just fixed up this typo.

Add a comment

View 1 comment

  1. William – Posted Sept. 16, 2010

    Might be a good idea to put the HTTP verbs in the same order as CRUD (POST, GET, PUT DELETE) so that when you say "map to ..." it makes it a little more literal.

Add a comment

View 2 comments

  1. davesailer – Posted Oct. 29, 2009

    "For more thorough coverage of REST in Rails see ." <===

    See what?

  2. codyfauser – Posted Nov. 20, 2009

    Thanks for the comment. The endpoint doesn't exist yet, therefore no link :)

Add a comment

View 4 comments

  1. davesailer – Posted Oct. 29, 2009

    "Api::Custer" ===> "Api::Customer"

    Nicht wahr?

  2. codyfauser – Posted Nov. 21, 2009

    Thanks Dave, I just fixed this one.

  3. danrussia – Posted Nov. 24, 2009

    I do not have an api folder. You might want to explain a bit more about it. If rails naturally looks / includes this folder, etc. Also, I wanted to say, it may be just the style of the book, but it seems hard to really tie all these chapters in together and understand how to take each section and apply it to a single project.

  4. danrussia – Posted Nov. 25, 2009

    Never mind. I see your explanation further down the page.

Add a comment

View 1 comment

  1. pagecr – Posted March 19, 2011

    Should be 'Now with logging configured...'

Add a comment

View 1 comment

  1. pagecr – Posted March 19, 2011

    Should be '... attributes your...'

Add a comment

View 1 comment

  1. danrussia – Posted Nov. 25, 2009

    Excellent chapter. These lines of codes are very helpful and really help understand Rails and REST. I'll definitely be buying your book as a reference.

Add a comment

View 1 comment

  1. danrussia – Posted Nov. 25, 2009

    word missing after generated.

Add a comment

View 2 comments

  1. Special_Dragonfly – Posted Nov. 6, 2009

    "show in Example 4.3, “Active Record Customer class on the server”, " should be "shown ..."

  2. codyfauser – Posted Nov. 20, 2009

    Thanks Dominic, I just fixed this typo.

Add a comment

View 2 comments

  1. chrisjschmitt – Posted Oct. 30, 2009

    This example assumes you have the id of the record, which you never have. I'd like to see an example that finds the record another way first, e.g. find_by_name, and then you proceed to update the record.

  2. Special_Dragonfly – Posted Nov. 6, 2009

    customer.update_attribute('age', 98) would be better wouldn't it?

Add a comment

View 2 comments

  1. Special_Dragonfly – Posted Nov. 6, 2009

    Appears to be a word missing... "If the update to the resource fails validation then the resource will" ... "the errors generated and the HTTP status code 422 Unprocessable Entity."

  2. codyfauser – Posted Nov. 20, 2009

    Rewritten to make sense, thanks!

Add a comment

View 1 comment

  1. danrussia – Posted Nov. 25, 2009

    There is an insignificant grammar problem here: "Finding resources from A CUSTOMS PATHS"

Add a comment

View 1 comment

  1. danrussia – Posted Nov. 25, 2009

    Excellent chapter. It's probably outside the scope of this book but it might be worth providing a link on how to store passwords with a salt.

Add a comment