2 # A module to support custom REST methods and sub-resources, allowing you to break out
3 # of the "default" REST methods with your own custom resource requests. For example,
4 # say you use Rails to expose a REST service and configure your routes with:
6 # map.resources :people, :new => { :register => :post },
7 # :member => { :promote => :put, :deactivate => :delete }
8 # :collection => { :active => :get }
10 # This route set creates routes for the following HTTP requests:
12 # POST /people/new/register.xml # PeopleController.register
13 # PUT /people/1/promote.xml # PeopleController.promote with :id => 1
14 # DELETE /people/1/deactivate.xml # PeopleController.deactivate with :id => 1
15 # GET /people/active.xml # PeopleController.active
17 # Using this module, Active Resource can use these custom REST methods just like the
20 # class Person < ActiveResource::Base
21 # self.site = "http://37s.sunrise.i:3000"
24 # Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.xml
25 # # => { :id => 1, :name => 'Ryan' }
27 # Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml
28 # Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml
30 # Person.get(:active) # GET /people/active.xml
31 # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
34 def self.included(base
)
36 extend ActiveResource
::CustomMethods::ClassMethods
37 include ActiveResource
::CustomMethods::InstanceMethods
40 alias :orig_delete :delete
42 # Invokes a GET to a given custom REST method. For example:
44 # Person.get(:active) # GET /people/active.xml
45 # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
47 # Person.get(:active, :awesome => true) # GET /people/active.xml?awesome=true
48 # # => [{:id => 1, :name => 'Ryan'}]
50 # Note: the objects returned from this method are not automatically converted
51 # into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
52 # ActiveResource::Base instances, use the <tt>find</tt> class method with the
53 # <tt>:from</tt> option. For example:
55 # Person.find(:all, :from => :active)
56 def get(custom_method_name
, options
= {})
57 connection
.get(custom_method_collection_url(custom_method_name
, options
), headers
)
60 def post(custom_method_name
, options
= {}, body
= '')
61 connection
.post(custom_method_collection_url(custom_method_name
, options
), body
, headers
)
64 def put(custom_method_name
, options
= {}, body
= '')
65 connection
.put(custom_method_collection_url(custom_method_name
, options
), body
, headers
)
68 def delete(custom_method_name
, options
= {})
69 # Need to jump through some hoops to retain the original class 'delete' method
70 if custom_method_name
.is_a
?(Symbol
)
71 connection
.delete(custom_method_collection_url(custom_method_name
, options
), headers
)
73 orig_delete(custom_method_name
, options
)
81 def custom_method_collection_url(method_name
, options
= {})
82 prefix_options
, query_options
= split_options(options
)
83 "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
87 module InstanceMethods
88 def get(method_name
, options
= {})
89 connection
.get(custom_method_element_url(method_name
, options
), self.class.headers
)
92 def post(method_name
, options
= {}, body
= nil)
93 request_body
= body
.blank
? ? encode
: body
95 connection
.post(custom_method_new_element_url(method_name
, options
), request_body
, self.class.headers
)
97 connection
.post(custom_method_element_url(method_name
, options
), request_body
, self.class.headers
)
101 def put(method_name
, options
= {}, body
= '')
102 connection
.put(custom_method_element_url(method_name
, options
), body
, self.class.headers
)
105 def delete(method_name
, options
= {})
106 connection
.delete(custom_method_element_url(method_name
, options
), self.class.headers
)
111 def custom_method_element_url(method_name
, options
= {})
112 "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
115 def custom_method_new_element_url(method_name
, options
= {})
116 "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"