lib/zermelo/associations/class_methods.rb in zermelo-1.1.0 vs lib/zermelo/associations/class_methods.rb in zermelo-1.2.0

- old
+ new

@@ -1,14 +1,12 @@ require 'zermelo/associations/association_data' require 'zermelo/associations/index_data' -require 'zermelo/associations/belongs_to' -require 'zermelo/associations/has_and_belongs_to_many' -require 'zermelo/associations/has_many' -require 'zermelo/associations/has_one' -require 'zermelo/associations/has_sorted_set' +require 'zermelo/associations/singular' +require 'zermelo/associations/multiple' require 'zermelo/associations/index' +require 'zermelo/associations/range_index' require 'zermelo/associations/unique_index' # NB: this module gets mixed in to Zermelo::Record as class methods # TODO update other side of associations without having to load the record (?) @@ -28,44 +26,54 @@ index(::Zermelo::Associations::Index, arg, :type => att_types[arg]) end nil end + def range_index_by(*args) + att_types = attribute_types + args.each do |arg| + index(::Zermelo::Associations::RangeIndex, arg, :type => att_types[arg]) + end + nil + end + def unique_index_by(*args) att_types = attribute_types args.each do |arg| index(::Zermelo::Associations::UniqueIndex, arg, :type => att_types[arg]) end nil end def has_many(name, args = {}) - associate(::Zermelo::Associations::HasMany, name, args) + associate(::Zermelo::Associations::Multiple, :has_many, name, args) nil end - def has_one(name, args = {}) - associate(::Zermelo::Associations::HasOne, name, args) + def has_sorted_set(name, args = {}) + associate(::Zermelo::Associations::Multiple, :has_sorted_set, name, args) nil end - def has_sorted_set(name, args = {}) - associate(::Zermelo::Associations::HasSortedSet, name, args) + def has_and_belongs_to_many(name, args = {}) + associate(::Zermelo::Associations::Multiple, :has_and_belongs_to_many, name, args) nil end - def has_and_belongs_to_many(name, args = {}) - associate(::Zermelo::Associations::HasAndBelongsToMany, name, args) + def has_one(name, args = {}) + associate(::Zermelo::Associations::Singular, :has_one, name, args) nil end def belongs_to(name, args = {}) - associate(::Zermelo::Associations::BelongsTo, name, args) + associate(::Zermelo::Associations::Singular, :belongs_to, name, args) nil end # end used by client classes + private + # used internally by other parts of Zermelo to implement the above # configuration # Works out which classes should be locked when updating associations # TODO work out if this can be replaced by 'related_klasses' assoc data @@ -75,19 +83,16 @@ @lock.synchronize do @association_data ||= {} @association_data.values.each do |data| klass = data.data_klass next if visited.include?(klass) - visited |= klass.associated_classes(visited, false) + visited |= klass.send(:associated_classes, visited, false) end end visited end - # TODO for each association: check whether it has changed - # would need an instance-level hash with association name as key, - # boolean 'changed' value def with_associations(record) @lock.synchronize do @association_data ||= {} @association_data.keys.each do |name| yield record.send("#{name}_proxy".to_sym) @@ -110,18 +115,10 @@ yield idx_data unless idx_data.nil? end end # end used internally within Zermelo - # # TODO can remove need for some of the inverse mapping - # # was inverse_of(source, klass) - # with_association_data do |d| - # d.detect {|name, data| data.klass == klass && data.inverse == source} - # end - - private - def add_index_data(klass, name, args = {}) return if name.nil? data = Zermelo::Associations::IndexData.new( :name => name, @@ -149,74 +146,64 @@ end } instance_eval idx, __FILE__, __LINE__ end - def add_association_data(klass, name, args = {}) - - # TODO have inverse be a reference (or copy?) of the association data - # record for that inverse association; would need to defer lookup until - # all data in place for all assocs, so might be best if looked up and - # cached on first use + def add_association_data(klass, type, name, args = {}) inverse = if args[:inverse_of].nil? || args[:inverse_of].to_s.empty? nil else args[:inverse_of].to_s end callbacks = case klass.name - when ::Zermelo::Associations::HasMany.name, - ::Zermelo::Associations::HasSortedSet.name, - ::Zermelo::Associations::HasAndBelongsToMany.name - [:before_add, :after_add, :before_remove, :after_remove] - when ::Zermelo::Associations::HasOne.name, - ::Zermelo::Associations::BelongsTo.name - [:before_set, :after_set, :before_clear, :after_clear] + when ::Zermelo::Associations::Multiple.name + [:before_add, :after_add, :before_remove, :after_remove, :before_read, :after_read] + when ::Zermelo::Associations::Singular.name + [:before_set, :after_set, :before_clear, :after_clear, :before_read, :after_read] else [] end data = Zermelo::Associations::AssociationData.new( :name => name, :data_klass_name => args[:class_name], + :data_type => type, :type_klass => klass, :inverse => inverse, :related_klass_names => args[:related_class_names], :callbacks => callbacks.each_with_object({}) {|c, memo| memo[c] = args[c] } ) - if klass.name == Zermelo::Associations::HasSortedSet.name - data.sort_key = (args[:key] || :id) + if :has_sorted_set.eql?(type) + data.sort_key = args[:key] + data.sort_order = + !args[:order].nil? && :desc.eql?(args[:order].to_sym) ? :desc : :asc end @lock.synchronize do @association_data ||= {} @association_data[name] = data end end - def associate(klass, name, args = {}) + def associate(klass, type, name, args = {}) return if name.nil? - add_association_data(klass, name, args) + add_association_data(klass, type, name, args) assoc = case klass.name - when ::Zermelo::Associations::HasMany.name, - ::Zermelo::Associations::HasSortedSet.name, - ::Zermelo::Associations::HasAndBelongsToMany.name - + when ::Zermelo::Associations::Multiple.name %Q{ def #{name} #{name}_proxy end } - when ::Zermelo::Associations::HasOne.name, - ::Zermelo::Associations::BelongsTo.name - + when ::Zermelo::Associations::Singular.name %Q{ def #{name} #{name}_proxy.value end @@ -230,10 +217,10 @@ proxy = %Q{ def #{name}_proxy raise "Associations cannot be invoked for records without an id" if self.id.nil? - @#{name}_proxy ||= #{klass.name}.new(self, '#{name}') + @#{name}_proxy ||= #{klass.name}.new(:#{type}, self.class, self.id, '#{name}') end private :#{name}_proxy } class_eval proxy, __FILE__, __LINE__ class_eval assoc, __FILE__, __LINE__ \ No newline at end of file