Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8694: active_resource_docs.diff

File active_resource_docs.diff, 50.1 kB (added by jeremymcanally, 2 years ago)
  • activeresource/lib/active_resource/validations.rb

    old new  
    1414      @base, @errors = base, {} 
    1515    end 
    1616 
     17    # Add an error to the base Active Resource object rather than an attribute. 
     18    # 
     19    # ==== Examples 
     20    #   my_folder = Folder.find(1) 
     21    #   my_folder.errors.add_to_base("You can't edit an existing folder") 
     22    #   my_folder.errors.on_base 
     23    #   # => "You can't edit an existing folder" 
     24    # 
     25    #   my_folder.errors.add_to_base("This folder has been tagged as frozen") 
     26    #   my_folder.valid? 
     27    #   # => false 
     28    #   my_folder.errors.on_base 
     29    #   # => ["You can't edit an existing folder", "This folder has been tagged as frozen"] 
     30    # 
    1731    def add_to_base(msg) 
    1832      add(:base, msg) 
    1933    end 
    2034 
     35    # Adds an error to an Active Resource object's attribute (named for the +attribute+ parameter) 
     36    # with the error message in +msg+. 
     37    # 
     38    # ==== Examples 
     39    #   my_resource = Node.find(1) 
     40    #   my_resource.errors.add('name', 'can not be "base"') if my_resource.name == 'base' 
     41    #   my_resource.errors.on('name') 
     42    #   # => 'can not be "base"!' 
     43    # 
     44    #   my_resource.errors.add('desc', 'can not be blank') if my_resource.desc == '' 
     45    #   my_resource.valid? 
     46    #   # => false 
     47    #   my_resource.errors.on('desc') 
     48    #   # => 'can not be blank!' 
     49    # 
    2150    def add(attribute, msg) 
    2251      @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? 
    2352      @errors[attribute.to_s] << msg 
    2453    end 
    2554 
    2655    # Returns true if the specified +attribute+ has errors associated with it. 
     56    # 
     57    # ==== Examples 
     58    #   my_resource = Disk.find(1) 
     59    #   my_resource.errors.add('location', 'must be Main') unless my_resource.location == 'Main' 
     60    #   my_resource.errors.on('location') 
     61    #   # => 'must be Main!' 
     62    # 
     63    #   my_resource.errors.invalid?('location') 
     64    #   # => true 
     65    #   my_resource.errors.invalid?('name') 
     66    #   # => false 
    2767    def invalid?(attribute) 
    2868      !@errors[attribute.to_s].nil? 
    2969    end 
    3070 
    31     # * Returns nil, if no errors are associated with the specified +attribute+. 
    32     # * Returns the error message, if one error is associated with the specified +attribute+. 
    33     # * Returns an array of error messages, if more than one error is associated with the specified +attribute+. 
     71    # A method to return the errors associated with +attribute+, which returns nil, if no errors are  
     72    # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+, 
     73    # or an array of error messages if more than one error is associated with the specified +attribute+. 
     74    # 
     75    # ==== Examples 
     76    #   my_person = Person.new(params[:person]) 
     77    #   my_person.errors.on('login') 
     78    #   # => nil 
     79    # 
     80    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     81    #   my_person.errors.on('login') 
     82    #   # => 'can not be empty' 
     83    # 
     84    #   my_person.errors.add('login', 'can not be longer than 10 characters') if my_person.login.length > 10 
     85    #   my_person.errors.on('login') 
     86    #   # => ['can not be empty', 'can not be longer than 10 characters'] 
    3487    def on(attribute) 
    3588      errors = @errors[attribute.to_s] 
    3689      return nil if errors.nil? 
     
    3992     
    4093    alias :[] :on 
    4194 
    42     # Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute). 
     95    # A method to return errors assigned to +base+ object through add_to_base, which returns nil, if no errors are  
     96    # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+, 
     97    # or an array of error messages if more than one error is associated with the specified +attribute+. 
     98    # 
     99    # ==== Examples 
     100    #   my_account = Account.find(1) 
     101    #   my_account.errors.on_base 
     102    #   # => nil 
     103    # 
     104    #   my_account.errors.add_to_base("This account is frozen") 
     105    #   my_account.errors.on_base 
     106    #   # => "This account is frozen" 
     107    # 
     108    #   my_account.errors.add_to_base("This account has been closed") 
     109    #   my_account.errors.on_base 
     110    #   # => ["This account is frozen", "This account has been closed"] 
     111    # 
    43112    def on_base 
    44113      on(:base) 
    45114    end 
    46115 
    47116    # Yields each attribute and associated message per error added. 
     117    # 
     118    # ==== Examples 
     119    #   my_person = Person.new(params[:person]) 
     120    # 
     121    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     122    #   my_person.errors.add('password', 'can not be empty') if my_person.password == '' 
     123    #   messages = '' 
     124    #   my_person.errors.each {|attr, msg| messages += attr.humanize + " " + msg + "<br />"} 
     125    #   messages 
     126    #   # => "Login can not be empty<br />Password can not be empty<br />" 
     127    # 
    48128    def each 
    49129      @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } } 
    50130    end 
    51131 
    52132    # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned 
    53133    # through iteration as "First name can't be empty". 
     134    # 
     135    # ==== Examples 
     136    #   my_person = Person.new(params[:person]) 
     137    # 
     138    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     139    #   my_person.errors.add('password', 'can not be empty') if my_person.password == '' 
     140    #   messages = '' 
     141    #   my_person.errors.each_full {|msg| messages += msg + "<br/>"} 
     142    #   messages 
     143    #   # => "Login can not be empty<br />Password can not be empty<br />" 
     144    # 
    54145    def each_full 
    55146      full_messages.each { |msg| yield msg } 
    56147    end 
    57148 
    58149    # Returns all the full error messages in an array. 
     150    # 
     151    # ==== Examples 
     152    #   my_person = Person.new(params[:person]) 
     153    # 
     154    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     155    #   my_person.errors.add('password', 'can not be empty') if my_person.password == '' 
     156    #   messages = '' 
     157    #   my_person.errors.full_messages.each {|msg| messages += msg + "<br/>"} 
     158    #   messages 
     159    #   # => "Login can not be empty<br />Password can not be empty<br />" 
     160    # 
    59161    def full_messages 
    60162      full_messages = [] 
    61163 
     
    79181 
    80182    # Returns the total number of errors added. Two errors added to the same attribute will be counted as such 
    81183    # with this as well. 
     184    # 
     185    # ==== Examples 
     186    #   my_person = Person.new(params[:person]) 
     187    #   my_person.errors.size 
     188    #   # => 0 
     189    # 
     190    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     191    #   my_person.errors.add('password', 'can not be empty') if my_person.password == '' 
     192    #   my_person.error.size 
     193    #   # => 2 
     194    # 
    82195    def size 
    83196      @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size } 
    84197    end 
     
    86199    alias_method :count, :size 
    87200    alias_method :length, :size 
    88201     
     202    # Grabs errors from the XML response. 
    89203    def from_xml(xml) 
    90204      clear 
    91205      humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) } 
     
    102216    end 
    103217  end 
    104218   
    105   # Module to allow validation of ActiveResource objects, which are implemented by overriding +Base#validate+ or its variants.  
    106   # Each of these methods can inspect the state of the object, which usually means ensuring that a number of  
    107   # attributes have a certain value (such as not empty, within a given range, matching a certain regular expression). For example: 
     219  # Module to allow validation of ActiveResource objects, which creates an Errors instance for every resource. 
     220  # Methods are implemented by overriding +Base#validate+ or its variants   Each of these methods can inspect  
     221  # the state of the object, which usually means  ensuring that a number of attributes have a certain value  
     222  # (such as not empty, within a given range, matching a certain regular expression and so on). 
    108223  # 
     224  # ==== Example 
     225  # 
    109226  #   class Person < ActiveResource::Base 
    110227  #      self.site = "http://www.localhost.com:3000/" 
    111228  #      protected 
     
    133250  #   person.attributes = { "last_name" => "Halpert", "phone_number" => "555-5555" } 
    134251  #   person.save                         # => true (and person is now saved to the remote service) 
    135252  # 
    136   # An Errors object is automatically created for every resource. 
    137253  module Validations 
    138254    def self.included(base) # :nodoc: 
    139255      base.class_eval do 
     
    141257      end 
    142258    end 
    143259 
     260    # Validate a resource and save (POST) it to the remote web service. 
    144261    def save_with_validation 
    145262      save_without_validation 
    146263      true 
     
    149266      false 
    150267    end 
    151268 
     269    # Checks for errors on an object (i.e., is resource.errors empty?). 
     270    #  
     271    # ==== Examples 
     272    #   my_person = Person.create(params[:person]) 
     273    #   my_person.valid? 
     274    #   # => true 
     275    # 
     276    #   my_person.errors.add('login', 'can not be empty') if my_person.login == '' 
     277    #   my_person.valid? 
     278    #   # => false 
    152279    def valid? 
    153280      errors.empty? 
    154281    end 
  • activeresource/lib/active_resource/custom_methods.rb

    old new  
    1 # Support custom methods and sub-resources for REST. 
     1# A module to support custom REST methods and sub-resources, allowing you to break out 
     2# of the "default" REST methods with your own custom resource requests.  For example,  
     3# say you use Rails to expose a REST service and configure your routes with: 
    24# 
    3 # Say you on the server configure your routes with: 
     5#    map.resources :people, :new => { :register => :post }, 
     6#                           :element => { :promote => :put, :deactivate => :delete } 
     7#                           :collection => { :active => :get } 
    48# 
    5 #   map.resources :people, :new => { :register => :post }, 
    6 #                          :element => { :promote => :put, :deactivate => :delete } 
    7 #                          :collection => { :active => :get } 
     9#  This route set creates routes for the following http requests: 
    810# 
    9 #  Which creates routes for the following http requests: 
     11#    POST    /people/new/register.xml #=> PeopleController.register 
     12#    PUT     /people/1/promote.xml    #=> PeopleController.promote with :id => 1 
     13#    DELETE  /people/1/deactivate.xml #=> PeopleController.deactivate with :id => 1 
     14#    GET     /people/active.xml       #=> PeopleController.active 
    1015# 
    11 #  POST    /people/new/register.xml #=> PeopleController.register 
    12 #  PUT     /people/1/promote.xml    #=> PeopleController.promote with :id => 1 
    13 #  DELETE  /people/1/deactivate.xml #=> PeopleController.deactivate with :id => 1 
    14 #  GET     /people/active.xml       #=> PeopleController.active 
     16# Using this module, Active Resource can use these custom REST methods just like the 
     17# standard methods. 
    1518# 
    16 # This module provides the ability for Active Resource to call these 
    17 # custom REST methods and get the response back. 
    18 # 
    1919#   class Person < ActiveResource::Base 
    2020#     self.site = "http://37s.sunrise.i:3000" 
    2121#   end 
    2222# 
    23 #   Person.new(:name => 'Ryan).post(:register) #=> { :id => 1, :name => 'Ryan' } 
     23#   Person.new(:name => 'Ryan).post(:register)  # POST /people/new/register.xml 
     24#   # => { :id => 1, :name => 'Ryan' } 
    2425# 
    25 #   Person.find(1).put(:promote, :position => 'Manager') 
    26 #   Person.find(1).delete(:deactivate) 
     26#   Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml 
     27#   Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml 
    2728# 
    28 #   Person.get(:active) #=> [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}] 
     29#   Person.get(:active)  # GET /people/active.xml  
     30#   # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}] 
     31
    2932module ActiveResource 
    3033  module CustomMethods  
    3134    def self.included(within) 
  • activeresource/lib/active_resource/connection.rb

    old new  
    55require 'benchmark' 
    66 
    77module ActiveResource 
    8   class ConnectionError < StandardError 
     8  class ConnectionError < StandardError # :nodoc: 
    99    attr_reader :response 
    1010 
    1111    def initialize(response, message = nil) 
     
    1818    end 
    1919  end 
    2020 
    21   class ClientError < ConnectionError;  end  # 4xx Client Error 
    22   class ResourceNotFound < ClientError; end  # 404 Not Found 
    23   class ResourceConflict < ClientError; end  # 409 Conflict 
     21  # 4xx Client Error 
     22  class ClientError < ConnectionError; end # :nodoc: 
     23   
     24  # 404 Not Found 
     25  class ResourceNotFound < ClientError; end # :nodoc: 
     26   
     27  # 409 Conflict 
     28  class ResourceConflict < ClientError; end # :nodoc: 
    2429 
    25   class ServerError < ConnectionError;  end  # 5xx Server Error 
     30  # 5xx Server Error 
     31  class ServerError < ConnectionError; end # :nodoc: 
    2632 
    2733  # 405 Method Not Allowed 
    28   class MethodNotAllowed < ClientError 
     34  class MethodNotAllowed < ClientError # :nodoc: 
    2935    def allowed_methods 
    3036      @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym } 
    3137    end 
    3238  end 
    3339 
    34   # Class to handle connections to remote services. 
     40  # Class to handle connections to remote web services. 
     41  # This class is used by ActiveResource::Base to interface with REST 
     42  # services. 
    3543  class Connection 
    3644    attr_reader :site 
    3745 
     
    4654      end 
    4755    end 
    4856 
     57    # Method to create a new instance of the Connection class. 
     58    # The +site+ parameter is required and will set the +site+ 
     59    # attribute to the URI for the remote resource service. 
    4960    def initialize(site) 
    5061      raise ArgumentError, 'Missing site URI' unless site 
    5162      self.site = site 
    5263    end 
    5364 
    54     # Set URI for remote service. 
     65    # Attribute writer to set the URI for the remote service. 
    5566    def site=(site) 
    5667      @site = site.is_a?(URI) ? site : URI.parse(site) 
    5768    end 
    5869 
    59     # Execute a GET request. 
    60     # Used to get (find) resources
     70    # Execute a GET request on the remote service.  
     71    # Used to get resources (e.g., ActiveResource::Base#find)
    6172    def get(path, headers = {}) 
    6273      xml_from_response(request(:get, path, build_request_headers(headers))) 
    6374    end 
    6475 
    65     # Execute a DELETE request (see HTTP protocol documentation if unfamiliar). 
    66     # Used to delete resources. 
     76    # Execute a DELETE request (see the "HTTP protocol specification"[http://www.w3.org/Protocols/rfc2616/rfc2616.html]) 
     77    # on the remote service. 
     78    # Used to delete resources (e.g., ActiveResource::Base#delete and ActiveResource::Base#destroy). 
    6779    def delete(path, headers = {}) 
    6880      request(:delete, path, build_request_headers(headers)) 
    6981    end 
    7082 
    71     # Execute a PUT request (see HTTP protocol documentation if unfamiliar). 
    72     # Used to update resources. 
     83    # Execute a PUT request (see the "HTTP protocol specification"[http://www.w3.org/Protocols/rfc2616/rfc2616.html]) 
     84    # on the remote service. 
     85    # Used to update resources (e.g., ActiveResource::Base#update and ActiveResource::Base#save). 
    7386    def put(path, body = '', headers = {}) 
    7487      request(:put, path, body, build_request_headers(headers)) 
    7588    end 
    7689 
    77     # Execute a POST request
    78     # Used to create new resources
     90    # Execute a POST request on the remote service
     91    # Used to create new resources (e.g., ActiveResource::Base#create and ActiveResource::Base#save)
    7992    def post(path, body = '', headers = {}) 
    8093      request(:post, path, body, build_request_headers(headers)) 
    8194    end 
    8295 
     96    # A method to convert XML from the response body into a usable +Hash+ instance. 
    8397    def xml_from_response(response) 
    8498      if response = from_xml_data(Hash.from_xml(response.body)) 
    8599        response.first 
     
    88102      end 
    89103    end 
    90104 
    91  
    92105    private 
    93106      # Makes request to remote service. 
    94107      def request(method, path, *arguments) 
  • activeresource/lib/active_resource/base.rb

    old new  
    33require 'set' 
    44 
    55module ActiveResource 
     6  # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. 
     7  # 
     8  # For an outline of what Active Resource is capable of, see link:files/README.html. 
     9  # 
     10  # == Automated mapping 
     11  # 
     12  # Active Resource objects represent your RESTful resources as manipulatable Ruby objects.  To map resources 
     13  # to Ruby objects, Active Resource only needs a class name that corresponds to the resource name (e.g., the class 
     14  # Person maps to the resources people, very similarly to Active Record) and a +site+ value, which holds the 
     15  # URI of the resources. 
     16  #  
     17  #             class Person < ActiveResource::Base 
     18  #               self.site = "http://api.people.com:3000/" 
     19  #             end 
     20  #  
     21  # Now the Person class is mapped to RESTful resources located at <tt>http://api.people.com:3000/people/</tt>, and 
     22  # you can now use Active Resource's lifecycles methods to manipulate resources.   
     23  #  
     24  # == Lifecycle methods 
     25  # 
     26  # Active Resource exposes methods for creating, finding, updating, and deleting resources 
     27  # from REST web services. 
     28  #  
     29  #     ryan = Person.new(:first => 'Ryan', :last => 'Daigle') 
     30  #     ryan.save  #=> true 
     31  #     ryan.id  #=> 2 
     32  #     Person.exists?(ryan.id)  #=> true 
     33  #     ryan.exists?  #=> true 
     34  #  
     35  #     ryan = Person.find(1) 
     36  #     # => Resource holding our newly create Person object 
     37  #  
     38  #     ryan.first = 'Rizzle' 
     39  #     ryan.save  #=> true 
     40  #  
     41  #     ryan.destroy  #=> true 
     42  # 
     43  # As you can see, these are very similar to Active Record's lifecycle methods for database records. 
     44  # You can read more about each of these methods in their respective documentation. 
     45  #  
     46  # === Custom REST methods 
     47  # 
     48  # Since simple CRUD/lifecycle methods can't accomplish every task, Active Resource also supports 
     49  # defining your own custom REST methods. 
     50  #  
     51  #   Person.new(:name => 'Ryan).post(:register)                                                 
     52  #   # => { :id => 1, :name => 'Ryan', :position => 'Clerk' } 
     53  # 
     54  #   Person.find(1).put(:promote, :position => 'Manager')               
     55  #   # => { :id => 1, :name => 'Ryan', :position => 'Manager' } 
     56  #  
     57  # For more information on creating and using custom REST methods, see the  
     58  # ActiveResource::CustomMethods documentation. 
     59  # 
     60  # == Validations 
     61  # 
     62  # You can validate resources client side by overriding validation methods in the base class. 
     63  #  
     64  #             class Person < ActiveResource::Base 
     65  #                self.site = "http://api.people.com:3000/" 
     66  #                protected 
     67  #                  def validate 
     68  #                    errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/ 
     69  #                  end 
     70  #             end 
     71  #  
     72  # See the ActiveResource::Validations documentation for more information. 
     73  # 
     74  # == Authentication 
     75  #  
     76  # Many REST APIs will require authentication, usually in the form of basic 
     77  # HTTP authentication.  Authentication can be specified by putting the credentials 
     78  # in the +site+ variable of the Active Resource class you need to authenticate. 
     79  #  
     80  #   class Person < ActiveResource::Base 
     81  #     self.site = "http://ryan:password@api.people.com:3000/" 
     82  #   end 
     83  #  
     84  # For obvious security reasons, it is probably best if such services are available  
     85  # over HTTPS. 
     86  #  
     87  # == Errors & Validation 
     88  # 
     89  # Error handling and validation is handled in much the same manner as you're used to seeing in 
     90  # Active Record.  Both the response code in the Http response and the body of the response are used to 
     91  # indicate that an error occurred. 
     92  #  
     93  # === Resource errors 
     94  #  
     95  # When a get is requested for a resource that does not exist, the HTTP +404+ (Resource Not Found) 
     96  # response code will be returned from the server which will raise an ActiveResource::ResourceNotFound 
     97  # exception. 
     98  #  
     99  #   # GET http://api.people.com:3000/people/999.xml 
     100  #   ryan = Person.find(999) # => Raises ActiveResource::ResourceNotFound 
     101  #   # => Response = 404 
     102  #  
     103  # +404+ is just one of the HTTP error response codes that ActiveResource will handle with its own exception. The  
     104  # following HTTP response codes will also result in these exceptions: 
     105  #  
     106  # 200 - 399:: Valid response, no exception 
     107  # 404:: ActiveResource::ResourceNotFound 
     108  # 409:: ActiveResource::ResourceConflict 
     109  # 422:: ActiveResource::ResourceInvalid (rescued by save as validation errors) 
     110  # 401 - 499:: ActiveResource::ClientError 
     111  # 500 - 599:: ActiveResource::ServerError 
     112  # 
     113  # These custom exceptions allow you to deal with resource errors more naturally and with more precision 
     114  # rather than returning a general HTTP error.  For example: 
     115  # 
     116  #   begin 
     117  #     ryan = Person.find(my_id) 
     118  #   rescue ActiveResource::ResourceNotFound 
     119  #     redirect_to :action => 'not_found' 
     120  #   rescue ActiveResource::ResourceConflict, ActiveResource::ResourceInvalid 
     121  #     redirect_to :action => 'new' 
     122  #   end 
     123  # 
     124  # === Validation errors 
     125  #  
     126  # Active Resource supports validations on resources and will return errors if any these validations fail 
     127  # (e.g., "First name can not be blank" and so on).  These types of errors are denoted in the response by  
     128  # a response code of +422+ and an XML representation of the validation errors.  The save operation will  
     129  # then fail (with a +false+ return value) and the validation errors can be accessed on the resource in question. 
     130  #  
     131  #   ryan = Person.find(1) 
     132  #   ryan.first #=> '' 
     133  #   ryan.save  #=> false 
     134  # 
     135  #   # When  
     136  #   # PUT http://api.people.com:3000/people/1.xml 
     137  #   # is requested with invalid values, the response is: 
     138  #   # 
     139  #   # Response (422): 
     140  #   # <errors><error>First cannot be empty</error></errors> 
     141  #   # 
     142  # 
     143  #   ryan.errors.invalid?(:first)  #=> true 
     144  #   ryan.errors.full_messages  #=> ['First cannot be empty'] 
     145  #  
     146  # Learn more about Active Resource's validation features in the ActiveResource::Validations documentation. 
     147  # 
    6148  class Base 
    7     # The logger for diagnosing and tracing ARes calls. 
     149    # The logger for diagnosing and tracing Active Resource calls. 
    8150    cattr_accessor :logger 
    9151 
    10152    class << self 
    11       # Gets the URI of the resource's site 
     153      # Gets the URI of the REST resources to map for this class.  The site variable is required  
     154      # ActiveResource's mapping to work. 
    12155      def site 
    13156        if defined?(@site) 
    14157          @site 
     
    17160        end 
    18161      end 
    19162 
    20       # Set the URI for the REST resources 
     163      # Sets the URI of the REST resources to map for this class to the value in the +site+ argument. 
     164      # The site variable is required ActiveResource's mapping to work. 
    21165      def site=(site) 
    22166        @connection = nil 
    23167        @site = create_site_uri_from(site) 
    24168      end 
    25169 
    26       # Base connection to remote service 
     170      # An instance of ActiveResource::Connection that is the base connection to the remote service. 
     171      # The +refresh+ parameter toggles whether or not the connection is refreshed at every request 
     172      # or not (defaults to +false+). 
    27173      def connection(refresh = false) 
    28174        @connection = Connection.new(site) if refresh || @connection.nil? 
    29175        @connection 
     
    40186      attr_accessor_with_default(:collection_name) { element_name.pluralize } #:nodoc: 
    41187      attr_accessor_with_default(:primary_key, 'id') #:nodoc: 
    42188       
    43       # Gets the resource prefix 
    44       #  prefix/collectionname/1.xml 
     189      # Gets the prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>) 
     190      # This method is regenerated at runtime based on what the prefix is set to. 
    45191      def prefix(options={}) 
    46192        default = site.path 
    47193        default << '/' unless default[-1..-1] == '/' 
     
    50196        prefix(options) 
    51197      end 
    52198 
     199      # An attribute reader for the source string for the resource path prefix.  This 
     200      # method is regenerated at runtime based on what the prefix is set to. 
    53201      def prefix_source 
    54202        prefix # generate #prefix and #prefix_source methods first 
    55203        prefix_source 
    56204      end 
    57205 
    58       # Sets the resource prefix 
    59       #  prefix/collectionname/1.xml 
     206      # Sets the prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>). 
     207      # Default value is <tt>site.path</tt>. 
    60208      def prefix=(value = '/') 
    61209        # Replace :placeholders with '#{embedded options[:lookups]}' 
    62210        prefix_call = value.gsub(/:\w+/) { |key| "\#{options[#{key}]}" } 
     
    77225      alias_method :set_element_name, :element_name=  #:nodoc: 
    78226      alias_method :set_collection_name, :collection_name=  #:nodoc: 
    79227 
    80       # Gets the element path for the given ID.  If no query_options are given, they are split from the prefix options: 
     228      # Gets the element path for the given ID in +id+.  If the +query_options+ parameter is omitted, Rails 
     229      # will split from the prefix options. 
    81230      # 
    82       # Post.element_path(1) # => /posts/1.xml 
    83       # Comment.element_path(1, :post_id => 5) # => /posts/5/comments/1.xml 
    84       # Comment.element_path(1, :post_id => 5, :active => 1) # => /posts/5/comments/1.xml?active=1 
    85       # Comment.element_path(1, {:post_id => 5}, {:active => 1}) # => /posts/5/comments/1.xml?active=1 
     231      # ==== Options 
     232      # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt> 
     233      #                    would yield a URL like <tt>/accounts/19/purchases.xml</tt>). 
     234      # +query_options+:: A hash to add items to the query string for the request. 
     235      # 
     236      # ==== Examples 
     237      #   Post.element_path(1)  
     238      #   # => /posts/1.xml 
     239      # 
     240      #   Comment.element_path(1, :post_id => 5)  
     241      #   # => /posts/5/comments/1.xml 
     242      # 
     243      #   Comment.element_path(1, :post_id => 5, :active => 1)  
     244      #   # => /posts/5/comments/1.xml?active=1 
     245      # 
     246      #   Comment.element_path(1, {:post_id => 5}, {:active => 1})  
     247      #   # => /posts/5/comments/1.xml?active=1 
     248      # 
    86249      def element_path(id, prefix_options = {}, query_options = nil) 
    87250        prefix_options, query_options = split_options(prefix_options) if query_options.nil? 
    88251        "#{prefix(prefix_options)}#{collection_name}/#{id}.xml#{query_string(query_options)}" 
    89252      end 
    90253 
    91       # Gets the collection path.  If no query_options are given, they are split from the prefix options: 
     254      # Gets the collection path for the REST resources.  If the +query_options+ parameter is omitted, Rails 
     255      # will split from the +prefix_options+. 
    92256      # 
    93       # Post.collection_path # => /posts.xml 
    94       # Comment.collection_path(:post_id => 5) # => /posts/5/comments.xml 
    95       # Comment.collection_path(:post_id => 5, :active => 1) # => /posts/5/comments.xml?active=1 
    96       # Comment.collection_path({:post_id => 5}, {:active => 1}) # => /posts/5/comments.xml?active=1 
     257      # ==== Options 
     258      # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., <tt>:account_id => 19</tt> 
     259      #                    would yield a URL like <tt>/accounts/19/purchases.xml</tt>). 
     260      # +query_options+:: A hash to add items to the query string for the request. 
     261      # 
     262      # ==== Examples 
     263      #   Post.collection_path 
     264      #   # => /posts.xml 
     265      # 
     266      #   Comment.collection_path(:post_id => 5)  
     267      #   # => /posts/5/comments.xml 
     268      # 
     269      #   Comment.collection_path(:post_id => 5, :active => 1)  
     270      #   # => /posts/5/comments.xml?active=1 
     271      # 
     272      #   Comment.collection_path({:post_id => 5}, {:active => 1})  
     273      #   # => /posts/5/comments.xml?active=1 
     274      # 
    97275      def collection_path(prefix_options = {}, query_options = nil) 
    98276        prefix_options, query_options = split_options(prefix_options) if query_options.nil? 
    99277        "#{prefix(prefix_options)}#{collection_name}.xml#{query_string(query_options)}" 
     
    102280      alias_method :set_primary_key, :primary_key=  #:nodoc: 
    103281 
    104282      # Create a new resource instance and request to the remote service 
    105       # that it be saved.  This is equivalent to the following simultaneous calls: 
     283      # that it be saved, making it equivalent to the following simultaneous calls: 
    106284      # 
    107285      #   ryan = Person.new(:first => 'ryan') 
    108286      #   ryan.save 
    109287      # 
    110288      # The newly created resource is returned.  If a failure has occurred an 
    111289      # exception will be raised (see save).  If the resource is invalid and 
    112       # has not been saved then <tt>resource.valid?</tt> will return <tt>false</tt>, 
    113       # while <tt>resource.new?</tt> will still return <tt>true</tt>. 
    114       #       
     290      # has not been saved then valid? will return <tt>false</tt>, 
     291      # while new? will still return <tt>true</tt>. 
     292      # 
     293      # ==== Examples 
     294      #   Person.create(:name => 'Jeremy', :email => 'myname@nospam.com', :enabled => true) 
     295      #   my_person = Person.find(:first) 
     296      #   my_person.email 
     297      #   # => myname@nospam.com 
     298      # 
     299      #   dhh = Person.create(:name => 'David', :email => 'dhh@nospam.com', :enabled => true) 
     300      #   dhh.valid? 
     301      #   # => true 
     302      #   dhh.new? 
     303      #   # => false 
     304      # 
     305      #   # We'll assume that there's a validation that requires the name attribute 
     306      #   that_guy = Person.create(:name => '', :email => 'thatguy@nospam.com', :enabled => true) 
     307      #   that_guy.valid? 
     308      #   # => false 
     309      #   that_guy.new? 
     310      #   # => true 
     311      # 
    115312      def create(attributes = {}) 
    116313        returning(self.new(attributes)) { |res| res.save }         
    117314      end 
    118315 
    119316      # Core method for finding resources.  Used similarly to Active Record's find method. 
    120317      # 
    121       #   Person.find(1)                                         # => GET /people/1.xml 
    122       #   Person.find(:all)                                      # => GET /people.xml 
    123       #   Person.find(:all, :params => { :title => "CEO" })      # => GET /people.xml?title=CEO 
    124       #   Person.find(:all, :from => :managers)                  # => GET /people/managers.xml 
    125       #   Person.find(:all, :from => "/companies/1/people.xml")  # => GET /companies/1/people.xml 
    126       #   Person.find(:one, :from => :leader)                    # => GET /people/leader.xml 
    127       #   Person.find(:one, :from => "/companies/1/manager.xml") # => GET /companies/1/manager.xml 
    128       #   StreetAddress.find(1, :params => { :person_id => 1 })  # => GET /people/1/street_addresses/1.xml 
     318      # ==== Arguments 
     319      # The first argument is considered to be the scope of the query.  That is, how many  
     320      # resources are returned from the request.  It can be one of the following. 
     321      # 
     322      # +:one+:: Returns a single resource. 
     323      # +:first+:: Returns the first resource found. 
     324      # +:all+:: Returns every resource that matches the request. 
     325      #  
     326      # ==== Options 
     327      # +from+:: Sets the path or custom method that resources will be fetched from. 
     328      # +params+:: Sets query and prefix (nested URL) parameters. 
     329      # 
     330      # ==== Examples 
     331      #   Person.find(1)                                          
     332      #   # => GET /people/1.xml 
     333      # 
     334      #   Person.find(:all)                                       
     335      #   # => GET /people.xml 
     336      # 
     337      #   Person.find(:all, :params => { :title => "CEO" })       
     338      #   # => GET /people.xml?title=CEO 
     339      # 
     340      #   Person.find(:first, :from => :managers)                   
     341      #   # => GET /people/managers.xml 
     342      # 
     343      #   Person.find(:all, :from => "/companies/1/people.xml")   
     344      #   # => GET /companies/1/people.xml 
     345      # 
     346      #   Person.find(:one, :from => :leader)                     
     347      #   # => GET /people/leader.xml 
     348      # 
     349      #   Person.find(:one, :from => "/companies/1/manager.xml")  
     350      #   # => GET /companies/1/manager.xml 
     351      # 
     352      #   StreetAddress.find(1, :params => { :person_id => 1 })   
     353      #   # => GET /people/1/street_addresses/1.xml 
    129354      def find(*arguments) 
    130355        scope   = arguments.slice!(0) 
    131356        options = arguments.slice!(0) || {} 
     
    138363        end 
    139364      end 
    140365 
     366      # Deletes the resources with the ID in the +id+ parameter. 
     367      # 
     368      # ==== Options 
     369      # All options specify prefix and query parameters. 
     370      # 
     371      # ==== Examples 
     372      #   Event.delete(2) 
     373      #   # => DELETE /events/2 
     374      # 
     375      #   Event.create(:name => 'Free Concert', :location => 'Community Center') 
     376      #   my_event = Event.find(:first) 
     377      #   # => Events (id: 7) 
     378      #   Event.delete(my_event.id) 
     379      #   # => DELETE /events/7 
     380      # 
     381      #   # Let's assume a request to events/5/cancel.xml 
     382      #   Event.delete(params[:id]) 
     383      #   # => DELETE /events/5 
     384      # 
    141385      def delete(id, options = {}) 
    142386        connection.delete(element_path(id, options)) 
    143387      end 
    144388 
    145       # Evalutes to <tt>true</tt> if the resource is found. 
     389      # Asserts the existence of a resource, returning <tt>true</tt> if the resource is found. 
     390      # 
     391      # ==== Examples 
     392      #   Note.create(:title => 'Hello, world.', :body => 'Nothing more for now...') 
     393      #   Note.exists?(1) 
     394      #   # => true 
     395      # 
     396      #   Note.exists(1349) 
     397      #   # => false 
    146398      def exists?(id, options = {}) 
    147399        id && !find_single(id, options).nil? 
    148400      rescue ActiveResource::ResourceNotFound 
     
    226478    attr_accessor :attributes #:nodoc: 
    227479    attr_accessor :prefix_options #:nodoc: 
    228480 
     481    # Constructor method for new resources; the optional +attributes+ parameter takes a +Hash+ 
     482    # of attributes for the new resource. 
     483    # 
     484    # ==== Examples 
     485    #   my_course = Course.new 
     486    #   my_course.name = "Western Civilization" 
     487    #   my_course.lecturer = "Don Trotter" 
     488    #   my_course.save 
     489    # 
     490    #   my_other_course = Course.new(:name => "Philosophy: Reason and Being", :lecturer => "Ralph Cling") 
     491    #   my_other_course.save 
    229492    def initialize(attributes = {}) 
    230493      @attributes     = {} 
    231494      @prefix_options = {} 
    232495      load(attributes) 
    233496    end 
    234497 
    235     # Is the resource a new object? 
     498    # A method to determine if the resource a new object (i.e., it has not been POSTed to the remote service yet). 
     499    # 
     500    # ==== Examples 
     501    #   not_new = Computer.create(:brand => 'Apple', :make => 'MacBook', :vendor => 'MacMall') 
     502    #   not_new.new? 
     503    #   # => false 
     504    # 
     505    #   is_new = Computer.new(:brand => 'IBM', :make => 'Thinkpad', :vendor => 'IBM') 
     506    #   is_new.new? 
     507    #   # => true 
     508    # 
     509    #   is_new.save 
     510    #   is_new.new? 
     511    #   # => false 
     512    # 
    236513    def new? 
    237514      id.nil? 
    238515    end 
    239516 
    240     # Get the id of the object
     517    # Get the +id+ attribute of the resource
    241518    def id 
    242519      attributes[self.class.primary_key] 
    243520    end 
    244521 
    245     # Set the id of the object
     522    # Set the +id+ attribute of the resource
    246523    def id=(id) 
    247524      attributes[self.class.primary_key] = id 
    248525    end 
    249526 
    250     # True if and only if +other+ is the same object or is an instance of the same class, is not +new?+, and has the same +id+. 
     527    # Test for equality.  Resource are equal if and only if +other+ is the same object or  
     528    # is an instance of the same class, is not +new?+, and has the same +id+. 
     529    # 
     530    # ==== Examples 
     531    #   ryan = Person.create(:name => 'Ryan') 
     532    #   jamie = Person.create(:name => 'Jamie') 
     533    # 
     534    #   ryan == jamie 
     535    #   # => false (Different name attribute and id) 
     536    # 
     537    #   ryan_again = Person.new(:name => 'Ryan') 
     538    #   ryan == ryan_again 
     539    #   # => false (ryan_again is new?) 
     540    # 
     541    #   ryans_clone = Person.create(:name => 'Ryan') 
     542    #   ryan == ryans_clone 
     543    #   # => false (Different id attributes) 
     544    # 
     545    #   ryans_twin = Person.find(ryan.id) 
     546    #   ryan == ryans_twin 
     547    #   # => true 
     548    # 
    251549    def ==(other) 
    252550      other.equal?(self) || (other.instance_of?(self.class) && !other.new? && other.id == id) 
    253551    end 
    254552 
    255     # Delegates to == 
     553    # Tests for equality (delegates to ==). 
    256554    def eql?(other) 
    257555      self == other 
    258556    end 
     
    263561      id.hash 
    264562    end 
    265563     
     564    # Duplicate the current resource without saving it. 
     565    # 
     566    # ==== Examples 
     567    #   my_invoice = Invoice.create(:customer => 'That Company') 
     568    #   next_invoice = my_invoice.dup 
     569    #   next_invoice.new? 
     570    #   # => true 
     571    # 
     572    #   next_invoice.save 
     573    #   next_invoice == my_invoice 
     574    #   # => false (different id attributes) 
     575    # 
     576    #   my_invoice.customer 
     577    #   # => That Company 
     578    #   next_invoice.customer 
     579    #   # => That Company 
     580    # 
    266581    def dup 
    267582      returning new do |resource| 
    268583        resource.attributes     = @attributes 
     
    270585      end 
    271586    end 
    272587 
    273     # Delegates to +create+ if a new object, +update+ if its old. If the response to the save includes a body, 
    274     # it will be assumed that this body is XML for the final object as it looked after the save (which would include 
    275     # attributes like created_at that wasn't part of the original submit). 
     588    # A method to save (+POST+) or update (+PUT+) a resource.  It delegates to +create+ if a new object,  
     589    # +update+ if it is existing. If the response to the save includes a body, it will be assumed that this body 
     590    # is XML for the final object as it looked after the save (which would include attributes like +created_at+ 
     591    # that weren't part of the original submit). 
     592    # 
     593    # ==== Examples 
     594    #   my_company = Company.new(:name => 'RoleModel Software', :owner => 'Ken Auer', :size => 2) 
     595    #   my_company.new? 
     596    #   # => true 
     597    #   my_company.save 
     598    #   # => POST /companies/ (create) 
     599    # 
     600    #   my_company.new? 
     601    #   # => false 
     602    #   my_company.size = 10 
     603    #   my_company.save 
     604    #   # => PUT /companies/1 (update) 
     605    # 
    276606    def save 
    277607      new? ? create : update 
    278608    end 
    279609 
    280     # Delete the resource. 
     610    # Deletes the resource from the remote service. 
     611    # 
     612    # ==== Examples 
     613    #   my_id = 3 
     614    #   my_person = Person.find(my_id) 
     615    #   my_person.destroy 
     616    #   Person.find(my_id) 
     617    #   # => 404 (Resource Not Found) 
     618    #    
     619    #   new_person = Person.create(:name => 'James') 
     620    #   new_id = new_person.id  
     621    #   # => 7 
     622    #   new_person.destroy 
     623    #   Person.find(new_id) 
     624    #   # => 404 (Resource Not Found) 
     625    # 
    281626    def destroy 
    282627      connection.delete(element_path, self.class.headers) 
    283628    end 
    284629 
    285     # Evaluates to <tt>true</tt> if this resource is found. 
     630    # Evaluates to <tt>true</tt> if this resource is not +new?+ and is 
     631    # found on the remote service.  Using this method, you can check for 
     632    # resources that may have been deleted between the object's instantiation 
     633    # and actions on it. 
     634    # 
     635    # ==== Examples 
     636    #   Person.create(:name => 'Theodore Roosevelt') 
     637    #   that_guy = Person.find(:first) 
     638    #   that_guy.exists? 
     639    #   # => true 
     640    # 
     641    #   that_lady = Person.new(:name => 'Paul Bean') 
     642    #   that_lady.exists? 
     643    #   # => false 
     644    # 
     645    #   guys_id = that_guy.id 
     646    #   Person.delete(guys_id) 
     647    #   that_guy.exists? 
     648    #   # => false 
     649    # 
    286650    def exists? 
    287651      !new? && self.class.exists?(id, :params => prefix_options) 
    288652    end 
    289653 
    290     # Convert the resource to an XML string 
     654    # A method to convert the the resource to an XML string. 
     655    # 
     656    # ==== Options 
     657    # The +options+ parameter is handed off to the +to_xml+ method on each 
     658    # attribute, so it has the same options as the +to_xml+ methods in 
     659    # ActiveSupport. 
     660    # 
     661    # indent:: Set the indent level for the XML output (default is +2+). 
     662    # dasherize:: Boolean option to determine whether or not element names should 
     663    #             replace underscores with dashes (default is +false+). 
     664    # skip_instruct::  Toggle skipping the +instruct!+ call on the XML builder  
     665    #                  that generates the XML declaration (default is +false+). 
     666    # 
     667    # ==== Examples 
     668    #   my_group = SubsidiaryGroup.find(:first) 
     669    #   my_group.to_xml 
     670    #   # => <?xml version="1.0" encoding="UTF-8"?> 
     671    #   #    <subsidiary_group> [...] </subsidiary_group> 
     672    # 
     673    #   my_group.to_xml(:dasherize => true) 
     674    #   # => <?xml version="1.0" encoding="UTF-8"?> 
     675    #   #    <subsidiary-group> [...] </subsidiary-group> 
     676    # 
     677    #   my_group.to_xml(:skip_instruct => true) 
     678    #   # => <subsidiary_group> [...] </subsidiary_group> 
     679    # 
    291680