Skip to content

Commit 39bdd5b

Browse files
authored
DOCSP-45361: callbacks (#75)
* DOCSP-45361: callbacks * wip * wip * wip * NR PR fixes 1
1 parent 2c1e2ae commit 39bdd5b

File tree

7 files changed

+268
-7
lines changed

7 files changed

+268
-7
lines changed

snooty.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ server-manual = "Server manual"
3131
api-root = "https://www.mongodb.com/docs/mongoid/master/api/Mongoid"
3232
api = "https://www.mongodb.com/docs/mongoid/master/api"
3333
ruby-api = "https://www.mongodb.com/docs/ruby-driver/current/api/Mongo"
34+
active-record-docs = "https://guides.rubyonrails.org"

source/add-existing.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ for each Rails component, as shown in the following sample
130130
.. note::
131131

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

139139
Disable Active Record Adapters

source/data-modeling.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Model Your Data
2020
Persistence Configuration </data-modeling/persistence-configuration>
2121
Inheritance </data-modeling/inheritance>
2222
Document Validation </data-modeling/validation>
23+
Callbacks </data-modeling/callbacks>
2324
Data Associations </data-modeling/associations>
2425
Optimize Queries With Indexes </data-modeling/indexes>
2526

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

47+
- :ref:`mongoid-modeling-callbacks`: Learn how to implement callbacks to
48+
customize the life cycle of your models.
49+
4650
- :ref:`mongoid-associations`: Learn how to create and manage data
4751
associations in your model classes.
4852

source/data-modeling/callbacks.txt

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
.. _mongoid-modeling-callbacks:
2+
3+
=========
4+
Callbacks
5+
=========
6+
7+
.. facet::
8+
:name: genre
9+
:values: reference
10+
11+
.. meta::
12+
:keywords: ruby framework, odm, code example, life cycle
13+
14+
.. contents:: On this page
15+
:local:
16+
:backlinks: none
17+
:depth: 2
18+
:class: singlecol
19+
20+
Overview
21+
--------
22+
23+
In this guide, you can learn how to implement **callbacks** in your
24+
{+odm+} models to customize the life cycle of your model instances.
25+
26+
Callbacks are methods that {+odm+} triggers at specified moments of
27+
an object's life cycle. They allow you to initiate specified actions
28+
before or after changes to an object's state.
29+
30+
{+odm+} implements many of the callbacks from Active Record. To learn
31+
more, see `Callbacks
32+
<{+active-record-docs+}/active_record_callbacks.html>`__ in the
33+
Active Record documentation.
34+
35+
Supported Callbacks
36+
-------------------
37+
38+
Mongoid supports the following callbacks on model classes that implement
39+
the :ref:`Document <mongoid-modeling-documents>` module:
40+
41+
- ``after_initialize``
42+
- ``after_build``
43+
- ``before_validation``
44+
- ``after_validation``
45+
- ``before_create``
46+
- ``around_create``
47+
- ``after_create``
48+
- ``after_find``
49+
- ``before_update``
50+
- ``around_update``
51+
- ``after_update``
52+
- ``before_upsert``
53+
- ``around_upsert``
54+
- ``after_upsert``
55+
- ``before_save``
56+
- ``around_save``
57+
- ``after_save``
58+
- ``before_destroy``
59+
- ``around_destroy``
60+
- ``after_destroy``
61+
62+
To learn more about any of the preceding callback types, see the
63+
`ActiveRecord::Callbacks
64+
<https://api.rubyonrails.org/{+rails-8-version-docs+}/classes/ActiveRecord/Callbacks.html>`__
65+
reference in the Rails API documentation.
66+
67+
You can implement callbacks in both top-level and embedded document
68+
models.
69+
70+
.. note:: Callback Invocation Behavior
71+
72+
For efficiency, {+odm+} invokes the callback only on the document
73+
that you performed the persistence action on. This behavior enables
74+
{+odm+} to support large hierarchies and handle optimized atomic
75+
updates efficiently by not invoking callbacks throughout the document
76+
hierarchy.
77+
78+
Take precautions and ensure testability when implementing callbacks for
79+
domain logic, because these designs can lead to unexpected errors when
80+
callbacks in the chain halt execution. We recommend using callbacks for
81+
cross-cutting concerns outside of your program's core functionality,
82+
such as queueing up background jobs.
83+
84+
Document Callbacks
85+
------------------
86+
87+
You must implement and register callbacks on your model classes.
88+
You can register a callback by using ordinary methods, blocks and
89+
``Proc`` objects, or by defining custom callback objects that use
90+
classes or modules.
91+
92+
This example demonstrates how to register callbacks on the ``Contact``
93+
model class in the following ways:
94+
95+
- Includes the ``before_save`` class method, which triggers the
96+
``process_phone`` method before a ``Contact`` instance is saved to
97+
MongoDB. The ``process_phone`` method is defined separately in the class.
98+
99+
- Includes the ``after_destroy`` class method and uses a block to print a
100+
message when a ``Contact`` instance is deleted.
101+
102+
.. literalinclude:: /includes/data-modeling/callbacks.rb
103+
:start-after: start-doc-callback
104+
:end-before: end-doc-callback
105+
:language: ruby
106+
:emphasize-lines: 8, 11-13, 16-18
107+
:dedent:
108+
109+
The following code performs data operations that demonstrate the
110+
callback actions:
111+
112+
.. literalinclude:: /includes/data-modeling/callbacks.rb
113+
:start-after: start-doc-ops
114+
:end-before: end-doc-ops
115+
:language: ruby
116+
:dedent:
117+
118+
Because callback functionality comes from Active Support, you can
119+
alternatively use the ``set_callback`` class method syntax to register
120+
callbacks. The following code demonstrates how to use this syntax to
121+
create a callback that stores original values of the ``name`` field in
122+
the ``aliases`` array:
123+
124+
.. literalinclude:: /includes/data-modeling/callbacks.rb
125+
:start-after: start-doc-set-syntax
126+
:end-before: end-doc-set-syntax
127+
:language: ruby
128+
:emphasize-lines: 8-12
129+
:dedent:
130+
131+
Association Callbacks
132+
---------------------
133+
134+
{+odm+} provides the following association callbacks:
135+
136+
- ``after_add``
137+
- ``after_remove``
138+
- ``before_add``
139+
- ``before_remove``
140+
141+
If you register an association callback on your model class, it is
142+
invoked whenever you add or remove a document from any of the following
143+
associations:
144+
145+
- ``embeds_many``
146+
- ``has_many``
147+
- ``has_and_belongs_to_many``
148+
149+
Specify association callbacks as options on the respective association.
150+
You must pass the added or removed document as the parameter to the
151+
specified callback.
152+
153+
The following code demonstrates how to register an association callback
154+
on a ``User`` model class that embeds multiple ``SavedArticle``
155+
instances to limit the number of embedded documents for a single
156+
instance:
157+
158+
.. literalinclude:: /includes/data-modeling/callbacks.rb
159+
:start-after: start-association-callback
160+
:end-before: end-association-callback
161+
:language: ruby
162+
:emphasize-lines: 6, 10-15
163+
:dedent:
164+
165+
Additional Information
166+
----------------------
167+
168+
To learn how to prevent {+odm+} from running callbacks, see the
169+
following references in the Active Record documentation:
170+
171+
- `Skipping Callbacks <{+active-record-docs+}/active_record_callbacks.html#skipping-callbacks>`__
172+
- `Suppressing Saving <{+active-record-docs+}/active_record_callbacks.html#suppressing-saving>`__
173+
174+
To learn about how {+odm+} manages callbacks in transactions, see the
175+
:ref:`mongoid-data-txn` guide.
176+
177+
To learn how to access and change your MongoDB data, see the
178+
:ref:`mongoid-interact-data` guides.

source/data-modeling/validation.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ of document fields in your collections.
2929
{+odm+} includes ``ActiveModel::Validations`` from Active Record to
3030
provide validation functionality, including an associated and uniqueness
3131
validator. To learn more, see the `Active Record Validations
32-
<https://guides.rubyonrails.org/active_record_validations.html>`__
32+
<{+active-record-docs+}/active_record_validations.html>`__
3333
Rails guide and `ActiveModel::Validations
3434
<https://api.rubyonrails.org/classes/ActiveModel/Validations.html>`__
3535
Rails API documentation.
@@ -284,14 +284,14 @@ Custom Validation Rules
284284
You can use the ``validates_each`` and ``validates_with`` helpers to
285285
create custom validators. To learn more about these helpers and view
286286
examples, see the `validates_each
287-
<https://guides.rubyonrails.org/active_record_validations.html#validates-each>`__
287+
<{+active-record-docs+}/active_record_validations.html#validates-each>`__
288288
and `validates_with
289-
<https://guides.rubyonrails.org/active_record_validations.html#validates-with>`__
289+
<{+active-record-docs+}/active_record_validations.html#validates-with>`__
290290
references in the Active Record documentation.
291291

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

297297
Behavior
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# start-doc-callback
2+
class Contact
3+
include Mongoid::Document
4+
5+
field :name, type: String
6+
field :phone, type: String
7+
8+
# Creates a callback to clean phone numbers before saving
9+
before_save :process_phone
10+
11+
protected
12+
def process_phone
13+
self.phone = phone.gsub(/[^0-9]/, "") if attribute_present?("phone")
14+
end
15+
16+
# Creates a callback to send a message about object deletion
17+
after_destroy do
18+
p "deleted the contact for #{name}"
19+
end
20+
end
21+
# end-doc-callback
22+
23+
# start-doc-ops
24+
Contact.create(name: 'Serena Atherton', phone: '999 555-3030')
25+
# => `phone` field saved as '9995553030'
26+
Contact.create(name: 'Zayba Haq', phone: '999 123?5050')
27+
# => `phone` field saved as '9991235050'
28+
29+
Contact.first.destroy
30+
# => Console message: "deleted the contact for Serena Atherton"
31+
# end-doc-ops
32+
33+
# start-doc-set-syntax
34+
class Contact
35+
include Mongoid::Document
36+
37+
field :name, type: String
38+
field :phone, type: String
39+
field :aliases, type: Array, default: []
40+
41+
set_callback(:update, :before) do |document|
42+
if document.name_changed?
43+
document.push(aliases: document.name_was)
44+
end
45+
end
46+
end
47+
48+
Contact.create(name: 'Xavier Bloom', phone: '4447779999')
49+
Contact.first.update(name: 'Xav - coworker')
50+
# Saved document in MongoDB:
51+
# {"aliases":["Xavier Bloom"],"name":"Xav - coworker","phone":"4447779999"}
52+
# end-doc-set-syntax
53+
54+
# start-association-callback
55+
class User
56+
include Mongoid::Document
57+
58+
field :username, type: String
59+
# Registers the callback in the association statement
60+
embeds_many :saved_articles, before_add: :send_message
61+
62+
protected
63+
# Passes the association document as a parameter to the callback
64+
def send_message(saved_article)
65+
if saved_articles.count >= 10
66+
p "you can't save more than 10 articles at a time"
67+
throw(:abort)
68+
end
69+
end
70+
end
71+
72+
class SavedArticle
73+
include Mongoid::Document
74+
embedded_in :user
75+
76+
field :url, type: String
77+
end
78+
# end-association-callback

source/quick-start-rails.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ modeled and displayed. {+odm+} replaces the default Active Record
4545
adapter for data modeling in Rails.
4646

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

5151
MongoDB Atlas is a fully managed cloud database service that hosts your

0 commit comments

Comments
 (0)