require 'data_mapper/associations/reference' require 'data_mapper/associations/has_many_association' require 'data_mapper/associations/belongs_to_association' require 'data_mapper/associations/has_and_belongs_to_many_association' module DataMapper module Associations # Extends +base+ with methods for setting up associations between different models. def self.included(base) base.extend(ClassMethods) end module ClassMethods # Adds the following methods for query of a single associated object: # * collection( - returns a set containing the associated objects. Returns # an empty set if no objects are found. # * collection << object - adds an object to the collection. # * collection = [objects] - replaces the collections content by deleting and # adding objects as appropriate. # * collection.empty? - returns +true+ if there is no associated objects. # * collection.size - returns the number of associated objects. # # Options are: # * :class - specify the class name of the association. So has_many :animals will by # default be linked to the Animal class, but if you want the association to use a # different class, you'll have to specify it with this option. DM also lets you specify # this with :class_name, for AR compability. # * :foreign_key - specify the foreign key used for the association. By default # this is guessed to be the name of this class in lower-case and _id suffixed. # * :dependent - if set to :destroy, the associated objects have their destroy! methods # called in a chain meaning all callbacks are also called for each object. # if set to :delete, the associated objects are deleted from the database # without their callbacks being triggered. # if set to :protect and the collection is not empty an AssociatedProtectedError will be raised. # if set to :nullify, the associated objects foreign key is set to NULL. # default is :nullify # # Option examples: # has_many :favourite_fruits, :class => 'Fruit', :dependent => :destroy def has_many(association_name, options = {}) database.schema[self].associations << HasManyAssociation.new(self, association_name, options) end # Adds the following methods for query of a single associated object: # * association( - returns the associated object. Returns an empty set if no # object is found. # * association=(associate) - assigns the associate object, extracts the # primary key, and sets it as the foreign key. # * association.nil? - returns +true+ if there is no associated object. # # The declaration can also include an options hash to specialize the behavior of the # association. # # Options are: # * :class - specify the class name of the association. So has_one :animal will by # default be linked to the Animal class, but if you want the association to use a # different class, you'll have to specify it with this option. DM also lets you specify # this with :class_name, for AR compability. # * :foreign_key - specify the foreign key used for the association. By default # this is guessed to be the name of this class in lower-case and _id suffixed. # * :dependent - has_one is secretly a has_many so this option performs the same # as the has_many # # Option examples: # has_one :favourite_fruit, :class => 'Fruit', :foreign_key => 'devourer_id' def has_one(association_name, options = {}) database.schema[self].associations << HasManyAssociation.new(self, association_name, options) end # Adds the following methods for query of a single associated object: # * association( - returns the associated object. Returns an empty set if no # object is found. # * association=(associate) - assigns the associate object, extracts the # primary key, and sets it as the foreign key. # * association.nil? - returns +true+ if there is no associated object. # * build_association - builds a new object of the associated type, without # saving it to the database. # * create_association - creates and saves a new object of the associated type. def belongs_to(association_name, options = {}) database.schema[self].associations << BelongsToAssociation.new(self, association_name, options) end # Associates two classes via an intermediate join table. # # Options are: # * :dependent - if set to :destroy, the associated objects have their destroy! methods # called in a chain meaning all callbacks are also called for each object. Beware that this # is a cascading delete and will affect all records that have a remote relationship with the # record being destroyed! # if set to :delete, the associated objects are deleted from the database without their # callbacks being triggered. This does NOT cascade the deletes. All associated objects will # have their relationships removed from other records before being deleted. The record calling # destroy will only delete those records directly associated to it. # if set to :protect and the collection is not empty an AssociatedProtectedError will be raised. # if set to :nullify, the join table will have the relationship records removed which is # effectively nullifying the foreign key. # default is :nullify def has_and_belongs_to_many(association_name, options = {}) database.schema[self].associations << HasAndBelongsToManyAssociation.new(self, association_name, options) end end end end