diff options
Diffstat (limited to 'activeresource/lib/active_resource/base.rb')
-rw-r--r-- | activeresource/lib/active_resource/base.rb | 104 |
1 files changed, 63 insertions, 41 deletions
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 62420725ad..160763779e 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1,6 +1,6 @@ require 'active_support' require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/module/attr_accessor_with_default' @@ -12,6 +12,7 @@ require 'active_support/core_ext/object/duplicable' require 'set' require 'uri' +require 'active_support/core_ext/uri' require 'active_resource/exceptions' require 'active_resource/connection' require 'active_resource/formats' @@ -21,7 +22,7 @@ require 'active_resource/log_subscriber' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. # - # For an outline of what Active Resource is capable of, see link:files/vendor/rails/activeresource/README.html. + # For an outline of what Active Resource is capable of, see its {README}[link:files/activeresource/README_rdoc.html]. # # == Automated mapping # @@ -35,7 +36,7 @@ module ActiveResource # end # # Now the Person class is mapped to RESTful resources located at <tt>http://api.people.com:3000/people/</tt>, and - # you can now use Active Resource's lifecycle methods to manipulate resources. In the case where you already have + # you can now use Active Resource's life cycle methods to manipulate resources. In the case where you already have # an existing model with the same name as the desired RESTful resource you can set the +element_name+ value. # # class PersonResource < ActiveResource::Base @@ -51,7 +52,7 @@ module ActiveResource # end # # - # == Lifecycle methods + # == Life cycle methods # # Active Resource exposes methods for creating, finding, updating, and deleting resources # from REST web services. @@ -70,12 +71,12 @@ module ActiveResource # # ryan.destroy # => true # - # As you can see, these are very similar to Active Record's lifecycle methods for database records. + # As you can see, these are very similar to Active Record's life cycle methods for database records. # You can read more about each of these methods in their respective documentation. # # === Custom REST methods # - # Since simple CRUD/lifecycle methods can't accomplish every task, Active Resource also supports + # Since simple CRUD/life cycle methods can't accomplish every task, Active Resource also supports # defining your own custom REST methods. To invoke them, Active Resource provides the <tt>get</tt>, # <tt>post</tt>, <tt>put</tt> and <tt>\delete</tt> methods where you can specify a custom REST method # name to invoke. @@ -166,6 +167,7 @@ module ActiveResource # # GET http://api.people.com:3000/people/999.xml # ryan = Person.find(999) # 404, raises ActiveResource::ResourceNotFound # + # # <tt>404</tt> is just one of the HTTP error response codes that Active Resource will handle with its own exception. The # following HTTP response codes will also result in these exceptions: # @@ -194,6 +196,16 @@ module ActiveResource # redirect_to :action => 'new' # end # + # When a GET is requested for a nested resource and you don't provide the prefix_param + # an ActiveResource::MissingPrefixParam will be raised. + # + # class Comment < ActiveResource::Base + # self.site = "http://someip.com/posts/:post_id/" + # end + # + # Comment.find(1) + # # => ActiveResource::MissingPrefixParam: post_id prefix_option is missing + # # === Validation errors # # Active Resource supports validations on resources and will return errors if any of these validations fail @@ -251,6 +263,8 @@ module ActiveResource # The logger for diagnosing and tracing Active Resource calls. cattr_accessor :logger + class_attribute :_format + class << self # Creates a schema for this resource - setting the attributes that are # known prior to fetching an instance from the remote system. @@ -403,8 +417,8 @@ module ActiveResource @site = nil else @site = create_site_uri_from(site) - @user = uri_parser.unescape(@site.user) if @site.user - @password = uri_parser.unescape(@site.password) if @site.password + @user = URI.parser.unescape(@site.user) if @site.user + @password = URI.parser.unescape(@site.password) if @site.password end end @@ -480,13 +494,13 @@ module ActiveResource format = mime_type_reference_or_format.is_a?(Symbol) ? ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format - write_inheritable_attribute(:format, format) + self._format = format connection.format = format if site end # Returns the current format, default is ActiveResource::Formats::XmlFormat. def format - read_inheritable_attribute(:format) || ActiveResource::Formats::XmlFormat + self._format || ActiveResource::Formats::XmlFormat end # Sets the number of seconds after which requests to the REST API should time out. @@ -508,9 +522,9 @@ module ActiveResource # # * <tt>:key</tt> - An OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. # * <tt>:cert</tt> - An OpenSSL::X509::Certificate object as client certificate - # * <tt>:ca_file</tt> - Path to a CA certification file in PEM format. The file can contrain several CA certificates. + # * <tt>:ca_file</tt> - Path to a CA certification file in PEM format. The file can contain several CA certificates. # * <tt>:ca_path</tt> - Path of a CA certification directory containing certifications in PEM format. - # * <tt>:verify_mode</tt> - Flags for server the certification verification at begining of SSL/TLS session. (OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable) + # * <tt>:verify_mode</tt> - Flags for server the certification verification at beginning of SSL/TLS session. (OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable) # * <tt>:verify_callback</tt> - The verify callback for the server certification verification. # * <tt>:verify_depth</tt> - The maximum depth for the certificate chain verification. # * <tt>:cert_store</tt> - OpenSSL::X509::Store to verify peer certificate. @@ -577,7 +591,7 @@ module ActiveResource # Default value is <tt>site.path</tt>. def prefix=(value = '/') # Replace :placeholders with '#{embedded options[:lookups]}' - prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.escape options[#{key}].to_s}" } + prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.parser.escape options[#{key}].to_s}" } # Clear prefix parameters in case they have been cached @prefix_parameters = nil @@ -589,8 +603,8 @@ module ActiveResource def prefix(options={}) "#{prefix_call}" end RUBY_EVAL end - rescue - logger.error "Couldn't set prefix: #{$!}\n #{code}" if logger + rescue Exception => e + logger.error "Couldn't set prefix: #{e}\n #{code}" if logger raise end @@ -621,8 +635,10 @@ module ActiveResource # # => /posts/5/comments/1.xml?active=1 # def element_path(id, prefix_options = {}, query_options = nil) + check_prefix_options(prefix_options) + prefix_options, query_options = split_options(prefix_options) if query_options.nil? - "#{prefix(prefix_options)}#{collection_name}/#{URI.escape id.to_s}.#{format.extension}#{query_string(query_options)}" + "#{prefix(prefix_options)}#{collection_name}/#{URI.parser.escape id.to_s}.#{format.extension}#{query_string(query_options)}" end # Gets the new element path for REST resources. @@ -663,6 +679,7 @@ module ActiveResource # # => /posts/5/comments.xml?active=1 # def collection_path(prefix_options = {}, query_options = nil) + check_prefix_options(prefix_options) prefix_options, query_options = split_options(prefix_options) if query_options.nil? "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}" end @@ -678,7 +695,7 @@ module ActiveResource # Returns the new resource instance. # def build(attributes = {}) - attrs = connection.get("#{new_element_path}").merge(attributes) + attrs = self.format.decode(connection.get("#{new_element_path}").body).merge(attributes) self.new(attrs) end @@ -842,6 +859,14 @@ module ActiveResource end private + + def check_prefix_options(prefix_options) + p_options = HashWithIndifferentAccess.new(prefix_options) + prefix_parameters.each do |p| + raise(MissingPrefixParam, "#{p} prefix_option is missing") if p_options[p].blank? + end + end + # Find every resource def find_every(options) begin @@ -850,11 +875,11 @@ module ActiveResource instantiate_collection(get(from, options[:params])) when String path = "#{from}#{query_string(options[:params])}" - instantiate_collection(connection.get(path, headers) || []) + instantiate_collection(format.decode(connection.get(path, headers).body) || []) else prefix_options, query_options = split_options(options[:params]) path = collection_path(prefix_options, query_options) - instantiate_collection( (connection.get(path, headers) || []), prefix_options ) + instantiate_collection( (format.decode(connection.get(path, headers).body) || []), prefix_options ) end rescue ActiveResource::ResourceNotFound # Swallowing ResourceNotFound exceptions and return nil - as per @@ -870,7 +895,7 @@ module ActiveResource instantiate_record(get(from, options[:params])) when String path = "#{from}#{query_string(options[:params])}" - instantiate_record(connection.get(path, headers)) + instantiate_record(format.decode(connection.get(path, headers).body)) end end @@ -878,7 +903,7 @@ module ActiveResource def find_single(scope, options) prefix_options, query_options = split_options(options[:params]) path = element_path(scope, prefix_options, query_options) - instantiate_record(connection.get(path, headers), prefix_options) + instantiate_record(format.decode(connection.get(path, headers).body), prefix_options) end def instantiate_collection(collection, prefix_options = {}) @@ -886,7 +911,7 @@ module ActiveResource end def instantiate_record(record, prefix_options = {}) - new(record).tap do |resource| + new(record, true).tap do |resource| resource.prefix_options = prefix_options end end @@ -894,12 +919,12 @@ module ActiveResource # Accepts a URI and creates the site URI from that. def create_site_uri_from(site) - site.is_a?(URI) ? site.dup : uri_parser.parse(site) + site.is_a?(URI) ? site.dup : URI.parser.parse(site) end # Accepts a URI and creates the proxy URI from that. def create_proxy_uri_from(proxy) - proxy.is_a?(URI) ? proxy.dup : uri_parser.parse(proxy) + proxy.is_a?(URI) ? proxy.dup : URI.parser.parse(proxy) end # contains a set of the current prefix parameters. @@ -924,10 +949,6 @@ module ActiveResource [ prefix_options, query_options ] end - - def uri_parser - @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI - end end attr_accessor :attributes #:nodoc: @@ -959,9 +980,10 @@ module ActiveResource # # my_other_course = Course.new(:name => "Philosophy: Reason and Being", :lecturer => "Ralph Cling") # my_other_course.save - def initialize(attributes = {}) + def initialize(attributes = {}, persisted = false) @attributes = {}.with_indifferent_access @prefix_options = {} + @persisted = persisted load(attributes) end @@ -987,10 +1009,7 @@ module ActiveResource # not_ryan.hash # => {:not => "an ARes instance"} def clone # Clone all attributes except the pk and any nested ARes - cloned = attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.inject({}) do |attrs, (k, v)| - attrs[k] = v.clone - attrs - end + cloned = Hash[attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.map { |k, v| [k, v.clone] }] # Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which # attempts to convert hashes into member objects and arrays into collections of objects. We want # the raw objects to be cloned so we bypass load by directly setting the attributes hash. @@ -1014,7 +1033,7 @@ module ActiveResource # is_new.new? # => false # def new? - id.nil? + !persisted? end alias :new_record? :new? @@ -1031,7 +1050,7 @@ module ActiveResource # not_persisted.persisted? # => true # def persisted? - !new? + @persisted end # Gets the <tt>\id</tt> attribute of the resource. @@ -1076,7 +1095,7 @@ module ActiveResource end # Delegates to id in order to allow two resources of the same type and \id to work with something like: - # [Person.find(1), Person.find(2)] & [Person.find(1), Person.find(4)] # => [Person.find(1)] + # [(a = Person.find 1), (b = Person.find 2)] & [(c = Person.find 1), (d = Person.find 4)] # => [a] def hash id.hash end @@ -1318,8 +1337,9 @@ module ActiveResource end def load_attributes_from_response(response) - if response['Content-Length'] != "0" && response.body.strip.size > 0 + if !response['Content-Length'].blank? && response['Content-Length'] != "0" && !response.body.nil? && response.body.strip.size > 0 load(self.class.format.decode(response.body)) + @persisted = true end end @@ -1353,8 +1373,9 @@ module ActiveResource namespaces = module_names[0, module_names.size-1].map do |module_name| receiver = receiver.const_get(module_name) end - if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(resource_name) } - return namespace.const_get(resource_name) + const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false] + if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(*const_args) } + return namespace.const_get(*const_args) else raise NameError end @@ -1370,8 +1391,9 @@ module ActiveResource self.class.const_get(resource_name) end rescue NameError - if self.class.const_defined?(resource_name) - resource = self.class.const_get(resource_name) + const_args = RUBY_VERSION < "1.9" ? [resource_name] : [resource_name, false] + if self.class.const_defined?(*const_args) + resource = self.class.const_get(*const_args) else resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base)) end |