aboutsummaryrefslogtreecommitdiffstats
path: root/activeresource/lib/active_resource/base.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activeresource/lib/active_resource/base.rb')
-rw-r--r--activeresource/lib/active_resource/base.rb104
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