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