Ruby on rails 如何在关系类上定义after_add方法?

Ruby on rails 如何在关系类上定义after_add方法?,ruby-on-rails,ruby,ruby-on-rails-4,associations,rails-activerecord,Ruby On Rails,Ruby,Ruby On Rails 4,Associations,Rails Activerecord,我有一个Character类和一个Siblingship类 为了表示一个字符是另一个字符的“同级”,我使用字符id(正在编辑的“主要”字符)和同级id(标记为主要字符同级的字符)保存同级实例 我想添加自动创建第二个具有反向ID的Siblingship实例的功能(所以,若我将Alice标记为Bob的兄弟,我也将Bob标记为Alice的兄弟) 如果我将逻辑放在Character类中,那么在添加后这将是一个简单的: class Character < ActiveRecord::Base h

我有一个
Character
类和一个
Siblingship

为了表示一个字符是另一个字符的“同级”,我使用
字符id
(正在编辑的“主要”字符)和
同级id
(标记为主要字符同级的字符)保存同级实例

我想添加自动创建第二个具有反向ID的Siblingship实例的功能(所以,若我将Alice标记为Bob的兄弟,我也将Bob标记为Alice的兄弟)

如果我将逻辑放在Character类中,那么在添加后这将是一个简单的

class Character < ActiveRecord::Base
  has_many :siblingships
  has_many :siblings, through: :siblingships, after_add: :reciprocate

  def reciprocate(sibling)
    ...
  end
end
有什么好办法吗?我还尝试指定
reciprogate
作为实例方法,如

has_many :siblings, through: :siblingships, after_add: Siblingship.new.reciprocate
但它不仅会在方法上出错[参数数量错误(给定0,预期为1)],而且在这里实例化同级关系只是为了获得一个实例方法也是错误的


我非常感谢任何关于如何解决这个问题的解决方案,同时保持Character类的干净,最好保持该关系类中每个关系的逻辑

