2017-06-01 38 views
-1

我想覆盖我的关系中的<< setter。例如,给定:Rails覆盖关系中的活动记录集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 

,但我只是想这样做,当我在阅读器调用<<

我想知道是否有知道我是否在一组用户关系调用<<的方式,或者我是否有权访问组对象的时候,我就调用用户组<<方法通过关系。

我想这样做,因为它看起来不错。最简单的方法是定义单独的方法来设置读者(并且更加明确),但是我想知道在activerecord或ruby中是否有可能。

编辑:

是的,我知道,压倒一切的核心方法是坏事,人们去地狱的是,亚达内容十分重要。

我只是好奇它是如何完成的。就像为了学习的目的。

除了目的只是重写<<方法在那个特定的关系,所以很可能有人可能会有一些理由为什么有人可能会这样做。

+0

在你的'AR'版本中'someLibrary.readers.class'是什么? – mudasobwa

+0

恕我直言,覆盖Rails方法是一个可怕的想法。你为什么不添加一个名称反映它真正做的新方法?你试图通过重写Rails核心方法来实现什么? – spickermann

回答

1

强制性免责声明:

不建议您这样做,在 '重要' 的代码。改变这种方法的行为会混淆其他开发人员(以及未来的自我),并导致各种意想不到的行为改变!

但假设这是“玩票” ......

基于上述信息,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 

如果您正在寻找然而,这样做的更好的方法,我建议使用服务对象设计模式。然后,您可以在一个干净而孤立的抽象中封装与创建/更新/删除用户,库等有关的任何更复杂/定制的逻辑。

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 
+0

这是一个很好的代码改进,但它实际上并没有回答这个问题?... –