lib/recurly/resource.rb in recurly-2.1.12 vs lib/recurly/resource.rb in recurly-2.2.0
- old
+ new
@@ -1,7 +1,6 @@
require 'date'
-require 'erb'
module Recurly
# The base class for all Recurly resources (e.g. {Account}, {Subscription},
# {Transaction}).
#
@@ -183,11 +182,10 @@
# @param uuid [String, nil]
# @example
# Recurly::Account.member_path "code" # => "accounts/code"
# Recurly::Account.member_path nil # => "accounts"
def member_path uuid
- uuid = ERB::Util.url_encode(uuid) if uuid
[collection_path, uuid].compact.join '/'
end
# @return [Array] Per attribute, defines readers, writers, boolean and
# change-tracking methods.
@@ -319,12 +317,13 @@
if uuid.nil?
# Should we raise an ArgumentError, instead?
raise NotFound, "can't find a record with nil identifier"
end
+ uri = uuid =~ /^http/ ? uuid : member_path(uuid)
begin
- from_response API.get(member_path(uuid), {}, options)
+ from_response API.get(uri, {}, options)
rescue API::NotFound => e
raise NotFound, e.description
end
end
@@ -392,35 +391,31 @@
record.instance_variable_set "@#{name}", value.to_s
end
xml.each_element do |el|
if el.name == 'a'
- name, uri = el.attribute('name').value, el.attribute('href').value
- record[name] = case el.attribute('method').to_s
- when 'get', '' then proc { |*opts| API.get uri, {}, *opts }
- when 'post' then proc { |*opts| API.post uri, nil, *opts }
- when 'put' then proc { |*opts| API.put uri, nil, *opts }
- when 'delete' then proc { |*opts| API.delete uri, *opts }
- end
+ record.links[el.attribute('name').value] = {
+ :method => el.attribute('method').to_s,
+ :href => el.attribute('href').value
+ }
next
end
if el.children.empty? && href = el.attribute('href')
resource_class = Recurly.const_get(
Helper.classify(el.attribute('type') || el.name), false
)
- record[el.name] = case el.name
+ case el.name
when *associations[:has_many]
- Pager.new resource_class, :uri => href.value, :parent => record
+ record[el.name] = Pager.new(
+ resource_class, :uri => href.value, :parent => record
+ )
when *(associations[:has_one] + associations[:belongs_to])
- lambda {
- begin
- relation = resource_class.from_response API.get(href.value)
- relation.attributes[member_name] = record
- relation
- rescue Recurly::API::NotFound
- end
+ record.links[el.name] = {
+ :resource_class => resource_class,
+ :method => :get,
+ :href => href.value
}
end
else
record[el.name] = XML.cast el
end
@@ -550,26 +545,19 @@
@attributes, @new_record, @destroyed, @uri, @href = {}, true, false
self.attributes = attributes
yield self if block_given?
end
- def to_param
- self[self.class.param_name]
- end
-
# @return [self] Reloads the record from the server.
def reload response = nil
if response
return if response.body.to_s.length.zero?
fresh = self.class.from_response response
else
- options = {:etag => (etag unless changed?)}
- fresh = if @href
- self.class.from_response API.get(@href, {}, options)
- else
- self.class.find(to_param, options)
- end
+ fresh = self.class.find(
+ @href || to_param, :etag => (etag unless changed?)
+ )
end
fresh and copy_from fresh
persist! true
self
rescue API::NotModified
@@ -639,13 +627,15 @@
# @example
# account.read_attribute :first_name # => "Ted"
# account[:last_name] # => "Beneke"
# @see #write_attribute
def read_attribute key
- value = attributes[key = key.to_s]
- if value.respond_to?(:call) && self.class.reflect_on_association(key)
- value = attributes[key] = value.call
+ key = key.to_s
+ if attributes.key? key
+ value = attributes[key]
+ elsif links.key?(key) && self.class.reflect_on_association(key)
+ value = attributes[key] = follow_link key
end
value
end
alias [] read_attribute
@@ -683,10 +673,43 @@
attributes.each_pair { |k, v|
respond_to?(name = "#{k}=") and send(name, v) or self[k] = v
}
end
+ # @return [Hash] The raw hash of record href links.
+ def links
+ @links ||= {}
+ end
+
+ # Whether a record has a link with the given name.
+ #
+ # @param key [Symbol, String] The name of the link to check for.
+ # @example
+ # account.link? :billing_info # => true
+ def link? key
+ links.key?(key.to_s)
+ end
+
+ # Fetch the value of a link by following the associated href.
+ #
+ # @param key [Symbol, String] The name of the link to be followed.
+ # @param options [Hash] A hash of API options.
+ # @example
+ # account.read_link :billing_info # => <Recurly::BillingInfo>
+ def follow_link key, options = {}
+ if link = links[key = key.to_s]
+ response = API.send link[:method], link[:href], nil, options
+ if resource_class = link[:resource_class]
+ response = resource_class.from_response response
+ response.attributes[self.class.member_name] = self
+ end
+ response
+ end
+ rescue Recurly::API::NotFound
+ raise unless resource_class
+ end
+
# Serializes the record to XML.
#
# @return [String] An XML string.
# @param options [Hash] A hash of XML options.
# @example
@@ -767,13 +790,14 @@
# account.valid? # => false
# account.account_code = 'account_code'
# account.save # => true
# account.valid? # => true
def valid?
- return true if persisted? && changed_attributes.empty?
- return if errors.empty? && changed_attributes?
- errors.empty?
+ return true if persisted? && !changed?
+ errors_empty = errors.values.flatten.empty?
+ return if errors_empty && changed?
+ errors_empty
end
# Update a record with a given hash of attributes.
#
# @return [true, false] The success of the update.
@@ -806,11 +830,11 @@
# an array of error messages.
# @example
# account.errors # => {"account_code"=>["can't be blank"]}
# account.errors[:account_code] # => ["can't be blank"]
def errors
- @errors ||= Errors.new
+ @errors ||= Errors.new { |h, k| h[k] = [] }
end
# Marks a record as persisted, i.e. not a new or deleted record, resetting
# any tracked attribute changes in the process. (This is an internal method
# and should probably not be called unless you know what you're doing.)
@@ -870,12 +894,13 @@
@destroyed,
@uri,
@href,
changed_attributes,
previous_changes,
+ response,
etag,
- response
+ links
]
end
def marshal_load serialization
@attributes,
@@ -884,11 +909,12 @@
@uri,
@href,
@changed_attributes,
@previous_changes,
@response,
- @etag = serialization
+ @etag,
+ @links = serialization
end
# @return [String]
def inspect attributes = self.class.attribute_names.to_a
string = "#<#{self.class}"
@@ -911,15 +937,15 @@
end
end
def invalid! attribute_path, error
if attribute_path.length == 1
- (errors[attribute_path[0]] ||= []) << error
+ errors[attribute_path[0]] << error
else
child, k, v = attribute_path.shift.scan(/[^\[\]=]+/)
if c = k ? self[child].find { |d| d[k] == v } : self[child]
c.invalid! attribute_path, error
- (e = errors[child] ||= []) << 'is invalid' and e.uniq!
+ e = errors[child] << 'is invalid' and e.uniq!
end
end
end
def clear_errors