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 14 14 @base, @errors = base, {} 15 15 end 16 16 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 # 17 31 def add_to_base(msg) 18 32 add(:base, msg) 19 33 end 20 34 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 # 21 50 def add(attribute, msg) 22 51 @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? 23 52 @errors[attribute.to_s] << msg 24 53 end 25 54 26 55 # 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 27 67 def invalid?(attribute) 28 68 !@errors[attribute.to_s].nil? 29 69 end 30 70 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'] 34 87 def on(attribute) 35 88 errors = @errors[attribute.to_s] 36 89 return nil if errors.nil? … … 39 92 40 93 alias :[] :on 41 94 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 # 43 112 def on_base 44 113 on(:base) 45 114 end 46 115 47 116 # 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 # 48 128 def each 49 129 @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } } 50 130 end 51 131 52 132 # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned 53 133 # 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 # 54 145 def each_full 55 146 full_messages.each { |msg| yield msg } 56 147 end 57 148 58 149 # 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 # 59 161 def full_messages 60 162 full_messages = [] 61 163 … … 79 181 80 182 # Returns the total number of errors added. Two errors added to the same attribute will be counted as such 81 183 # 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 # 82 195 def size 83 196 @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size } 84 197 end … … 86 199 alias_method :count, :size 87 200 alias_method :length, :size 88 201 202 # Grabs errors from the XML response. 89 203 def from_xml(xml) 90 204 clear 91 205 humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) } … … 102 216 end 103 217 end 104 218 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). 108 223 # 224 # ==== Example 225 # 109 226 # class Person < ActiveResource::Base 110 227 # self.site = "http://www.localhost.com:3000/" 111 228 # protected … … 133 250 # person.attributes = { "last_name" => "Halpert", "phone_number" => "555-5555" } 134 251 # person.save # => true (and person is now saved to the remote service) 135 252 # 136 # An Errors object is automatically created for every resource.137 253 module Validations 138 254 def self.included(base) # :nodoc: 139 255 base.class_eval do … … 141 257 end 142 258 end 143 259 260 # Validate a resource and save (POST) it to the remote web service. 144 261 def save_with_validation 145 262 save_without_validation 146 263 true … … 149 266 false 150 267 end 151 268 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 152 279 def valid? 153 280 errors.empty? 154 281 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: 2 4 # 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 } 4 8 # 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: 8 10 # 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 10 15 # 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. 15 18 # 16 # This module provides the ability for Active Resource to call these17 # custom REST methods and get the response back.18 #19 19 # class Person < ActiveResource::Base 20 20 # self.site = "http://37s.sunrise.i:3000" 21 21 # end 22 22 # 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' } 24 25 # 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 27 28 # 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 # 29 32 module ActiveResource 30 33 module CustomMethods 31 34 def self.included(within) -
activeresource/lib/active_resource/connection.rb
old new 5 5 require 'benchmark' 6 6 7 7 module ActiveResource 8 class ConnectionError < StandardError 8 class ConnectionError < StandardError # :nodoc: 9 9 attr_reader :response 10 10 11 11 def initialize(response, message = nil) … … 18 18 end 19 19 end 20 20 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: 24 29 25 class ServerError < ConnectionError; end # 5xx Server Error 30 # 5xx Server Error 31 class ServerError < ConnectionError; end # :nodoc: 26 32 27 33 # 405 Method Not Allowed 28 class MethodNotAllowed < ClientError 34 class MethodNotAllowed < ClientError # :nodoc: 29 35 def allowed_methods 30 36 @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym } 31 37 end 32 38 end 33 39 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. 35 43 class Connection 36 44 attr_reader :site 37 45 … … 46 54 end 47 55 end 48 56 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. 49 60 def initialize(site) 50 61 raise ArgumentError, 'Missing site URI' unless site 51 62 self.site = site 52 63 end 53 64 54 # Set URI forremote service.65 # Attribute writer to set the URI for the remote service. 55 66 def site=(site) 56 67 @site = site.is_a?(URI) ? site : URI.parse(site) 57 68 end 58 69 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). 61 72 def get(path, headers = {}) 62 73 xml_from_response(request(:get, path, build_request_headers(headers))) 63 74 end 64 75 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). 67 79 def delete(path, headers = {}) 68 80 request(:delete, path, build_request_headers(headers)) 69 81 end 70 82 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). 73 86 def put(path, body = '', headers = {}) 74 87 request(:put, path, body, build_request_headers(headers)) 75 88 end 76 89 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). 79 92 def post(path, body = '', headers = {}) 80 93 request(:post, path, body, build_request_headers(headers)) 81 94 end 82 95 96 # A method to convert XML from the response body into a usable +Hash+ instance. 83 97 def xml_from_response(response) 84 98 if response = from_xml_data(Hash.from_xml(response.body)) 85 99 response.first … … 88 102 end 89 103 end 90 104 91 92 105 private 93 106 # Makes request to remote service. 94 107 def request(method, path, *arguments) -
activeresource/lib/active_resource/base.rb
old new 3 3 require 'set' 4 4 5 5 module 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 # 6 148 class Base 7 # The logger for diagnosing and tracing A Rescalls.149 # The logger for diagnosing and tracing Active Resource calls. 8 150 cattr_accessor :logger 9 151 10 152 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. 12 155 def site 13 156 if defined?(@site) 14 157 @site … … 17 160 end 18 161 end 19 162 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. 21 165 def site=(site) 22 166 @connection = nil 23 167 @site = create_site_uri_from(site) 24 168 end 25 169 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+). 27 173 def connection(refresh = false) 28 174 @connection = Connection.new(site) if refresh || @connection.nil? 29 175 @connection … … 40 186 attr_accessor_with_default(:collection_name) { element_name.pluralize } #:nodoc: 41 187 attr_accessor_with_default(:primary_key, 'id') #:nodoc: 42 188 43 # Gets the resource prefix44 # prefix/collectionname/1.xml189 # 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. 45 191 def prefix(options={}) 46 192 default = site.path 47 193 default << '/' unless default[-1..-1] == '/' … … 50 196 prefix(options) 51 197 end 52 198 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. 53 201 def prefix_source 54 202 prefix # generate #prefix and #prefix_source methods first 55 203 prefix_source 56 204 end 57 205 58 # Sets the resource prefix59 # prefix/collectionname/1.xml206 # 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>. 60 208 def prefix=(value = '/') 61 209 # Replace :placeholders with '#{embedded options[:lookups]}' 62 210 prefix_call = value.gsub(/:\w+/) { |key| "\#{options[#{key}]}" } … … 77 225 alias_method :set_element_name, :element_name= #:nodoc: 78 226 alias_method :set_collection_name, :collection_name= #:nodoc: 79 227 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. 81 230 # 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 # 86 249 def element_path(id, prefix_options = {}, query_options = nil) 87 250 prefix_options, query_options = split_options(prefix_options) if query_options.nil? 88 251 "#{prefix(prefix_options)}#{collection_name}/#{id}.xml#{query_string(query_options)}" 89 252 end 90 253 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+. 92 256 # 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 # 97 275 def collection_path(prefix_options = {}, query_options = nil) 98 276 prefix_options, query_options = split_options(prefix_options) if query_options.nil? 99 277 "#{prefix(prefix_options)}#{collection_name}.xml#{query_string(query_options)}" … … 102 280 alias_method :set_primary_key, :primary_key= #:nodoc: 103 281 104 282 # Create a new resource instance and request to the remote service 105 # that it be saved . This isequivalent to the following simultaneous calls:283 # that it be saved, making it equivalent to the following simultaneous calls: 106 284 # 107 285 # ryan = Person.new(:first => 'ryan') 108 286 # ryan.save 109 287 # 110 288 # The newly created resource is returned. If a failure has occurred an 111 289 # 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 # 115 312 def create(attributes = {}) 116 313 returning(self.new(attributes)) { |res| res.save } 117 314 end 118 315 119 316 # Core method for finding resources. Used similarly to Active Record's find method. 120 317 # 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 129 354 def find(*arguments) 130 355 scope = arguments.slice!(0) 131 356 options = arguments.slice!(0) || {} … … 138 363 end 139 364 end 140 365 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 # 141 385 def delete(id, options = {}) 142 386 connection.delete(element_path(id, options)) 143 387 end 144 388 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 146 398 def exists?(id, options = {}) 147 399 id && !find_single(id, options).nil? 148 400 rescue ActiveResource::ResourceNotFound … … 226 478 attr_accessor :attributes #:nodoc: 227 479 attr_accessor :prefix_options #:nodoc: 228 480 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 229 492 def initialize(attributes = {}) 230 493 @attributes = {} 231 494 @prefix_options = {} 232 495 load(attributes) 233 496 end 234 497 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 # 236 513 def new? 237 514 id.nil? 238 515 end 239 516 240 # Get the id of the object.517 # Get the +id+ attribute of the resource. 241 518 def id 242 519 attributes[self.class.primary_key] 243 520 end 244 521 245 # Set the id of the object.522 # Set the +id+ attribute of the resource. 246 523 def id=(id) 247 524 attributes[self.class.primary_key] = id 248 525 end 249 526 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 # 251 549 def ==(other) 252 550 other.equal?(self) || (other.instance_of?(self.class) && !other.new? && other.id == id) 253 551 end 254 552 255 # Delegates to ==553 # Tests for equality (delegates to ==). 256 554 def eql?(other) 257 555 self == other 258 556 end … … 263 561 id.hash 264 562 end 265 563 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 # 266 581 def dup 267 582 returning new do |resource| 268 583 resource.attributes = @attributes … … 270 585 end 271 586 end 272 587 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 # 276 606 def save 277 607 new? ? create : update 278 608 end 279 609 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 # 281 626 def destroy 282 627 connection.delete(element_path, self.class.headers) 283 628 end 284 629 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 # 286 650 def exists? 287 651 !new? && self.class.exists?(id, :params => prefix_options) 288 652 end 289 653 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 # 291 680