Recipe 6.5. Consuming Complex Nested REST Resources


Problem

Contributed by: Diego Scataglini

You want to consume a REST resource that has a nested structure. For example, the resources that you want to consume have the structure http://localhost:3000/users/1/blogs/1.

Solution

For this solution you'll need two Rails applications, a server and a client. For the server application, use the application you created in Section 6.1." Start that application now, but be sure to run that application on a port other than the default port 3000. For example, type the following in a terminal window in the root of the Rails application:

$ ruby script/server lighttpd -d -p 3008             

With the server application running on port 3008, create an empty Rails application to serve as a client. In the same terminal window, type:

$ rails ../rest_client $ cd ../rest_client             

You'll be working with the client application from here on in. You will use Active Resource, a bleeding-edge feature that was still in development at the time of the Rails 1.2 release, which lets you work with RESTful resources in much the same way that Active Record lets you work with databases. While Active Resource may change significantly in the coming months, an early look will help you get a jump on this exciting new API. To install it, execute the following command from the root of your Rails application:

$ svn co http://dev.rubyonrails.org/svn/rails/trunk/activeresource \ > lib/activeresource             

You'll also need to explicitly require the library from your environment:

config/environment.rb:

require "activeresource/lib/active_resource"

Next, create two Active Resource models corresponding to the two model objects from the server project, User and Blog:

app/models/user.rb:

class User < ActiveResource::Base   self.site = "http://localhost:3008" end

app/models/blog.rb:

class Blog < ActiveResource::Base   self.site = "http://localhost:3008/users/:user_id/" end

That's all there is to it! You can now leverage the full power of Rails' REST support to find, create, update, or delete remote resources using an API very similar to the one exposed by Active Record. Test your models in the Rails console to see for yourself:

$ ruby script/console  Loading development environment. >> User.find(:all) => [#<User:0x2a9144c @attributes={"name"=>"Diego" ...  #<User:0x2a903a8 @attributes={"name"=>"Chris",... #<User:0x2a9013c @attributes={"name"=>"Rob", ..] >> Blog.find(:all, :user_id => 1)  => [#<Blog:0x29cf3b0 @attributes={"title"=>"My work blog".. , #<Blog:0x29cad88 @attributes={"title"=>"My fun rblog",.. ] >> Blog.find(2, :user_id => 1) => #<Blog:0x20ba304 @attributes={"title"=>"My fun rblog", ... >> @user = User.new(:name => "john") => #<User:0x21ca960 @attributes={"name"=>"john"}, @prefix_options={}> >> @user.save => true >> @user.id => "4" >> @user.name = "Bobby" => "Bobby" >> @user.save           => true >> @user      => #<User:0x21ca960 @attributes={"name"=>"Bobby", "id"=>"4"}, ...

Discussion

The beauty of consuming Active Resource objects is that they behave very much like Active Record models, flattening the learning curve dramatically. You can think of Active Resource as a web-friendly form of object remoting. Of course, being built on REST, this remoting is message-based rather than RPC-based.

In this solution, you created models that were named identically to the resources in the server application. If you wish to change the name of the Active Resource models, perhaps to avoid a name collision, Active Resource provides two hooks (element_name and collection_name) that allow you to customize your class names.

Create two new models, Customer and Diary, as follows:

app/models/customer.rb:

class Customer < ActiveResource::Base   self.site = "http://localhost:3008"   self.element_name ="user" end

app/models/diary.rb:

class Diary < ActiveResource::Base   self.site = "http://localhost:3008/users/:user_id/"   self.element_name ="blog" end

Now test them in the Rails console:

$ ruby script/console Loading development environment. >> Customer.find(1) => [#<Customer:0x2a4090c @attributes={"name"=>"Diego".. >> Customer.find(:first) => #<Customer:0x29bd73c @attributes={"name"=>"Diego", "id"= ... >> Customer.find(2) => #<Customer:0x20b8400 @attributes={"name"=>"Chris",  >> Diary.find(1, :user_id => 1) => #<Diary:0x2a98bfc @attributes={"title"=>"My work blog",... >> Diary.find(3, :user_id => 2) => #<Diary:0x2b6dde8 @attributes={"title"=>"My xml blog", ...

You can also inspect the paths and objects for insights into Rails' RESTful paths:

>> Diary.element_path(:all, :user_id => 1) => "/users/1/blogs/all.xml" >> Diary.element_path(:first, :user_id => 1) => "/users/1/blogs/first.xml" >> Diary.element_name              => "blog" >> Diary.collection_name => "blogs" >> Diary.collection_path(:user_id => 2) => "/users/2/blogs.xml" >> Diary.element_path(:first) => "/users//blogs/first.xml" >> Diary.element_path(:first, 4) => "/users/0/blogs/first.xml" >> Diary.element_path(:mycustomer_para, :user_id => 3, :cu => "so") => "/users/3/blogs/mycustomer_para.xml" >> puts Diary.methods.grep(/eleme/) set_element_name element_name element_name= element_path => nil >> puts Diary.methods.grep(/colle/) collection_name= set_collection_name collection_path collection_name => nil

As you can see, interacting with REST models via Active Resource is quite simple. The flexibility and power of the REST API make the common task of interacting with other Rails applications a breeze.

See Also

  • Section 6.1"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net