我想在我的关系中覆盖<<
setter。例如,给定:
class Library < ActiveRecord::Base
has_many :groups
def readers
groups.find_by(name: 'readers').users
end
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, through: :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :groups, through :group_membership
end
我想做点什么
someLibrary.readers << user1
在此之后还会发生一些其他事情。
代码应该类似于:
def <<(objects)
super objects
#do other things here
end
应该在哪里?我想在Group
,如:
class Group
...
def users<<(objects)
super objects
#do stuff
end
end
但我只是想在读者调用<<
时这样做。
我想知道是否有办法知道我是否在群组用户关系上调用<<
,或者在我调用{{1}时是否可以访问群组对象通过关系对组用户的方法。
我想这样做,因为它看起来不错。最简单的方法是定义单独的方法来设置读者(并且更明确),但我想知道它是否可以在activerecord或ruby中使用。
编辑:
是的,我知道最重要的核心方法是坏事,人们为此而下地狱,yada yada yada。
我只是好奇它是如何完成的。喜欢,出于学习目的。
除了目标只是覆盖特定关系的<<
方法,因此可能有一些理由说明为什么有人想要这样做。
答案 0 :(得分:1)
强制性免责声明:
我不建议你这样做,重要的是&#39;码。改变这样的方法的行为会让其他开发人员(以及你未来的自我)感到困惑,并导致各种意想不到的行为改变!
但假设这只是为了好玩而......
根据上述信息,someLibrary.readers
会返回User
条记录的集合。所以我们需要做的就是在该类中添加所需的行为。
通常你可以通过以下两种方式之一定义一个类方法来做到这一点:
class User
def self.foo
puts 'this works!'
end
class << self
def bar
puts 'this works too!'
end
end
end
完成上述操作后,您可以调用以下方法:
someLibrary.readers.foo
someLibrary.readers.bar
......然而,这里有一些黑魔法在引擎盖下。 someLibrary.readers
实际上是User::ActiveRecord_Associations_CollectionProxy
的一个实例,上述方法正在被动态选取并附加到ActiveRecord::Associations::CollectionProxy
。
由于此动态方法定义,无法以这种方式覆盖现有的Rails方法(例如<<
)。相反,我们需要直接修补User::ActiveRecord_Associations_CollectionProxy
类:
class User
class ActiveRecord_Associations_CollectionProxy
def <<(objects)
super(objects)
# do stuff
end
end
end
如果您正在寻找更好的方法,我建议您使用服务对象设计模式。然后,您可以在一个干净且孤立的抽象中封装与创建/更新/删除用户,库等相关的任何更复杂/自定义逻辑。
答案 1 :(得分:0)
更成熟的方法......
class Library < ActiveRecord::Base
has_many :groups
has_one :reader_group -> {groups.find_by(name: 'readers')}
has_many :readers, through: :reader_group, class_name: 'User', foreign_key: 'user_id'
end
就是这样。你现在可以做
my_library.readers << another_user