1
+ # encoding: utf-8
1
2
module Mongoid
3
+
4
+ # Observer classes respond to life cycle callbacks to implement trigger-like
5
+ # behavior outside the original class. This is a great way to reduce the
6
+ # clutter that normally comes when the model class is burdened with
7
+ # functionality that doesn't pertain to the core responsibility of the
8
+ # class. Mongoid's observers work similar to ActiveRecord's. Example:
9
+ #
10
+ # class CommentObserver < Mongoid::Observer
11
+ # def after_save(comment)
12
+ # Notifications.comment(
13
+ # "[email protected] ", "New comment was posted", comment
14
+ # ).deliver
15
+ # end
16
+ # end
17
+ #
18
+ # This Observer sends an email when a Comment#save is finished.
19
+ #
20
+ # class ContactObserver < Mongoid::Observer
21
+ # def after_create(contact)
22
+ # contact.logger.info('New contact added!')
23
+ # end
24
+ #
25
+ # def after_destroy(contact)
26
+ # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
27
+ # end
28
+ # end
29
+ #
30
+ # This Observer uses logger to log when specific callbacks are triggered.
31
+ #
32
+ # == Observing a class that can't be inferred
33
+ #
34
+ # Observers will by default be mapped to the class with which they share a
35
+ # name. So CommentObserver will be tied to observing Comment,
36
+ # ProductManagerObserver to ProductManager, and so on. If you want to
37
+ # name your observer differently than the class you're interested in
38
+ # observing, you can use the Observer.observe class method which takes
39
+ # either the concrete class (Product) or a symbol for that class (:product):
40
+ #
41
+ # class AuditObserver < Mongoid::Observer
42
+ # observe :account
43
+ #
44
+ # def after_update(account)
45
+ # AuditTrail.new(account, "UPDATED")
46
+ # end
47
+ # end
48
+ #
49
+ # If the audit observer needs to watch more than one kind of object,
50
+ # this can be specified with multiple arguments:
51
+ #
52
+ # class AuditObserver < Mongoid::Observer
53
+ # observe :account, :balance
54
+ #
55
+ # def after_update(record)
56
+ # AuditTrail.new(record, "UPDATED")
57
+ # end
58
+ # end
59
+ #
60
+ # The AuditObserver will now act on both updates to Account and Balance
61
+ # by treating them both as records.
62
+ #
63
+ # == Available callback methods
64
+ #
65
+ # * after_initialize
66
+ # * before_validation
67
+ # * after_validation
68
+ # * before_create
69
+ # * around_create
70
+ # * after_create
71
+ # * before_update
72
+ # * around_update
73
+ # * after_update
74
+ # * before_upsert
75
+ # * around_upsert
76
+ # * after_upsert
77
+ # * before_save
78
+ # * around_save
79
+ # * after_save
80
+ # * before_destroy
81
+ # * around_destroy
82
+ # * after_destroy
83
+ #
84
+ # == Storing Observers in Rails
85
+ #
86
+ # If you're using Mongoid within Rails, observer classes are usually stored
87
+ # in +app/models+ with the naming convention of +app/models/audit_observer.rb+.
88
+ #
89
+ # == Configuration
90
+ #
91
+ # In order to activate an observer, list it in the +config.mongoid.observers+
92
+ # configuration setting in your +config/application.rb+ file.
93
+ #
94
+ # config.mongoid.observers = :comment_observer, :signup_observer
95
+ #
96
+ # Observers will not be invoked unless you define them in your
97
+ # application configuration.
98
+ #
99
+ # == Loading
100
+ #
101
+ # Observers register themselves with the model class that they observe,
102
+ # since it is the class that notifies them of events when they occur.
103
+ # As a side-effect, when an observer is loaded, its corresponding model
104
+ # class is loaded.
105
+ #
106
+ # Observers are loaded after the application initializers, so that
107
+ # observed models can make use of extensions. If by any chance you are
108
+ # using observed models in the initialization, you can
109
+ # still load their observers by calling +ModelObserver.instance+ before.
110
+ # Observers are singletons and that call instantiates and registers them.
2
111
class Observer < ActiveModel ::Observer
112
+
113
+ private
114
+
115
+ # Adds the specified observer to the class.
116
+ #
117
+ # @example Add the observer.
118
+ # observer.add_observer!(Document)
119
+ #
120
+ # @param [ Class ] klass The child observer to add.
121
+ #
122
+ # @since 2.0.0.rc.8
123
+ def add_observer! ( klass )
124
+ super and define_callbacks ( klass )
125
+ end
126
+
127
+ # Defines all the callbacks for each observer of the model.
128
+ #
129
+ # @example Define all the callbacks.
130
+ # observer.define_callbacks(Document)
131
+ #
132
+ # @param [ Class ] klass The model to define them on.
133
+ #
134
+ # @since 2.0.0.rc.8
135
+ def define_callbacks ( klass )
136
+ observer = self
137
+ observer_name = observer . class . name . underscore . gsub ( '/' , '__' )
138
+ Mongoid ::Interceptable ::CALLBACKS . each do |callback |
139
+ next unless respond_to? ( callback )
140
+ callback_meth = :"_notify_#{ observer_name } _for_#{ callback } "
141
+ unless klass . respond_to? ( callback_meth )
142
+ klass . send ( :define_method , callback_meth ) do |&block |
143
+ if value = observer . update ( callback , self , &block )
144
+ value
145
+ else
146
+ block . call if block
147
+ end
148
+ end
149
+ klass . send ( callback , callback_meth )
150
+ end
151
+ end
152
+ self
153
+ end
154
+
155
+ # Are the observers disabled for the object?
156
+ #
157
+ # @api private
158
+ #
159
+ # @example If the observer disabled?
160
+ # Observer.disabled_for(band)
161
+ #
162
+ # @param [ Document ] object The model instance.
163
+ #
164
+ # @return [ true, false ] If the observer is disabled.
165
+ def disabled_for? ( object )
166
+ klass = object . class
167
+ return false unless klass . respond_to? ( :observers )
168
+ klass . observers . disabled_for? ( self ) || Mongoid . observers . disabled_for? ( self )
169
+ end
170
+
171
+ class << self
172
+
173
+ # Attaches the observer to the specified classes.
174
+ #
175
+ # @example Attach the BandObserver to the class Artist.
176
+ # class BandObserver < Mongoid::Observer
177
+ # observe :artist
178
+ # end
179
+ #
180
+ # @param [ Array<Symbol> ] models The names of the models.
181
+ #
182
+ # @since 3.0.15
183
+ def observe ( *models )
184
+ models . flatten!
185
+ models . collect! do |model |
186
+ model . respond_to? ( :to_sym ) ? model . to_s . camelize . constantize : model
187
+ end
188
+ singleton_class . redefine_method ( :observed_classes ) { models }
189
+ end
190
+ end
3
191
end
4
192
end
0 commit comments