module ActiveObject
module Associations
def self.included(base)
base.send :include, HasOneAssociation
base.send :include, HasManyAssociation
end
module HasOneAssociation
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# has_one用于指明两个对象之间的关联。
#
# 一旦使用has_one,下列方法将被添加:
#
# [association]
# 返回关联对象. 如果没有找到则返回+nil+。
# [association=(associate)]
# 对关联对象赋值。
# [association.nil?]
# 如果没有关联对象,则返回+true+。
#
# (用第一个参数替换+association+,例如:
# has_one :manager 将添加 manager.nil?.)
#
# === 示例
#
# Account类定义了has_one :beneficiary,将添加:
# * Account#beneficiary
# * Account#beneficiary=(beneficiary)
# * Account#beneficiary.nil?
#
# === 选项
#
# 通过hash选项指定关联的行为
#
# 选项:
# [:class_name]
# 指定关联的类名。默认情况类名是有关联推断出来的,如果无法推断则需要显性指定。
#
# 选项示例:
# has_one :icon,:class_name=>"Photo"
#
def has_one(association, options = {})
class_name = options.delete(:class_name) || association.to_s.camelize
write_inheritable_attribute "#{association}_class",class_name # 保存关联的类名
attr_accessor "#{association}"
merge_attributes "#{association}_id" # 将关联id作为持久化属性
define_method("#{association}_id") do
self.instance_variable_get("@#{association}") ? self.instance_variable_get("@#{association}").id : nil
end
define_method("#{association}_id=") do |value|
self.instance_variable_set("@#{association}",self.class.read_inheritable_attribute("#{association}_class").constantize.find(value))
end
end
end
end
module HasManyAssociation
def self.included(base)
base.extend ClassMethods
end
class Collection
def initialize()
@objects = []
end
# 遍历关联对象集合
def each
@objects.each do |object|
yield object
end
end
def each_with_index
@objects.each_with_index do |object,index|
yield object,index
end
end
# 添加一个关联对象
def append(object)
@objects << object
end
# 插入一个关联对象
def insert(index,object)
@objects.insert(index,object)
end
# 删除一个关联对象
def delete(object)
@objects.delete(object)
end
def size
@objects.size
end
def clear
@objects.clear
end
def empty?
@objects.empty?
end
def object_ids
@objects.collect{|object| object.id}
end
def objects
@objects
end
end
module ClassMethods
# has_many用于指明两个对象之间的关联。
#
# 一旦使用has_many,下列方法将被添加:
# [associations.each]
# 遍历对象集合
# [associations]
# 返回关联的对象集合. 如果没有找到则返回空数组。
# [associations.append(object)]
# 添加一个关联对象。
# [associations.insert(object,index)]
# 插入一个关联对象。
# [associations.delete(object)]
# 删除一个关联对象。
# [associations.clear]
# 清除所有关联对象。
# [associations.size]
# 关联对象的数量。
# [association.empty?]
# 如果没有关联对象,则返回+true+。
#
# (用第一个参数替换+associations+,例如:
# has_many :managers 将添加 managers.empty?.)
#
# === 示例
#
# Account类定义了has_many :friends,将添加:
# * Account#friends
# * Account#friends.append(user)
# * Account#friends.delete(user)
# * Account#friends.clear
# * Account#friends.size
# * Account#friends.empty?
#
# === 选项
#
# 通过hash选项指定关联的行为
#
# 选项:
# [:class_name]
# 指定关联的类名。默认情况类名是有关联推断出来的,如果无法推断则需要显性指定。
#
# 选项示例:
# has_many :friends,:class_name=>"User"
#
def has_many(associations, options = {})
class_name = options.delete(:class_name) || associations.to_s.singularize.camelize
write_inheritable_attribute "#{associations}_class",class_name # 保存关联的类名
merge_attributes "#{associations}_ids" # 将关联ids作为持久化属性
define_method("#{associations}_ids") do
self.instance_variable_get("@#{associations}") ? self.instance_variable_get("@#{associations}").object_ids : []
end
define_method("#{associations}_ids=") do |value|
self.instance_variable_set("@#{associations}",Collection.new)
value.each do |object_id|
self.instance_variable_get("@#{associations}").append(self.class.read_inheritable_attribute("#{associations}_class").constantize.find(object_id))
end
end
define_method("#{associations}") do
self.instance_variable_set("@#{associations}",Collection.new) unless self.instance_variable_get("@#{associations}")
self.instance_variable_get("@#{associations}")
end
end
end
end
end
end