Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Active Resource (ARes) connects business objects and REST web services. It is a
library
intended to provide transparent proxying capabilities between a client and a
RESTful
service (for which Rails provides the {Simply RESTful
routing}[http://dev.rubyonrails.org/browser/trunk/actionpack/lib/action_controller
/resources.rb] implementation).
Person is now REST enable and can invoke REST services very similarly to how
ActiveRecord invokes
lifecycle methods that operate against a persistent store.
# Resource creation can also use the convenience <tt>create</tt> method which
# will request a resource save after instantiation.
ryan = Person.create(:first => 'Ryan', :last => 'Daigle')
ryan.exists? #=> true
# And destruction
# DELETE http://api.people.com:3000/people/1.xml
#
ryan = Person.find(1)
ryan.destroy #=> true # Or Person.delete(ryan.id)
=== Protocol
ARes is built on a standard XML format for requesting and submitting resources.
It mirrors the
RESTful routing built into ActionController, though it's useful to discuss what
ARes expects
outside the context of ActionController as it is not dependent on a Rails-based
RESTful implementation.
==== Find
GET Http requests expect the XML form of whatever resource/resources is/are being
requested. So,
for a request for a single element - the XML of that item is expected in response:
# Expects a response of
#
#
<person><id>1</id><attribute1>value1</attribute1><attribute2>..</attribute2></pers
on>
#
# for GET http://api.people.com:3000/people/1.xml
#
ryan = Person.find(1)
The XML document that is received is used to build a new object of type Person,
with each
XML element becoming an attribute on the object.
Any complex element (one that contains other elements) becomes its own object:
# Expects a response of
#
# <people>
# <person><id>1</id><first>Ryan</first></person>
# <person><id>2</id><first>Jim</first></person>
# </people>
#
# for GET http://api.people.com:3000/people.xml
#
people = Person.find(:all)
people.first #=> <Person::xxx 'first' => 'Ryan' ...>
people.last #=> <Person::xxx 'first' => 'Jim' ...>
==== Create
Creating a new resource submits the xml form of the resource as the body of the
request and expects
a 'Location' header in the response with the RESTful URL location of the newly
created resource. The
id of the newly created resource is parsed out of the Location response header and
automatically set
as the id of the ARes object.
# <person><first>Ryan</first></person>
#
# is submitted as the body on
#
# POST http://api.people.com:3000/people.xml
#
# when save is called on a new Person object. An empty response is
# is expected with a 'Location' header value:
#
# Response (201): Location: http://api.people.com:3000/people/2
#
ryan = Person.new(:first => 'Ryan')
ryan.new? #=> true
ryan.save #=> true
ryan.new? #=> false
ryan.id #=> 2
==== Update
'save' is also used to update an existing resource - and follows the same protocol
as creating a resource
with the exception that no response headers are needed - just an empty response
when the update on the
server side was successful.
# <person><first>Ryan</first></person>
#
# is submitted as the body on
#
# PUT http://api.people.com:3000/people/1.xml
#
# when save is called on an existing Person object. An empty response is
# is expected with code (204)
#
ryan = Person.find(1)
ryan.first #=> 'Ryan'
ryan.first = 'Rizzle'
ryan.save #=> true
==== Delete
Destruction of a resource can be invoked as a class and instance method of the
resource.
# A request is made to
#
# DELETE http://api.people.com:3000/people/1.xml
#
# for both of these forms. An empty response with
# is expected with response code (200)
#
ryan = Person.find(1)
ryan.destroy #=> true
ryan.exists? #=> false
Person.delete(2) #=> true
Person.exists?(2) #=> false
Error handling and validation is handled in much the same manner as you're used to
seeing in
ActiveRecord. Both the response code in the Http response and the body of the
response are used to
indicate that an error occurred.
When a get is requested for a resource that does not exist, the Http '404'
(resource not found)
response code will be returned from the server which will raise an
ActiveResource::ResourceNotFound
exception.
# GET http://api.people.com:3000/people/1.xml
# #=> Response (404)
#
ryan = Person.find(1) #=> Raises ActiveResource::ResourceNotFound
Creating and updating resources can lead to validation errors - i.e. 'First name
cannot be empty' etc...
These types of errors are denoted in the response by a response code of 422 and
the xml representation
of the validation errors. The save operation will then fail (with a 'false'
return value) and the
validation errors can be accessed on the resource in question.
# When
#
# PUT http://api.people.com:3000/people/1.xml
#
# is requested with invalid values, the expected response is:
#
# Response (422):
# <errors><error>First cannot be empty</error></errors>
#
ryan = Person.find(1)
ryan.first #=> ''
ryan.save #=> false
ryan.errors.invalid?(:first) #=> true
ryan.errors.full_messages #=> ['First cannot be empty']
If the underlying Http request for an ARes operation results in an error response
code, an
exception will be raised. The following Http response codes will result in these
exceptions:
=== Authentication
Many REST apis will require username/password authentication, usually in the form
of
Http authentication. This can easily be specified by putting the username and
password
in the Url of the ARes site:
For obvious reasons it is best if such services are available over https.