module Og # An 'active' collection that reflects a relation. # A collection stores entitities that participate in # a relation. class Collection # The owner of this collection. attr_accessor :owner # The members of this collection. Keeps the objects # that belong to this collection. attr_accessor :members # The class of the members of this collection. attr_accessor :member_class # A method used to add insert objects in the collection. attr_accessor :insert_proc # A method used to remove objects from the collection. attr_accessor :remove_proc # A method used to find the objects that belong to the # collection. attr_accessor :find_proc # A method used to count the objects that belong to the # collection. attr_accessor :count_proc # The default find options. attr_accessor :find_options # Is the collection in build mode? attr_accessor :building # Is the collection loaded? attr_accessor :loaded # Initialize the collection. def initialize(owner = nil, member_class = nil, insert_proc = nil, remove_proc = nil, find_proc = nil, count_proc = nil, find_options = {}) @owner = owner @member_class = member_class @insert_proc = insert_proc @remove_proc = remove_proc @find_proc = find_proc @count_proc = count_proc @find_options = find_options @members = [] @loaded = false @building = false end # Load the members of the collection. def load_members unless @loaded or @owner.unsaved? @members = @owner.send(@find_proc, @find_options) @loaded = true end @members end # Reload the collection. def reload(options = {}) # gmosx, NOOO: this was a bug! it corrupts the default options. # @find_options = options @members = @owner.send(@find_proc, options) end # Unload the members (clear the cache). def unload @members.clear @loaded = false end # Convert the collection to an array. def to_ary load_members @members end # Defined to avoid the method missing overhead. def each(&block) load_members @members.each(&block) end # Defined to avoid the method missing overhead. def [](idx) load_members @members[idx] end # Add a new member to the collection. # this method will overwrite any objects already # existing in the collection def push(obj, options = nil) remove(obj) if members.include?(obj) @members.push(obj) unless @building or owner.unsaved? @owner.send(@insert_proc, obj, options) end end alias_method :<<, :push alias_method :add, :push # Remove a member from the collection, the actual object # is not deleted. #-- # TODO: add remove by oid! #++ def remove(*objects) objects = objects.flatten objects.reject! { |obj| @members.delete(obj) if obj.unsaved? } return if objects.empty? @owner.transaction do objects.each do |obj| @owner.send(@remove_proc, obj) @members.delete(obj) end end end # Delete a member from the collection AND the store. #-- # TODO: add delete by oid! #++ def delete(*objects) objects = objects.flatten objects.reject! { |obj| @members.delete(obj) if obj.unsaved? } return if objects.empty? @owner.transaction do objects.each do |obj| obj.delete @members.delete(obj) end end end # Delete a member from the collection AND the store, if the # condition block evaluates to true. def delete_if(&block) objects = @members.select(&block) objects.reject! { |obj| @members.delete(obj) if obj.unsaved? } return if objects.empty? @owner.transaction do objects.each do |obj| obj.delete @members.delete(obj) end end end # Remove all members from the collection. def remove_all @owner.transaction do self.each { |obj| @owner.send(@remove_proc, obj) } end @members.clear @loaded = false # gmosx: IS this needed? end alias_method :clear, :remove_all # Delete all members of the collection. Also delete from the # store. def delete_all @owner.transaction do self.each { |obj| obj.delete } end @members.clear end # Return the size of a collection. def size(reload = false) if @loaded and !reload return @members.size else return @owner.send(@count_proc) end end alias_method :count, :size # Allows to perform a scoped query. def find(options = {}) @member_class.with_scope(options) do return @owner.send(@find_proc, @find_options) end end # Find one object. def find_one(options = {}) find(options).first end # Redirect all other methods to the members array. def method_missing(symbol, *args, &block) load_members @members.send(symbol, *args, &block) end private def check_type(obj) end end end # * George Moschovitis # * Julien Perrot