@@ -677,6 +677,154 @@ When you globally set ``join_contexts`` to ``true``, you can use the
677677``join_context: false`` option on an ``atomically`` block to run
678678operations at the end of the block for that block only.
679679
680+ Dirty Tracking
681+ --------------
682+
683+ You can track changed ("dirty") fields by using a {+odm+} API similar to
684+ the one available in Active Model. If you modify a defined field in a
685+ model, {+odm+} marks the model as dirty and allows you to perform
686+ special actions. The following sections describe how you can interact
687+ with dirty models.
688+
689+ View Changes
690+ ~~~~~~~~~~~~
691+
692+ {+odm+} records changes from the time a model is instantiated, either as
693+ a new document or by retrieving one from the database, until the time it is
694+ saved. Any persistence operation clears the changes.
695+
696+ {+odm+} creates model-specific methods that allow you to explore the
697+ changes to a model instance. The following code demonstrates ways that
698+ you can view changes on your model instance:
699+
700+ .. literalinclude:: /includes/interact-data/crud.rb
701+ :language: ruby
702+ :start-after: start-dirty-tracking-view
703+ :end-before: end-dirty-tracking-view
704+
705+ .. note:: Tracking Changes to Associations
706+
707+ Setting the associations on a document does not modify the
708+ ``changes`` or ``changed_attributes`` hashes. This is true for all
709+ types of associations. However, changing the
710+ ``_id`` field on referenced associations causes the changes to
711+ show up in the ``changes`` and the ``changed_attributes`` hashes.
712+
713+ Reset Changes
714+ ~~~~~~~~~~~~~
715+
716+ You can reset a changed field to its previous value by calling the
717+ ``reset`` method, as shown in the following code:
718+
719+ .. literalinclude:: /includes/interact-data/crud.rb
720+ :language: ruby
721+ :start-after: start-dirty-tracking-reset
722+ :end-before: end-dirty-tracking-reset
723+
724+ Persistence
725+ ~~~~~~~~~~~
726+
727+ {+odm+} uses dirty tracking as the basis of all persistence operations.
728+ It evaluates the changes on a document and atomically updates only what
729+ has changed, compared to other frameworks that write the entire document on
730+ each save. If you don't make any changes, {+odm+} does not access the
731+ database when you call ``Model#save``.
732+
733+ View Previous Changes
734+ ~~~~~~~~~~~~~~~~~~~~~
735+
736+ After you persist a model to MongoDB, {+odm+} clears the current
737+ changes. However, you can still see what changes were made previously by
738+ calling the ``previous_changes`` method, as shown in the following
739+ code:
740+
741+ .. literalinclude:: /includes/interact-data/crud.rb
742+ :language: ruby
743+ :start-after: start-dirty-tracking-prev
744+ :end-before: end-dirty-tracking-prev
745+
746+ Update Container Fields
747+ -----------------------
748+
749+ {+odm+} currently has an issue that prevents changes to attributes of
750+ container types, such as ``Set`` or ``Array``, from saving to MongoDB.
751+ You must assign all fields, including container types, for their values
752+ to save to MongoDB.
753+
754+ For example, adding an item to a ``Set`` instance as shown in the
755+ following code *does not* persist changes to MongoDB:
756+
757+ .. code-block:: ruby
758+
759+ person = Person.new
760+ person.interests
761+ # => #<Set: {}>
762+
763+ person.interests << 'Hiking'
764+ # => #<Set: {"Hiking"}>
765+ person.interests
766+ # => #<Set: {}> # Change does not take effect
767+
768+ To persist this change, you must modify the field value *outside* of the
769+ model and assign it back to the model as shown in the following code:
770+
771+ .. literalinclude:: /includes/interact-data/crud.rb
772+ :language: ruby
773+ :start-after: start-container-save
774+ :end-before: end-container-save
775+
776+ Read-only Documents
777+ -------------------
778+
779+ You can mark documents as read-only in the following ways, depending on
780+ the value of the ``Mongoid.legacy_readonly`` feature flag:
781+
782+ - If this flag is turned *off*, you can mark a document as
783+ read-only by calling the ``readonly!`` method on that document. The
784+ resulting read-only document raises a ``ReadonlyDocument`` error if
785+ you attempt to perform any persistence operation, including, but not
786+ limited to, saving, updating, deleting, and destroying. Note that
787+ reloading *does not* reset the read-only state.
788+
789+ .. code-block:: ruby
790+
791+ person = Person.first
792+ person.readonly? # => false
793+ person.readonly! # Sets the document as read-only
794+ person.readonly? # => true
795+ person.name = "Larissa Shay" # Changes the document
796+ person.save # => raises ReadonlyDocument error
797+ person.reload.readonly? # => true
798+
799+ - If this flag is turned ``on``, you can mark a document as read-only
800+ after you project that document by using methods such as ``only`` or
801+ ``without``. As a result, you can't delete or destroy the read-only
802+ document because {+odm+} raises a ``ReadonlyDocument`` error, but you can
803+ save and update it. The read-only status *is reset* if you reload the
804+ document.
805+
806+ .. code-block:: ruby
807+
808+ person = Person.only(:name).first
809+ person.readonly? # => true
810+ person.destroy # => raises ReadonlyDocument error
811+ person.reload.readonly? # => false
812+
813+ .. tip:: Projection
814+
815+ To learn more about projections, see the
816+ :ref:`mongoid-data-projection` section of the Modify Query
817+ Results guide.
818+
819+ You can also make a document read-only by overriding the ``readonly?``
820+ method, as shown in the following code:
821+
822+ .. literalinclude:: /includes/interact-data/crud.rb
823+ :language: ruby
824+ :start-after: start-override-readonly
825+ :end-before: end-override-readonly
826+ :emphasize-lines: 5-7
827+
680828Additional Information
681829----------------------
682830
0 commit comments