What's New in Edge Rails: The Ins and Outs of ActiveResource

Posted by ryan
at 9:58 AM on Friday, June 30, 2006

See here from some syntactical changes that occurred after this post was published

Following the tail of David Heinemeier Hansson’s much talked about RailsConf keynote speech comes the addition of the ActiveResource library to edge Rails. What’s all the hype about? Well, I’ll let you read David’s post for the lowdown, then I’ll try to do some justice to the concept….. Done reading? If you were too lazy let me give you the one line summary: CRUD is great because it’s consistent, simple, expressive and foundational. Every application should be built solely on CRUD operations – and Rails is going to help you construct such applications.. Great, let’s carry on.

If you haven’t noticed, it appears that the next version of Rails will whole heartedly embrace the RESTful model of application development. Not only was it the topic of David’s keynote speech, but we’ve seen a lot of updates into edge rails that support the various aspects of RESTful calls (for instance we’ve seen REST support in the various link helpers and the ability to populate a Hash directly from XML). REST is a great companion to the RAILs style of simple, expressive application development.

for those that need a quick primer on REST, see the bottom part of this post

ActiveResource provides a large piece of the REST puzzle by basically implementing the client side of a RESTful system – the parts of a decoupled system that consume RESTful services. At its essence, ActiveResource provides a way to utilize model objects as REST-based client proxies to remote services. Ok, sounds impressive, but what does this mean?

Let’s look at an example of a typical find call on a Person model object:


Person.find(1).name #=> "Ryan"

Doesn’t appear to be anything out of the ordinary going on here – except that under the covers it’s not a database SELECT that’s occuring. Instead, if the Person model is an Active Resource, this find call is actually sending an HTTP GET request across the wire to a RESTful service:

GET http://api.myremote.com/people/1
<= Ryan

See how an XML response comes back from the GET request? That XML is automatically transformed for us behind the scenes into a Person object. (Hmmm, now that Hash.create_from_xml addition we saw come across in edge rails a few days ago makes sense.. ). There’s a lot of power in that – transparently making a remote call (based on REST principles) simply by using the same methods you’re used to using with Active Record.

What about creating or deleting? Same thing:


Person.new(:name => "Ryan").save

HTTP request:

POST http://api.myremote.com/people
=> Ryan
<= Location: http://api.myremote.com/people/1

With the create, a POST HTTP request is made and an XML document is submitted as the request in the format we saw come across before – the XML structure of the Person object with its values. The return value is the URL that represents where that resource now lives (from which the id of the new person can be parsed). Note: this create functionality of ActiveResource is not yet committed to edge rails, it’s been deduced from various other resources and presentations

Deletes just return a 200 status code when successful (note there’s not yet a Person.destroy(id) class method like in ActiveRecord – you have to get the item first).


Person.find(1).destroy

HTTP request:

DELETE http://api.myremote.com/people/1
<= Status: 200

To be clear, ActiveResource is not meant to replace ActiveRecord in your controllers since that would involve an unecessary trip across the wire. However, when building APIs and de-coupled services, Active Resource makes perfect, painlessly simple sense in the consumption of such services. To implement the Active Resource client proxy, all you have to do is the extend ActiveResource:Struct and point to the remote URL:

1
2
3
4
Person = ActiveResource::Struct.new do |person|
  person.uri = "http://api.myremote.com/people"
  person.credentials :name => "me", :password => "password"
end

From this simple setup ActiveResource will now perform the following on each individual operation:

Model request Http REST operation request body Request URI Response
find(id)GETN/A/people/id.xml
1
2
3
<person>
  <name>name</name>
</person>
save (update)PUT
1
2
3
<person>
  <name>name</name>
</person>
/people/id.xmlStatus: 200 OK
save (create)POST
1
2
3
<person>
  <name>name</name>
</person>
/peopleLocation: http://x/people/id.xml
destroyDELETEN/A/people/id.xmlStatus: 200 OK
listGETN/A/people
1
2
3
4
5
<people>
  <person>
    <name>name</name>
  </person>
</people>


So we know how ActiveResource abstracts the calling of REST services from the client side, but how do we implement those RESTful services? I suspect you’re going to see the server side components flowing into edge rails for the 1.2 release, but there are already a few plugins that provide that functionality.


