lib/mongoid/association/referenced/auto_save.rb
# frozen_string_literal: true
# rubocop:todo all
module Mongoid
module Association
module Referenced
# Mixin module included into Mongoid::Document which adds
# the ability to automatically save opposite-side documents
# in referenced associations when saving the subject document.
module AutoSave
extend ActiveSupport::Concern
# Used to prevent infinite loops in associated autosaves.
#
# @example Is the document autosaved?
# document.autosaved?
#
# @return [ true | false ] Has the document already been autosaved?
def autosaved?
Threaded.autosaved?(self)
end
# Begin the associated autosave.
#
# @example Begin autosave.
# document.__autosaving__
def __autosaving__
Threaded.begin_autosave(self)
yield
ensure
Threaded.exit_autosave(self)
end
# Check if there is changes for auto-saving
#
# @example Return true if there is changes on self or in
# autosaved associations.
# document.changed_for_autosave?
def changed_for_autosave?(doc)
doc.new_record? || doc.changed? || doc.marked_for_destruction?
end
# Define the autosave method on an association's owning class for
# an associated object.
#
# @example Define the autosave method:
# Association::Referenced::Autosave.define_autosave!(association)
#
# @param [ Mongoid::Association::Relatable ] association The association for which autosaving is enabled.
#
# @return [ Class ] The association's owner class.
def self.define_autosave!(association)
association.inverse_class.tap do |klass|
save_method = :"autosave_documents_for_#{association.name}"
klass.send(:define_method, save_method) do
if before_callback_halted?
self.before_callback_halted = false
else
__autosaving__ do
if assoc_value = ivar(association.name)
Array(assoc_value).each do |doc|
pc = doc.persistence_context? ? doc.persistence_context : persistence_context.for_child(doc)
doc.with(pc) do |d|
d.save
end
end
end
end
end
end
klass.after_persist_parent save_method, unless: :autosaved?
end
end
end
end
end
end