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