What is REST?

REST is the request and manipulation of web-accessible resources through the use of standard HTTP request methods (GET, POST, PUT, DELETE). REST is based on the notion that the individual HTTP methods represent the type of action to take (the verb) and the URI is the location of the resource to act upon (the noun). Using this paradigm, the same URL can be used to perform different actions. For instance, if I wanted to get the person with id 1, I might make this call:

GET: /person/1

And if I wanted to delete that person, I would call the same URL with a different request type:

DELETE: /person/1

There you go, the noun is the URL representing a single resource, and the action to perform is the type of request. No longer do we need different URLs for every system action. One url, many actions. Powerful and concise.

Saying it in a slightly different way, REST is the invocation of an atomic, CRUD-based operation on a web accessible resources by the appropriate HTTP method instead of the URL of the request.

HTTP has several types, or methods, of making a request. We’re all familiar with GET, used to fetch a resource or URL, and POST which is most often used to submit form data. However, there are also the PUT and DELETE methods of requests (among others) that largely go unused. The whole idea behind REST is to properly synchronize the intent of the request with the method of the request.

It’s not uncommon to see the following URL to delete an item: http://url/people/delete?id=1. Somebody requests that URL and the resource is deleted from the server. But, this represents a break between the effect of the request and the type of request. When a browser or application requests that URL (via a GET), a deletion occurs within the system. How a deletion should be requested is with the HTTP DELETE method. That way the method of the request (DELETE) matches the effect of the request (something is deleted). There are similar mappings between every REST action and the basic CRUD operations:

CRUD operation REST method
Create POST
Read GET
Update PUT
Delete DELETE

You will often see REST and CRUD being compared as they mirror one another – REST is how CRUD operations are invoked in an HTTP-networked environment.

References:

tags: rails, rubyonrails, active resource

Comments

