2
2
3
3
module Mongoid
4
4
module Touchable
5
-
6
5
module InstanceMethods
7
6
7
+ # Suppresses the invocation of touch callbacks, for the class that
8
+ # includes this module, for the duration of the block.
9
+ #
10
+ # @example Suppress touch callbacks on Person documents:
11
+ # person.suppress_touch_callbacks { ... }
12
+ #
13
+ # @api private
14
+ def suppress_touch_callbacks
15
+ Touchable . suppress_touch_callbacks ( self . class . name ) { yield }
16
+ end
17
+
18
+ # Queries whether touch callbacks are being suppressed for the class
19
+ # that includes this module.
20
+ #
21
+ # @return [ true | false ] Whether touch callbacks are suppressed.
22
+ #
23
+ # @api private
24
+ def touch_callbacks_suppressed?
25
+ Touchable . touch_callbacks_suppressed? ( self . class . name )
26
+ end
27
+
8
28
# Touch the document, in effect updating its updated_at timestamp and
9
29
# optionally the provided field to the current time. If any belongs_to
10
30
# associations exist with a touch option, they will be updated as well.
@@ -45,7 +65,10 @@ def touch(field = nil)
45
65
#
46
66
# @api private
47
67
def _gather_touch_updates ( now , field = nil )
68
+ return if touch_callbacks_suppressed?
69
+
48
70
field = database_field_name ( field )
71
+
49
72
write_attribute ( :updated_at , now ) if respond_to? ( "updated_at=" )
50
73
write_attribute ( field , now ) if field
51
74
@@ -71,6 +94,7 @@ def _clear_touch_updates(field = nil)
71
94
#
72
95
# @api private
73
96
def _run_touch_callbacks_from_root
97
+ return if touch_callbacks_suppressed?
74
98
_parent . _run_touch_callbacks_from_root if _touchable_parent?
75
99
run_callbacks ( :touch )
76
100
end
@@ -131,8 +155,41 @@ def define_touchable!(association)
131
155
end
132
156
end
133
157
158
+ # Suppresses touch callbacks for the named class, for the duration of
159
+ # the associated block.
160
+ #
161
+ # @api private
162
+ def suppress_touch_callbacks ( name )
163
+ save , touch_callback_statuses [ name ] = touch_callback_statuses [ name ] , true
164
+ yield
165
+ ensure
166
+ touch_callback_statuses [ name ] = save
167
+ end
168
+
169
+ # Queries whether touch callbacks are being suppressed for the named
170
+ # class.
171
+ #
172
+ # @return [ true | false ] Whether touch callbacks are suppressed.
173
+ #
174
+ # @api private
175
+ def touch_callbacks_suppressed? ( name )
176
+ touch_callback_statuses [ name ]
177
+ end
178
+
134
179
private
135
180
181
+ # The key to use to store the active touch callback suppression statuses
182
+ SUPPRESS_TOUCH_CALLBACKS_KEY = "[mongoid]:suppress-touch-callbacks"
183
+
184
+ # Returns a hash to be used to store and query the various touch callback
185
+ # suppression statuses for different classes.
186
+ #
187
+ # @return [ Hash ] The hash that contains touch callback suppression
188
+ # statuses
189
+ def touch_callback_statuses
190
+ Thread . current [ SUPPRESS_TOUCH_CALLBACKS_KEY ] ||= { }
191
+ end
192
+
136
193
# Define the method that will get called for touching belongs_to
137
194
# associations.
138
195
#
@@ -153,12 +210,11 @@ def define_relation_touch_method(name, association)
153
210
[ association . relation_class ]
154
211
end
155
212
156
- relation_classes . each { |c | c . send ( :include , InstanceMethods ) }
157
213
method_name = "touch_#{ name } _after_create_or_destroy"
158
214
association . inverse_class . class_eval do
159
215
define_method ( method_name ) do
160
216
without_autobuild do
161
- if relation = __send__ ( name )
217
+ if ! touch_callbacks_suppressed? && relation = __send__ ( name )
162
218
# This looks up touch_field at runtime, rather than at method definition time.
163
219
# If touch_field is nil, it will only touch the default field (updated_at).
164
220
relation . touch ( association . touch_field )
0 commit comments