Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions snooty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ server-manual = "Server manual"
api-root = "https://www.mongodb.com/docs/mongoid/master/api/Mongoid"
api = "https://www.mongodb.com/docs/mongoid/master/api"
ruby-api = "https://www.mongodb.com/docs/ruby-driver/current/api/Mongo"
active-record-docs = "https://guides.rubyonrails.org"
4 changes: 2 additions & 2 deletions source/add-existing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ for each Rails component, as shown in the following sample
.. note::

Because they rely on Active Record, the `ActionText
<https://guides.rubyonrails.org/action_text_overview.html>`__,
<{+active-record-docs+}/action_text_overview.html>`__,
`ActiveStorage <https://edgeguides.rubyonrails.org/active_storage_overview.html>`__, and
`ActionMailbox
<https://guides.rubyonrails.org/action_mailbox_basics.html>`__
<{+active-record-docs+}/action_mailbox_basics.html>`__
adapters cannot be used alongside {+odm+}.

Disable Active Record Adapters
Expand Down
4 changes: 4 additions & 0 deletions source/data-modeling.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Model Your Data
Persistence Configuration </data-modeling/persistence-configuration>
Inheritance </data-modeling/inheritance>
Document Validation </data-modeling/validation>
Callbacks </data-modeling/callbacks>
Data Associations </data-modeling/associations>
Optimize Queries With Indexes </data-modeling/indexes>

Expand All @@ -43,6 +44,9 @@ In this section, you can learn how to model data in {+odm+}.
- :ref:`mongoid-modeling-validation`: Learn how to create document
validation rules for your model classes.

- :ref:`mongoid-modeling-callbacks`: Learn how to implement callbacks to
customize the life cycle of your models.

- :ref:`mongoid-associations`: Learn how to create and manage data
associations in your model classes.

Expand Down
178 changes: 178 additions & 0 deletions source/data-modeling/callbacks.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
.. _mongoid-modeling-callbacks:

=========
Callbacks
=========

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: ruby framework, odm, code example, life cycle

.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:class: singlecol

Overview
--------

In this guide, you can learn how to implement **callbacks** in your
{+odm+} models to customize the life cycle of your model instances.

Callbacks are methods that {+odm+} triggers at specified moments of
an object's life cycle. They allow you to initiate specified actions
before or after changes to an object's state.

{+odm+} implements many of the callbacks from Active Record. To learn
more, see `Callbacks
<{+active-record-docs+}/active_record_callbacks.html>`__ in the
Active Record documentation.

Supported Callbacks
-------------------

Mongoid supports the following callbacks on model classes that implement
the :ref:`Document <mongoid-modeling-documents>` module:

- ``after_initialize``
- ``after_build``
- ``before_validation``
- ``after_validation``
- ``before_create``
- ``around_create``
- ``after_create``
- ``after_find``
- ``before_update``
- ``around_update``
- ``after_update``
- ``before_upsert``
- ``around_upsert``
- ``after_upsert``
- ``before_save``
- ``around_save``
- ``after_save``
- ``before_destroy``

Check failure on line 58 in source/data-modeling/callbacks.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/callbacks.txt", "range": {"start": {"line": 58, "column": 13}}}, "severity": "ERROR"}
- ``around_destroy``

Check failure on line 59 in source/data-modeling/callbacks.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/callbacks.txt", "range": {"start": {"line": 59, "column": 13}}}, "severity": "ERROR"}
- ``after_destroy``

Check failure on line 60 in source/data-modeling/callbacks.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/callbacks.txt", "range": {"start": {"line": 60, "column": 12}}}, "severity": "ERROR"}

To learn more about any of the preceding callback types, see the
`ActiveRecord::Callbacks
<https://api.rubyonrails.org/{+rails-8-version-docs+}/classes/ActiveRecord/Callbacks.html>`__
reference in the Rails API documentation.

You can implement callbacks in both top-level and embedded document
models.

.. note:: Callback Invocation Behavior

For efficiency, {+odm+} invokes the callback only on the document
that you performed the persistence action on. This behavior enables
{+odm+} to support large hierarchies and handle optimized atomic
updates efficiently by not invoking callbacks throughout the document
hierarchy.

Take precautions and ensure testability when implementing callbacks for
domain logic, because these designs can lead to unexpected errors when
callbacks in the chain halt execution. We recommend using callbacks for
cross-cutting concerns outside of your program's core functionality,
such as queueing up background jobs.

Document Callbacks
------------------

You must implement and register callbacks on your model classes.
You can register a callback by using ordinary methods, blocks and
``Proc`` objects, or by defining custom callback objects that use
classes or modules.

This example demonstrates how to register callbacks on the ``Contact``
model class in the following ways:

- Includes the ``before_save`` class method, which triggers the
``process_phone`` method before a ``Contact`` instance is saved to
MongoDB. The ``process_phone`` method is defined separately in the class.

- Includes the ``after_destroy`` class method and uses a block to print a

Check failure on line 99 in source/data-modeling/callbacks.txt

View workflow job for this annotation

GitHub Actions / TDBX Vale rules

