这是(大致)我所做的。我将跳过大量细节,但如果有帮助,我很乐意分享更多。这可能是令人发指的,所以我很好奇别人的想法。
基本上,我有一个 Relationship 模型,类似于:
module ActsAsRelatingTo
class Relationship < ActiveRecord::Base
validates :owner_id, presence: true
validates :owner_type, presence: true
validates :in_relation_to_id, presence: true
validates :in_relation_to_type, presence: true
belongs_to :owner, polymorphic: true
belongs_to :in_relation_to, polymorphic: true
acts_as_taggable
acts_as_taggable_on :roles
end
end
然后,我创建了一个acts_as_relating_to 模块(同样,忽略了细节),其中包括以下内容:
module ActsAsRelatingTo
def acts_as_relating_to(*classes_array)
# re-opens the class at run time so I can do things like add
# new instance methods on-the-fly
class_eval do
before_destroy :tell_to_unrelate
has_many :owned_relationships,
as: :owner,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
has_many :referencing_relationships,
as: :in_relation_to,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
# iterates through arguments passed in 'acts_as_relating_to' call
# in the Person model, below.
classes_array.each do |class_sym|
# This is a method created on-the-fly. So, when I call
# (in the Person model, below) 'acts_as_relating_to :people',
# then I get a method on each instance of 'Person' called
# 'people_that_relate_to_me. You can also create class methods
# using `define_singleton_method`.
#
# The reason for doing all of this via the define_method
# is that it lets me create any number of 'things_i_relate_to'
# methods on any class descending from ActiveRecord::Base
# (more on this in a bit). Which, if I understand it
# correctly, is how a lot of the ActiveRecord functionality gets
# into a model in the first place.
define_method(class_sym.to_s + "_that_relate_to_me") do |options={}|
... some stuff
end
# Same as above, but know I'm defining a method called
# 'people_i_relate_to'
define_method(class_sym.to_s+"_i_relate_to") do |options={}|
... some more stuff
end
# I can also create static methods and incorporate them in the
# 'Person' class. I just define them in modules (such as
# ActsAsRelatingTo::InstanceMethods and ActsAsRelatingTo::ClassMethods)
# and then either 'include' (for instance methods) or 'extend'
# (for class methods) them.
include InstanceMethods
extend ClassMethods
end
end
end
end
# Here, I'm telling ActiveRecord::Base to 'extend' this module.
# That makes the 'acts_as_relating_to' method available in
# any class that descends from ActiveRecord::Base.
ActiveRecord::Base.extend ActsAsRelatingTo
然后,我可以这样做:
class Person < ActiveRecord::Base
# Here, I am calling the method that I defined above, passing in
# :people, :organizations, and :programs. This is exactly the
# sort of thing you do all the time when you say something like
# 'has_one :foo', or 'belongs_to :bar'.
acts_as_relating_to :people, :organizations, :programs
# Here, I am calling a method I have that builds on acts_as_relating_to,
# but which I did not show, that creates administrative methods on
# the person so that I can say stuff like 'person.administrate organization'.
# Or, 'organization.administrators'.
acts_as_administering :organizations, :programs
...
end
所以,如果我有person_1 和person_2,并且我有一个关系记录,其中owner 是person_1,in_relation_to 是person_2,那么我可以说person_1.people_i_relate_to 并得到返回person_2。或者,我可以说,person_2.people_that_relate_to_me 并返回 person_1。
我还开发了一个基于 acts_as_relating_to 模块的 acts_as_administering 模块,让我可以执行 person_1.administered_organizations 和 person_1.administered_programs 之类的操作。
我可能已经喋喋不休太久了。无论如何,如果它有趣,我可以说更多。
干杯!