lib/recurly/resource.rb in recurly-2.3.11 vs lib/recurly/resource.rb in recurly-2.4.0
- old
+ new
@@ -1,7 +1,8 @@
require 'date'
require 'erb'
+require 'recurly/resource/association'
module Recurly
# The base class for all Recurly resources (e.g. {Account}, {Subscription},
# {Transaction}).
#
@@ -319,12 +320,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
@@ -401,18 +403,19 @@
next
end
if el.children.empty? && href = el.attribute('href')
resource_class = Recurly.const_get(
- Helper.classify(el.attribute('type') || el.name), false
+ Helper.classify(association_class_name(el.name) ||
+ el.attribute('type') || el.name), false
)
case el.name
- when *associations[:has_many]
+ when *associations_for_relation(:has_many)
record[el.name] = Pager.new(
resource_class, :uri => href.value, :parent => record
)
- when *(associations[:has_one] + associations[:belongs_to])
+ when *(associations_for_relation(:has_one) + associations_for_relation(:belongs_to))
record.links[el.name] = {
:resource_class => resource_class,
:method => :get,
:href => href.value
}
@@ -429,29 +432,49 @@
record.persist! if record.respond_to? :persist!
record
end
- # @return [Hash] A list of association names for the current class.
+ # @return [Array] A list of associations for the current class.
def associations
- @associations ||= {
- :has_many => [], :has_one => [], :belongs_to => []
- }
+ @associations ||= []
end
+ # @return [Array] A list of associated resource classes with
+ # the relation [:has_many, :has_one, :belongs_to] for the current class.
+ def associations_for_relation(relation)
+ associations.select{ |a| a.relation == relation }.map(&:resource_class)
+ end
+
+ # @return [String, nil] The actual associated resource class name
+ # for the current class if the resource class does not match the
+ # actual class.
+ def association_class_name(resource_class)
+ association = find_association(resource_class)
+ association.class_name if association
+ end
+
+ # @return [Association, nil] Find association for the current class
+ # with resource class name.
+ def find_association(resource_class)
+ associations.find{ |a| a.resource_class == resource_class }
+ end
+
def associations_helper
@associations_helper ||= Module.new.tap { |helper| include helper }
end
# Establishes a has_many association.
#
# @return [Proc, nil]
# @param collection_name [Symbol] Association name.
# @param options [Hash] A hash of association options.
# @option options [true, false] :readonly Don't define a setter.
+ # [String] :class_name Actual associated resource class name
+ # if not same as collection_name.
def has_many collection_name, options = {}
- associations[:has_many] << collection_name.to_s
+ associations << Association.new(:has_many, collection_name.to_s, options)
associations_helper.module_eval do
define_method collection_name do
self[collection_name] ||= []
end
if options.key?(:readonly) && options[:readonly] == false
@@ -466,12 +489,14 @@
#
# @return [Proc, nil]
# @param member_name [Symbol] Association name.
# @param options [Hash] A hash of association options.
# @option options [true, false] :readonly Don't define a setter.
+ # [String] :class_name Actual associated resource class name
+ # if not same as member_name.
def has_one member_name, options = {}
- associations[:has_one] << member_name.to_s
+ associations << Association.new(:has_one, member_name.to_s, options)
associations_helper.module_eval do
define_method(member_name) { self[member_name] }
if options.key?(:readonly) && options[:readonly] == false
associated = Recurly.const_get Helper.classify(member_name), false
define_method "#{member_name}=" do |member|
@@ -499,12 +524,17 @@
end
# Establishes a belongs_to association.
#
# @return [Proc]
+ # @param parent_name [Symbol] Association name.
+ # @param options [Hash] A hash of association options.
+ # @option options [true, false] :readonly Don't define a setter.
+ # [String] :class_name Actual associated resource class name
+ # if not same as parent_name.
def belongs_to parent_name, options = {}
- associations[:belongs_to] << parent_name.to_s
+ associations << Association.new(:belongs_to, parent_name.to_s, options)
associations_helper.module_eval do
define_method(parent_name) { self[parent_name] }
if options.key?(:readonly) && options[:readonly] == false
define_method "#{parent_name}=" do |parent|
self[parent_name] = parent
@@ -513,11 +543,12 @@
end
end
# @return [:has_many, :has_one, :belongs_to, nil] An association type.
def reflect_on_association name
- a = associations.find { |k, v| v.include? name.to_s } and a.first
+ a = find_association name.to_s
+ a.relation if a
end
def embedded! root_index = false
private :initialize
private_class_method(*%w(new create create!))
@@ -557,16 +588,13 @@
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
@@ -661,11 +689,11 @@
changed_attributes.delete key if changed_attributes[key] == value
elsif self[key] != value
changed_attributes[key] = self[key]
end
- if self.class.associations.values.flatten.include? key
+ if self.class.find_association key
value = fetch_association key, value
# FIXME: More explicit; less magic.
elsif value && key.end_with?('_in_cents') && !respond_to?(:currency)
value = Money.new value, self, key unless value.is_a? Money
end
@@ -958,15 +986,13 @@
end
end
def clear_errors
errors.clear
- self.class.associations.each_value do |associations|
- associations.each do |association|
- next unless respond_to? "#{association}=" # Clear writable only.
- [*self[association]].each do |associated|
- associated.clear_errors if associated.respond_to? :clear_errors
- end
+ self.class.associations do |association|
+ next unless respond_to? "#{association}=" # Clear writable only.
+ [*self[association]].each do |associated|
+ associated.clear_errors if associated.respond_to? :clear_errors
end
end
end
def copy_from other