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