Rails覆盖关系

时间:2017-06-01 08:21:19

标签: ruby activerecord

我想在我的关系中覆盖<< 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。

我只是好奇它是如何完成的。喜欢,出于学习目的。

除了目标只是覆盖特定关系的<<方法,因此可能有一些理由说明为什么有人想要这样做。

2 个答案:

答案 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