这似乎可以通过连接模型上的简单挂钩(例如
同级关系
)来解决,而不是尝试将它们添加到它们所连接的相关类(例如
字符

这段代码是从我在问题中给出的具体示例中抽象出来的,因为我需要一个解决方案,该解决方案能够以最少的代码重复跨无限的连接类工作。以下是我最后为实现双向链接(创建和删除)所做的工作:

类字符
然后,对于每个加入类:

class Siblingship < ActiveRecord::Base
  include SmartContentLinking
  LINK_TYPE = :two_way

  belongs_to :character
  belongs_to :sibling, class_name: 'Character'

  # Since this is a two-way relation, also create an opposite relation
  after_create do
    self.reciprocate relation: :siblingships, parent_object_ref: :character, added_object_ref: :sibling
  end

  # Since this is a two-way relation, also delete any opposite relation
  after_destroy do
    this_object  = Character.find_by(id: self.character_id)
    other_object = Character.find_by(id: self.sibling_id)

    other_object.siblings.delete this_object
  end
end
class Siblinghood
  def initialize(sibling_one, sibling_two)
    @sibling_one, @sibling_two = sibling_one, sibling_two
  end

  def form
    @sibling_one.siblings << @sibling_two unless @sibling_one.siblings.include?(@sibling_two)
    @sibling_two.siblings << @sibling_one unless @sibling_two.siblings.include?(@sibling_one)
  end
end
class-Siblingship
显然,为了使代码更清晰(并完全抽象到SmartContentLinking关注点),仍有一些事情需要做,但目前这是可行的

SmartContentLinking关注点:

require 'active_support/concern'
module SmartContentLinking
  extend ActiveSupport::Concern

  # Default linking to one-way. All possible values:
  #  - :one_way
  #  - :two_way
  LINK_TYPE = :one_way

  included do
    def reciprocate relation:, parent_object_ref:, added_object_ref:
      parent_object = self.send(parent_object_ref)
      added_object  = self.send(added_object_ref)

      # if some_character.siblingships.pluck(:sibling_id).include?(parent_object.id)
      if added_object.send(relation).pluck("#{added_object_ref}_id").include?(parent_object.id)
        # Two-way relation already exists
      else
        # If a two-way relation doesn't already exist, create it
        added_object.send(relation) << relation.to_s.singularize.camelize.constantize.create({
          "#{parent_object_ref}": added_object,   # character: sibling
          "#{added_object_ref}":  parent_object   # sibling:   character
        })
      end
    end
  end
end
需要“主动支持/关注”
模块SmartContentLinking
扩展ActiveSupport::关注点
#默认链接为单向。所有可能的值:
#-:单向
#-:双向
链接类型=:单向
包括做
定义交互关系:,父对象\参考:,添加\对象\参考:
父对象=self.send(父对象\u ref)
添加的\u对象=self.send(添加的\u对象\u ref)
#如果某个字符.siblingships.pull(:sibling\u id).include?(parent\u object.id)
如果添加了_object.send(relation).pull(“#{added_object_ref}}_id”).include?(parent_object.id)
#双向关系已经存在
其他的
#如果双向关系不存在,请创建它

添加了_object.send(relation)看起来这可以通过连接模型上的简单挂钩(例如
Siblingship
)来解决,而不是尝试将它们添加到它们所连接的相关类中(例如
Character

这段代码是从我在问题中给出的具体示例中抽象出来的,因为我需要一个解决方案,该解决方案能够以最少的代码重复跨无限的连接类工作。以下是我最后为实现双向链接(创建和删除)所做的工作:

类字符
然后,对于每个加入类:

class Siblingship < ActiveRecord::Base
  include SmartContentLinking
  LINK_TYPE = :two_way

  belongs_to :character
  belongs_to :sibling, class_name: 'Character'

  # Since this is a two-way relation, also create an opposite relation
  after_create do
    self.reciprocate relation: :siblingships, parent_object_ref: :character, added_object_ref: :sibling
  end

  # Since this is a two-way relation, also delete any opposite relation
  after_destroy do
    this_object  = Character.find_by(id: self.character_id)
    other_object = Character.find_by(id: self.sibling_id)

    other_object.siblings.delete this_object
  end
end
class Siblinghood
  def initialize(sibling_one, sibling_two)
    @sibling_one, @sibling_two = sibling_one, sibling_two
  end

  def form
    @sibling_one.siblings << @sibling_two unless @sibling_one.siblings.include?(@sibling_two)
    @sibling_two.siblings << @sibling_one unless @sibling_two.siblings.include?(@sibling_one)
  end
end
class-Siblingship
显然,为了使代码更清晰(并完全抽象到SmartContentLinking关注点),仍有一些事情需要做,但目前这是可行的

SmartContentLinking关注点:

require 'active_support/concern'
module SmartContentLinking
  extend ActiveSupport::Concern

  # Default linking to one-way. All possible values:
  #  - :one_way
  #  - :two_way
  LINK_TYPE = :one_way

  included do
    def reciprocate relation:, parent_object_ref:, added_object_ref:
      parent_object = self.send(parent_object_ref)
      added_object  = self.send(added_object_ref)

      # if some_character.siblingships.pluck(:sibling_id).include?(parent_object.id)
      if added_object.send(relation).pluck("#{added_object_ref}_id").include?(parent_object.id)
        # Two-way relation already exists
      else
        # If a two-way relation doesn't already exist, create it
        added_object.send(relation) << relation.to_s.singularize.camelize.constantize.create({
          "#{parent_object_ref}": added_object,   # character: sibling
          "#{added_object_ref}":  parent_object   # sibling:   character
        })
      end
    end
  end
end
需要“主动支持/关注”
模块SmartContentLinking
扩展ActiveSupport::关注点
#默认链接为单向。所有可能的值:
#-:单向
#-:双向
链接类型=:单向
包括做
定义交互关系:,父对象\参考:,添加\对象\参考:
父对象=self.send(父对象\u ref)
添加的\u对象=self.send(添加的\u对象\u ref)
#如果某个字符.siblingships.pull(:sibling\u id).include?(parent\u object.id)
如果添加了_object.send(relation).pull(“#{added_object_ref}}_id”).include?(parent_object.id)
#双向关系已经存在
其他的
#如果双向关系不存在,请创建它

添加了_object.send(relation)让我们忽略ActiveRecord,看看您想要实现什么

当角色添加了新的同级角色时,该同级角色的现有 兄弟姐妹关系需要重新调整

您提到,这是一个有大约100个类似关系的项目。因此,我们可以将上述情况概括为:

当一个新的关系
@character.siblings.add(@other_character)
Siblinghood.new(@character, @other_character).form 
Siblinghood.new(@character, @other_character).destroy
class Character
  def add_sibling(other_character)
    Siblinghood.new(self, other_character).form
  end

  def remove_sibling(other_character)
    Siblinghood.new(self, other_character).destroy
  end
end