Leave a response

  1. Ryan DaigleJune 30, 2006 @ 07:21 AM
    I've fixed Fielding's dissertation link to point to google's cache, don't believe it would have been taken down permanently though?
  2. DGMJune 30, 2006 @ 07:21 AM
    But do browsers support PUT and DELETE methods? Or is REST only usefull for the reciept of ActiveResource calls?
  3. Ryan DaigleJune 30, 2006 @ 07:21 AM
    Sorry Jeremy, I had Textile as the default parser, then somebody put in html and it messed them up, so I switched it to be no comment parser... Then you come along with your Textile format, what's a man to do?
    That's it - Textile from here on out!!

    tamc: You're right, the ActiveResource library, in its current state, doesn't live up to the level of support present in David's presentation. However, I have no doubt it soon will. By the way, good digging around ActiveResource yourself... (r.e. the link you posted)
  4. billJune 30, 2006 @ 07:21 AM
    Basically browsers don't support PUT and DELETE. So as has been stated, these things are done with tricks in rails. Rails gives us a world that _feels_ like browsers support PUT and DELETE. That lets us pretend its a perfect world. We can think about resources and what action we want to apply to them instead of using a url soup to get what we want out of our requests. Its about programming and design. REST was never about 'pretty' URLs. And now REST isn't even about browsers. Its about a protocol that makes sense. Rails is embracing http and the ideas behind it. Rails is not embracing the crippled browser support of the protocol. One of the things I really love about Rails is that the ideas driving it are not crazy new approaches to application development, but things we have known already. Rails is not some new magical approach to development. Its applying all the things we've known all along but never put together under one roof (or at all). It really is a great way to program. Its that simple. Im really excited! hope Im not out of place commenting here, bill
  5. Ryan DaigleJune 30, 2006 @ 07:21 AM
    Matias, while PUT and POST are very similar, I do believe POST usually corresponds to a new entity while PUT is an existing one.
    This listing seems to indicate that the two can be used almost interchangeably, while wikipedia indicates that POST is to be used for anything with a side effect (which can be editing an existing or adding a new...).
    So, at the end of the day I don't think it's incorrect to use a PUT for a new resource, or a POST to update an existing one, but I do believe the standard is that PUT=edit and POST=create.
  6. Jeremy VoorhisJune 30, 2006 @ 07:21 AM
    Erk. I suppose it is not quite a safe assumption that Textile will be available in a Typo comment form. Use your inner parser to read the above comment.
  7. Jeremy VoorhisJune 30, 2006 @ 07:21 AM
    bq. do browsers support PUT and DELETE methods? That is correct. The hidden field method, however, allows us to emulate the two missing verbs. It seems that people who seem to care fall into either one of two camps. First, there are those who see the method trick as a dirty hack, remeniscent of the garbage hidden fields ASP.NET inserts into the page to track state. The rest of us look to this technique as an opportunity to assert to the browser implementers and spec writers that we are indeed interested in taking advantage of HTTP and RESTful techniques. Every hidden method field is a vote!
  8. tamcJune 30, 2006 @ 07:21 AM
    The version of active_resource in current edge rails doesn't quite work like this. See http://tom.counsell.org/view/EarlyExperiencesWithActiveResource for my early attempts at getting this to work.
  9. DHHJune 30, 2006 @ 07:21 AM
    We simulate PUT and DELETE in browsers by piggybacking something like _method=PUT over POST. So the browser will just send a POST, but Rails will pick it up as a PUT and do the right thing.
  10. Jon GretarJune 30, 2006 @ 07:21 AM
    Just a Q here. New to REST really. You show examples of finding a user with id 1. But do I use the same find command to find a list of records. Like Person.find(:all, :condition => "country = Iceland").
  11. Matias PelenurJune 30, 2006 @ 07:21 AM
    I think you have PUT and POST the other way around... The "create" operation generally corresponds to a PUT, and "save" or "update" generally corresponds to POST.
  12. Dan KubbJune 30, 2006 @ 07:21 AM
    DGM: Many browsers do support PUT and DELETE, the trouble is that HTML forms don't support them. You can do AJAX requests using PUT and DELETE just fine proving that its not the browser itself that is at fault, its the standards.
  13. supreme_spectatorJune 30, 2006 @ 07:21 AM
    does the Hash.create_from_xml method mean that Rails does XML sit-ups now? j/k :) Seriously, this is great. Thanks for the explanation and the "What's new in edge Rails" series.
  14. mennekeJune 30, 2006 @ 07:21 AM
    The link to http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm [RESTful] returns a 403
  15. AlanJune 30, 2006 @ 07:21 AM
    I wonder how and if it would handle delayed responses, for example if a "get" causes a job to be run so the results are NOT available in "real-time" but retrieved later. Will this replace ActionWebService?
  16. Ryan DaigleJune 30, 2006 @ 07:21 AM
    Jon: ActiveResource as it stands in edge rails doesn't really support finding with conditions. However, that support is no doubt coming with further enhancements. When it does arrive I suspect you will see the following code to find with conditions:
    
    Person.find(:all, :conditions => [:name => 'ryan'])
    GET /people?name=ryan
    
  17. billJune 30, 2006 @ 07:21 AM
    woh, the comments are backwards. I need a drink.
  18. DarkUnicornJune 30, 2006 @ 07:21 AM
    The RESTful Link (http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) is broken. I get a forbidden message.
  19. TimAugust 04, 2006 @ 03:40 PM
    Unless you have other information in the response, shouldn't the response code for a DELETE be 204 ????
  20. beppuJanuary 18, 2007 @ 01:57 PM
    FYI, for those of you who are curious: http://admin.ipo.hr/rails/test/ <-- This URL will test what HTTP methods your browser supports.
  21. isopaleocopriaMay 21, 2007 @ 12:49 PM

    I’m new to ActiveResource. That said, upon initial examination, the design seems to me like a case of the tail wagging the dog. There is an assumption that web service APIs can be crafted to conform to Rails conventions, in the same way that a database schema is. I suspect however that developers have less control over the web service APIs they consume than they do over database schema.

    Is there something like a Routes map, to allow a more flexible mapping from active resource model values to tokens in a URL?

  22. Ryan DaigleMay 22, 2007 @ 03:47 PM

    isopaleocopria: ActiveResource is definitely geared to provide a rich object abstraction on top of Rails RESTful services. If your corresponding web service is not one of them, then you may be on your own. However, ActiveResource still provides a Connection class that may be useful to you in building your own, more generic, abstraction layer?