我在一个以用户为父文档的应用程序中使用mongoid,几乎所有其他信息都嵌入在用户中。因此,例如,我对属于用户的#new
的控制器Relationship
动作如下:
def new
@relationship = current_user.relationships.new(friend_id: params[:fid])
@relationship.validate
end
因为我对将在视图中显示的关系进行验证,并且其中一些验证需要能够引用父级,所以我不能只调用@relationship = Relationship.new(friend_id: params[:fid])
,而是在实例中实例化了该关系。用户的关系数组,即使用户决定不想再建立新的关系,而现在又去了网站的另一部分,它现在仍然在那里闲逛。如果他们转到关系索引页面,除非我将其过滤掉,否则他们会在列表中看到它。
如果该关系有效,并且他们在其他地方进行了一些操作导致用户保存,则该虚拟关系现在是真实的。如果无效,保存将因未知原因而失败。
我打算在用户中嵌入许多模型,因此每个模型都会遇到这个问题。
我知道我可以打电话给current_user.reload
来清除垃圾,但是对我来说,每次我想这样做都必须打数据库是很荒谬的。验证后,我也可以孤立该关系,但这感觉很不容易。
在我看来,这是人们始终会遇到的嵌入式文档问题,因此我认为应该有某种内置的解决方案,但似乎找不到任何解决方案。我看到了this question,它与我的相似,但是我想要更可扩展的东西,这样我就不必把它放到任何地方。
我将要创建一个模块,为每个嵌入式关系的类添加一个clear_unsaved_#{relation}
方法,但是这个想法使我感到沮丧,所以我想看看是否有人对如何做有更好的想法它,以及最好在哪里调用它。
答案 0 :(得分:1)
我最终制作了一个覆盖Mongoid的embeds_many
和embeds_one
类方法的猴子补丁,还定义了一个实例方法来清除该关系的未保存文档。这对我来说是最直接的方法,因为它很少的代码,这意味着我不必记住将其包含在位置。
# config/initializers/patches/dirty_tracking_embedded.rb
module DirtyTrackingEmbedded
# override the embedding methods to also make dirty-tracking
def embeds_many(name, options= {}, &block)
define_method "clear_unsaved_#{name}" do
# remove_child removes it from the array without hitting the database
send(name).each {|o| remove_child(o) unless o.persisted?}
end
super
end
def embeds_one(name, options={}, &block)
define_method "clear_unsaved_#{name}" do
dirty = send(name)
remove_child(dirty) unless dirty.persisted?
end
super
end
end
module Mongoid
module Association
module Macros
module ClassMethods
prepend DirtyTrackingEmbedded
end
end
end
end
然后在我的控制器中,我求助于after_action
:
# app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
after_action :clear_unsaved, only: [:new]
def new
@relationship = current_user.relationships.new(friend_id: params[:fid])
@relationship.validate
end
private
def clear_unsaved
current_user.clear_unsaved_relationships
end
end
您可以猴子修补Mongoid::Association::Embedded::EmbedsMany
和Mongoid::Association::Embedded::EmbedsOne
中的setup_instance_methods!
方法,以包括设置一个实例方法来清除未保存的方法。通过查看Mongoid::Association::Accessors#self.define_ids_setter!
,您可以找到蒙古人如何做这类事情的示例。我建议像我使用的解决方案一样使用prepend
进行修补,以便您可以继承该方法的其余部分。
Mongoid选择用于实例化a constant called MACRO_MAPPING
in Mongoid::Association
的关联的类,因此您可以使从EmbedsMany
和EmbedsOne
继承而来的类仅覆盖setup_instance_methods!
来添加所需的实例方法,则只需猴子补丁MACRO_MAPPING
即可映射到新类。
如果您要进行反猴子修补,则可以使用我的DirtyTrackingEmbedded
模块中的代码来制作一个ActiveSupport::Concern
来执行相同的功能。您需要将重写的方法放在class_methods
块中,然后确保在将Mongoid::Document
包含在任何您要加入的模型类中之后,确保包含此模块。