[vale] reported by reviewdog 🐶 [MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'. Raw Output: {"message": "[MongoDB.NegativeWords] Use 'remove, delete' instead of the negative word 'destroy'.", "location": {"path": "source/data-modeling/callbacks.txt", "range": {"start": {"line": 99, "column": 24}}}, "severity": "ERROR"}
message when a ``Contact`` instance is deleted.

.. literalinclude:: /includes/data-modeling/callbacks.rb
:start-after: start-doc-callback
:end-before: end-doc-callback
:language: ruby
:emphasize-lines: 8, 11-13, 16-18
:dedent:

The following code performs data operations that demonstrate the
callback actions:

.. literalinclude:: /includes/data-modeling/callbacks.rb
:start-after: start-doc-ops
:end-before: end-doc-ops
:language: ruby
:dedent:

Because callback functionality comes from Active Support, you can
alternatively use the ``set_callback`` class method syntax to register
callbacks. The following code demonstrates how to use this syntax to
create a callback that stores original values of the ``name`` field in
the ``aliases`` array:

.. literalinclude:: /includes/data-modeling/callbacks.rb
:start-after: start-doc-set-syntax
:end-before: end-doc-set-syntax
:language: ruby
:emphasize-lines: 8-12
:dedent:

Association Callbacks
---------------------

{+odm+} provides the following association callbacks:

- ``after_add``
- ``after_remove``
- ``before_add``
- ``before_remove``

If you register an association callback on your model class, it is
invoked whenever you add or remove a document from any of the following
associations:

- ``embeds_many``
- ``has_many``
- ``has_and_belongs_to_many``

Specify association callbacks as options on the respective association.
You must pass the added or removed document as the parameter to the
specified callback.

The following code demonstrates how to register an association callback
on a ``User`` model class that embeds multiple ``SavedArticle``
instances to limit the number of embedded documents for a single
instance:

.. literalinclude:: /includes/data-modeling/callbacks.rb
:start-after: start-association-callback
:end-before: end-association-callback
:language: ruby
:emphasize-lines: 6, 10-15
:dedent:

Additional Information
----------------------

To learn how to prevent {+odm+} from running callbacks, see the
following references in the Active Record documentation:

- `Skipping Callbacks <{+active-record-docs+}/active_record_callbacks.html#skipping-callbacks>`__
- `Suppressing Saving <{+active-record-docs+}/active_record_callbacks.html#suppressing-saving>`__

To learn about how {+odm+} manages callbacks in transactions, see the
:ref:`mongoid-data-txn` guide.

To learn how to access and change your MongoDB data, see the
:ref:`mongoid-interact-data` guides.
8 changes: 4 additions & 4 deletions source/data-modeling/validation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ of document fields in your collections.
{+odm+} includes ``ActiveModel::Validations`` from Active Record to
provide validation functionality, including an associated and uniqueness
validator. To learn more, see the `Active Record Validations
<https://guides.rubyonrails.org/active_record_validations.html>`__
<{+active-record-docs+}/active_record_validations.html>`__
Rails guide and `ActiveModel::Validations
<https://api.rubyonrails.org/classes/ActiveModel/Validations.html>`__
Rails API documentation.
Expand Down Expand Up @@ -284,14 +284,14 @@ Custom Validation Rules
You can use the ``validates_each`` and ``validates_with`` helpers to
create custom validators. To learn more about these helpers and view
examples, see the `validates_each
<https://guides.rubyonrails.org/active_record_validations.html#validates-each>`__
<{+active-record-docs+}/active_record_validations.html#validates-each>`__
and `validates_with
<https://guides.rubyonrails.org/active_record_validations.html#validates-with>`__
<{+active-record-docs+}/active_record_validations.html#validates-with>`__
references in the Active Record documentation.

To learn more about custom validators, see `Performing Custom
Validations
<https://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations>`__
<{+active-record-docs+}/active_record_validations.html#performing-custom-validations>`__
in the Active Record documentation.

Behavior
Expand Down
78 changes: 78 additions & 0 deletions source/includes/data-modeling/callbacks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# start-doc-callback
class Contact
include Mongoid::Document

field :name, type: String
field :phone, type: String

# Creates a callback to clean phone numbers before saving
before_save :process_phone

protected
def process_phone
self.phone = phone.gsub(/[^0-9]/, "") if attribute_present?("phone")
end

# Creates a callback to send a message about object deletion
after_destroy do
p "deleted the contact for #{name}"
end
end
# end-doc-callback

# start-doc-ops
Contact.create(name: 'Serena Atherton', phone: '999 555-3030')
# => `phone` field saved as '9995553030'
Contact.create(name: 'Zayba Haq', phone: '999 123?5050')
# => `phone` field saved as '9991235050'

Contact.first.destroy
# => Console message: "deleted the contact for Serena Atherton"
# end-doc-ops

# start-doc-set-syntax
class Contact
include Mongoid::Document

field :name, type: String
field :phone, type: String
field :aliases, type: Array, default: []

set_callback(:update, :before) do |document|
if document.name_changed?
document.push(aliases: document.name_was)
end
end
end

Contact.create(name: 'Xavier Bloom', phone: '4447779999')
Contact.first.update(name: 'Xav - coworker')
# Saved document in MongoDB:
# {"aliases":["Xavier Bloom"],"name":"Xav - coworker","phone":"4447779999"}
# end-doc-set-syntax

# start-association-callback
class User
include Mongoid::Document

field :username, type: String
# Registers the callback in the association statement
embeds_many :saved_articles, before_add: :send_message

protected
# Passes the association document as a parameter to the callback
def send_message(saved_article)
if saved_articles.count >= 10
p "you can't save more than 10 articles at a time"
throw(:abort)
end
end
end

class SavedArticle
include Mongoid::Document
embedded_in :user

field :url, type: String
end
# end-association-callback
2 changes: 1 addition & 1 deletion source/quick-start-rails.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ modeled and displayed. {+odm+} replaces the default Active Record
adapter for data modeling in Rails.

To learn more about Ruby on Rails, see the `Getting Started
with Rails <https://guides.rubyonrails.org/getting_started.html>`__
with Rails <{+active-record-docs+}/getting_started.html>`__
guide in the Rails documentation.

MongoDB Atlas is a fully managed cloud database service that hosts your
Expand Down
Loading