u-observers 1.0.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +345 -40
- data/README.pt-BR.md +610 -0
- data/lib/micro/observers.rb +5 -3
- data/lib/micro/observers/broadcast.rb +77 -0
- data/lib/micro/observers/event.rb +21 -0
- data/lib/micro/observers/{events.rb → event/names.rb} +5 -3
- data/lib/micro/observers/for/active_model.rb +2 -2
- data/lib/micro/observers/set.rb +107 -0
- data/lib/micro/observers/subscribers.rb +110 -0
- data/lib/micro/observers/utils.rb +12 -3
- data/lib/micro/observers/version.rb +1 -1
- metadata +8 -4
- data/lib/micro/observers/manager.rb +0 -172
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55ea9f6a206dba9526be1d7777969b26311f725652b3020344db33563a5835c8
|
4
|
+
data.tar.gz: d8a6e1bf2e73d15fadbd71e82aa17e0c511fb11d97a7839ba165c50589324b66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9b907879c02a2071b6c194af9f5c1a6e50a3bd17655beaed0f9dfb7858c892f657c9a57666d320d23d0055a48625d168d625fbae19ca7a560722b8c1b475d31
|
7
|
+
data.tar.gz: 4a551b0a43d15b5968d843d2a3b737ae245e177da3ac0c595f9265be887ff3b2b1205a3544c368973cae993869a9083fbffdc2cf64dab9b18c5d6d17eae5282b
|
data/README.md
CHANGED
@@ -26,17 +26,30 @@
|
|
26
26
|
|
27
27
|
This gem implements the observer pattern [[1]](https://en.wikipedia.org/wiki/Observer_pattern)[[2]](https://refactoring.guru/design-patterns/observer) (also known as publish/subscribe). It provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.
|
28
28
|
|
29
|
-
Ruby's standard library [has an abstraction](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html) that enables you to use this pattern. But its design can conflict with other mainstream libraries, like the [`ActiveModel`/`ActiveRecord`](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed), which also has the [`changed`](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html#method-i-changed) method. In this case, the behavior of the Stdlib will be
|
29
|
+
Ruby's standard library [has an abstraction](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html) that enables you to use this pattern. But its design can conflict with other mainstream libraries, like the [`ActiveModel`/`ActiveRecord`](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed), which also has the [`changed`](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html#method-i-changed) method. In this case, the behavior of the Stdlib will be compromised.
|
30
30
|
|
31
|
-
Because of this issue, I decided to create a gem that encapsulates the pattern without changing the object's implementation so much. The `Micro::Observers` includes just one instance method in the target class (its instance will be the observed subject).
|
31
|
+
Because of this issue, I decided to create a gem that encapsulates the pattern without changing the object's implementation so much. The `Micro::Observers` includes just one instance method in the target class (its instance will be the observed subject/object).
|
32
|
+
|
33
|
+
> **Note:** Você entende português? 🇧🇷 🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/serradura/u-observers/blob/main/README.pt-BR.md).
|
32
34
|
|
33
35
|
# Table of contents <!-- omit in toc -->
|
34
36
|
- [Installation](#installation)
|
35
37
|
- [Compatibility](#compatibility)
|
36
38
|
- [Usage](#usage)
|
37
|
-
- [
|
39
|
+
- [Sharing a context with your observers](#sharing-a-context-with-your-observers)
|
40
|
+
- [Sharing data when notifying the observers](#sharing-data-when-notifying-the-observers)
|
41
|
+
- [What is a `Micro::Observers::Event`?](#what-is-a-microobserversevent)
|
42
|
+
- [Using a callable as an observer](#using-a-callable-as-an-observer)
|
38
43
|
- [Calling the observers](#calling-the-observers)
|
39
44
|
- [Notifying observers without marking them as changed](#notifying-observers-without-marking-them-as-changed)
|
45
|
+
- [Defining observers that execute only once](#defining-observers-that-execute-only-once)
|
46
|
+
- [`observers.attach(*args, perform_once: true)`](#observersattachargs-perform_once-true)
|
47
|
+
- [`observers.once(event:, call:, ...)`](#observersonceevent-call-)
|
48
|
+
- [Defining observers using blocks](#defining-observers-using-blocks)
|
49
|
+
- [order.observers.on()](#orderobserverson)
|
50
|
+
- [order.observers.on()](#orderobserverson-1)
|
51
|
+
- [Replacing a block by a `lambda`/`proc`](#replacing-a-block-by-a-lambdaproc)
|
52
|
+
- [Detaching observers](#detaching-observers)
|
40
53
|
- [ActiveRecord and ActiveModel integrations](#activerecord-and-activemodel-integrations)
|
41
54
|
- [notify_observers_on()](#notify_observers_on)
|
42
55
|
- [notify_observers()](#notify_observers)
|
@@ -57,11 +70,13 @@ gem 'u-observers'
|
|
57
70
|
|
58
71
|
| u-observers | branch | ruby | activerecord |
|
59
72
|
| ----------- | ------- | -------- | ------------- |
|
60
|
-
|
|
73
|
+
| unreleased | main | >= 2.2.0 | >= 3.2, < 6.1 |
|
74
|
+
| 2.3.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
75
|
+
| 1.0.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
61
76
|
|
62
77
|
> **Note**: The ActiveRecord isn't a dependency, but you could add a module to enable some static methods that were designed to be used with its [callbacks](https://guides.rubyonrails.org/active_record_callbacks.html).
|
63
78
|
|
64
|
-
[⬆️ Back to Top](#table-of-contents-)
|
79
|
+
[⬆️ Back to Top](#table-of-contents-)
|
65
80
|
|
66
81
|
## Usage
|
67
82
|
|
@@ -102,8 +117,8 @@ end
|
|
102
117
|
order = Order.new
|
103
118
|
#<Order:0x00007fb5dd8fce70 @code="X0o9yf1GsdQFvLR4", @status=:draft>
|
104
119
|
|
105
|
-
order.observers.attach(OrderEvents)
|
106
|
-
# <#Micro::Observers::
|
120
|
+
order.observers.attach(OrderEvents) # attaching multiple observers. e.g. observers.attach(A, B, C)
|
121
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[OrderEvents]>
|
107
122
|
|
108
123
|
order.canceled?
|
109
124
|
# false
|
@@ -114,11 +129,20 @@ order.cancel!
|
|
114
129
|
|
115
130
|
order.canceled?
|
116
131
|
# true
|
132
|
+
|
133
|
+
order.observers.detach(OrderEvents) # detaching multiple observers. e.g. observers.detach(A, B, C)
|
134
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[]>
|
135
|
+
|
136
|
+
order.canceled?
|
137
|
+
# true
|
138
|
+
|
139
|
+
order.observers.subject_changed!
|
140
|
+
order.observers.notify(:canceled) # nothing will happen, because there are no observers attached.
|
117
141
|
```
|
118
142
|
|
119
143
|
**Highlights of the previous example:**
|
120
144
|
|
121
|
-
To avoid an undesired behavior,
|
145
|
+
To avoid an undesired behavior, you need to mark the subject as changed before notifying your observers about some event.
|
122
146
|
|
123
147
|
You can do this when using the `#subject_changed!` method. It will automatically mark the subject as changed.
|
124
148
|
|
@@ -131,11 +155,13 @@ order.observers.notify
|
|
131
155
|
# ArgumentError (no events (expected at least 1))
|
132
156
|
```
|
133
157
|
|
134
|
-
[⬆️ Back to Top](#table-of-contents-)
|
158
|
+
[⬆️ Back to Top](#table-of-contents-)
|
135
159
|
|
136
|
-
###
|
160
|
+
### Sharing a context with your observers
|
137
161
|
|
138
|
-
To
|
162
|
+
To share a context value (any kind of Ruby object) with one or more observers, you will need to use the `:context` keyword as the last argument of the `#attach` method. This feature gives you a unique opportunity to share a value in the attaching moment.
|
163
|
+
|
164
|
+
When the observer method receives two arguments, the first one will be the subject, and the second one an instance of `Micro::Observers::Event` that will have the given context value.
|
139
165
|
|
140
166
|
```ruby
|
141
167
|
class Order
|
@@ -149,24 +175,113 @@ class Order
|
|
149
175
|
end
|
150
176
|
|
151
177
|
module OrderEvents
|
152
|
-
def self.canceled(order,
|
153
|
-
puts "The order #(#{order.
|
178
|
+
def self.canceled(order, event)
|
179
|
+
puts "The order #(#{order.object_id}) has been canceled. (from: #{event.context[:from]})" # event.ctx is an alias for event.context
|
154
180
|
end
|
155
181
|
end
|
156
182
|
|
157
183
|
order = Order.new
|
158
|
-
order.observers.attach(OrderEvents, context: { from: 'example #2' ) # attaching multiple observers. e.g. observers.attach(A, B, context: {hello: :world})
|
184
|
+
order.observers.attach(OrderEvents, context: { from: 'example #2' }) # attaching multiple observers. e.g. observers.attach(A, B, context: {hello: :world})
|
159
185
|
order.cancel!
|
160
186
|
# The message below will be printed by the observer (OrderEvents):
|
161
187
|
# The order #(70196221441820) has been canceled. (from: example #2)
|
162
188
|
```
|
163
189
|
|
164
|
-
[⬆️ Back to Top](#table-of-contents-)
|
190
|
+
[⬆️ Back to Top](#table-of-contents-)
|
191
|
+
|
192
|
+
### Sharing data when notifying the observers
|
193
|
+
|
194
|
+
As previously mentioned, the [`event context`](#sharing-a-context-with-your-observers) is a value that is stored when you attach your observer. But sometimes, it will be useful to send some additional data when broadcasting an event to the observers. The `event data` gives you this unique opportunity to share some value at the the notification moment.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
class Order
|
198
|
+
include Micro::Observers
|
199
|
+
end
|
200
|
+
|
201
|
+
module OrderHandler
|
202
|
+
def self.changed(order, event)
|
203
|
+
puts "The order #(#{order.object_id}) received the number #{event.data} from #{event.ctx[:from]}."
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
order = Order.new
|
208
|
+
order.observers.attach(OrderHandler, context: { from: 'example #3' })
|
209
|
+
order.observers.subject_changed!
|
210
|
+
order.observers.notify(:changed, data: 1)
|
211
|
+
# The message below will be printed by the observer (OrderHandler):
|
212
|
+
# The order #(70196221441820) received the number 1 from example #3.
|
213
|
+
```
|
214
|
+
|
215
|
+
[⬆️ Back to Top](#table-of-contents-)
|
216
|
+
|
217
|
+
### What is a `Micro::Observers::Event`?
|
218
|
+
|
219
|
+
The `Micro::Observers::Event` is the event payload. Follow below all of its properties:
|
220
|
+
|
221
|
+
- `#name` will be the broadcasted event.
|
222
|
+
- `#subject` will be the observed subject.
|
223
|
+
- `#context` will be [the context data](#sharing-a-context-with-your-observers) that was defined in the moment that you attach the observer.
|
224
|
+
- `#data` will be [the value that was shared in the observers' notification](#sharing-data-when-notifying-the-observers).
|
225
|
+
- `#ctx` is an alias for the `#context` method.
|
226
|
+
- `#subj` is an alias for the `#subject` method.
|
227
|
+
|
228
|
+
[⬆️ Back to Top](#table-of-contents-)
|
229
|
+
|
230
|
+
### Using a callable as an observer
|
231
|
+
|
232
|
+
The `observers.on()` method enables you to attach a callable as an observer.
|
233
|
+
|
234
|
+
Usually, a callable has a well-defined responsibility (do only one thing), because of this, it tends to be more [SRP (Single-responsibility principle)](https://en.wikipedia.org/wiki/Single-responsibility_principle). friendly than a conventional observer (that could have N methods to respond to different kinds of notification).
|
235
|
+
|
236
|
+
This method receives the below options:
|
237
|
+
1. `:event` the expected event name.
|
238
|
+
2. `:call` the callable object itself.
|
239
|
+
3. `:with` (optional) it can define the value which will be used as the callable object's argument. So, if it is a `Proc`, a `Micro::Observers::Event` instance will be received as the `Proc` argument, and its output will be the callable argument. But if this option wasn't defined, the `Micro::Observers::Event` instance will be the callable argument.
|
240
|
+
4. `:context` will be the context data that was defined in the moment that you attach the observer.
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
class Person
|
244
|
+
include Micro::Observers
|
245
|
+
|
246
|
+
attr_reader :name
|
247
|
+
|
248
|
+
def initialize(name)
|
249
|
+
@name = name
|
250
|
+
end
|
251
|
+
|
252
|
+
def name=(new_name)
|
253
|
+
return unless observers.subject_changed(new_name != @name)
|
254
|
+
|
255
|
+
@name = new_name
|
256
|
+
|
257
|
+
observers.notify(:name_has_been_changed)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
PrintPersonName = -> (data) do
|
262
|
+
puts("Person name: #{data.fetch(:person).name}, number: #{data.fetch(:number)}")
|
263
|
+
end
|
264
|
+
|
265
|
+
person = Person.new('Rodrigo')
|
266
|
+
|
267
|
+
person.observers.on(
|
268
|
+
event: :name_has_been_changed,
|
269
|
+
call: PrintPersonName,
|
270
|
+
with: -> event { {person: event.subject, number: event.context} },
|
271
|
+
context: rand
|
272
|
+
)
|
273
|
+
|
274
|
+
person.name = 'Serradura'
|
275
|
+
# The message below will be printed by the observer (PrintPersonName):
|
276
|
+
# Person name: Serradura, number: 0.5018509191706862
|
277
|
+
```
|
278
|
+
|
279
|
+
[⬆️ Back to Top](#table-of-contents-)
|
165
280
|
|
166
281
|
### Calling the observers
|
167
282
|
|
168
283
|
You can use a callable (a class, module, or object that responds to the call method) to be your observers.
|
169
|
-
To do this, you only need make use of the method `#call` instead of `#notify`.
|
284
|
+
To do this, you only need to make use of the method `#call` instead of `#notify`.
|
170
285
|
|
171
286
|
```ruby
|
172
287
|
class Order
|
@@ -179,18 +294,18 @@ class Order
|
|
179
294
|
end
|
180
295
|
end
|
181
296
|
|
182
|
-
|
297
|
+
NotifyAfterCancel = -> (order) { puts "The order #(#{order.object_id}) has been canceled." }
|
183
298
|
|
184
299
|
order = Order.new
|
185
|
-
order.observers.attach(
|
300
|
+
order.observers.attach(NotifyAfterCancel)
|
186
301
|
order.cancel!
|
187
|
-
# The message below will be printed by the observer (
|
302
|
+
# The message below will be printed by the observer (NotifyAfterCancel):
|
188
303
|
# The order #(70196221441820) has been canceled.
|
189
304
|
```
|
190
305
|
|
191
|
-
|
306
|
+
> **Note**: The `observers.call` can receive one or more events, but in this case, the default event (`call`) won't be transmitted.
|
192
307
|
|
193
|
-
[⬆️ Back to Top](#table-of-contents-)
|
308
|
+
[⬆️ Back to Top](#table-of-contents-)
|
194
309
|
|
195
310
|
### Notifying observers without marking them as changed
|
196
311
|
|
@@ -198,11 +313,188 @@ This feature needs to be used with caution!
|
|
198
313
|
|
199
314
|
If you use the methods `#notify!` or `#call!` you won't need to mark observers with `#subject_changed`.
|
200
315
|
|
201
|
-
[⬆️ Back to Top](#table-of-contents-)
|
316
|
+
[⬆️ Back to Top](#table-of-contents-)
|
317
|
+
|
318
|
+
### Defining observers that execute only once
|
319
|
+
|
320
|
+
There are two ways to attach an observer and define it to be performed only once.
|
321
|
+
|
322
|
+
The first way to do this is passing the `perform_once: true` option to the `observers.attach()` method. e.g.
|
323
|
+
|
324
|
+
#### `observers.attach(*args, perform_once: true)`
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
class Order
|
328
|
+
include Micro::Observers
|
329
|
+
|
330
|
+
def cancel!
|
331
|
+
observers.notify!(:canceled)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
module OrderNotifications
|
336
|
+
def self.canceled(order)
|
337
|
+
puts "The order #(#{order.object_id}) has been canceled."
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
order = Order.new
|
342
|
+
order.observers.attach(OrderNotifications, perform_once: true) # you can also pass an array of observers with this option
|
343
|
+
|
344
|
+
order.observers.some? # true
|
345
|
+
order.cancel! # The order #(70291642071660) has been canceled.
|
346
|
+
|
347
|
+
order.observers.some? # false
|
348
|
+
order.cancel! # Nothing will happen because there aren't observers.
|
349
|
+
```
|
350
|
+
|
351
|
+
#### `observers.once(event:, call:, ...)`
|
352
|
+
|
353
|
+
The second way to achieve this is using `observers.once()` that has the same API of [`observers.on()`](#using-a-callable-as-an-observer). But the difference of the `#once()` method is that it will remove the observer after its execution.
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class Order
|
357
|
+
include Micro::Observers
|
358
|
+
|
359
|
+
def cancel!
|
360
|
+
observers.notify!(:canceled)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
module NotifyAfterCancel
|
365
|
+
def self.call(event)
|
366
|
+
puts "The order #(#{event.subject.object_id}) has been canceled."
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
order = Order.new
|
371
|
+
order.observers.once(event: :canceled, call: NotifyAfterCancel)
|
372
|
+
|
373
|
+
order.observers.some? # true
|
374
|
+
order.cancel! # The order #(70301497466060) has been canceled.
|
375
|
+
|
376
|
+
order.observers.some? # false
|
377
|
+
order.cancel! # Nothing will happen because there aren't observers.
|
378
|
+
```
|
379
|
+
|
380
|
+
[⬆️ Back to Top](#table-of-contents-)
|
381
|
+
|
382
|
+
### Defining observers using blocks
|
383
|
+
|
384
|
+
The methods `#on()` and `#once()` can receive the event name (a `symbol`) and a block to define observers.
|
385
|
+
|
386
|
+
#### order.observers.on()
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
class Order
|
390
|
+
include Micro::Observers
|
391
|
+
|
392
|
+
def cancel!
|
393
|
+
observers.notify!(:canceled)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
order = Order.new
|
398
|
+
order.observers.on(:canceled) do |event|
|
399
|
+
puts "The order #(#{event.subject.object_id}) has been canceled."
|
400
|
+
end
|
401
|
+
|
402
|
+
order.observers.some? # true
|
403
|
+
|
404
|
+
order.cancel! # The order #(70301497466060) has been canceled.
|
405
|
+
|
406
|
+
order.observers.some? # true
|
407
|
+
```
|
408
|
+
|
409
|
+
#### order.observers.on()
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
class Order
|
413
|
+
include Micro::Observers
|
414
|
+
|
415
|
+
def cancel!
|
416
|
+
observers.notify!(:canceled)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
order = Order.new
|
421
|
+
order.observers.once(:canceled) do |event|
|
422
|
+
puts "The order #(#{event.subject.object_id}) has been canceled."
|
423
|
+
end
|
424
|
+
|
425
|
+
order.observers.some? # true
|
426
|
+
|
427
|
+
order.cancel! # The order #(70301497466060) has been canceled.
|
428
|
+
|
429
|
+
order.observers.some? # false
|
430
|
+
```
|
431
|
+
|
432
|
+
#### Replacing a block by a `lambda`/`proc`
|
433
|
+
|
434
|
+
Ruby allows you to replace any block with a `lambda`/`proc`. e.g.
|
435
|
+
|
436
|
+
```ruby
|
437
|
+
class Order
|
438
|
+
include Micro::Observers
|
439
|
+
|
440
|
+
def cancel!
|
441
|
+
observers.notify!(:canceled)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
NotifyAfterCancel = -> event { puts "The order #(#{event.subject.object_id}) has been canceled." }
|
446
|
+
|
447
|
+
order = Order.new
|
448
|
+
order.observers.once(:canceled, &NotifyAfterCancel)
|
449
|
+
|
450
|
+
order.observers.some? # true
|
451
|
+
order.cancel! # The order #(70301497466060) has been canceled.
|
452
|
+
|
453
|
+
order.observers.some? # false
|
454
|
+
order.cancel! # Nothing will happen because there aren't observers.
|
455
|
+
```
|
456
|
+
|
457
|
+
[⬆️ Back to Top](#table-of-contents-)
|
458
|
+
|
459
|
+
### Detaching observers
|
460
|
+
|
461
|
+
As shown in the first example, you can use the `observers.detach()` to remove observers.
|
462
|
+
|
463
|
+
But, there is an alternative method to remove observer objects or remove callables by their event names. The method to do this is: `observers.off()`.
|
464
|
+
|
465
|
+
```ruby
|
466
|
+
class Order
|
467
|
+
include Micro::Observers
|
468
|
+
end
|
469
|
+
|
470
|
+
NotifyAfterCancel = -> {}
|
471
|
+
|
472
|
+
module OrderNotifications
|
473
|
+
def self.canceled(_order)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
order = Order.new
|
478
|
+
order.observers.on(event: :canceled, call: NotifyAfterCancel)
|
479
|
+
order.observers.attach(OrderNotifications)
|
480
|
+
|
481
|
+
order.observers.some? # true
|
482
|
+
order.observers.count # 2
|
483
|
+
|
484
|
+
order.observers.off(:canceled) # removing the callable (NotifyAfterCancel).
|
485
|
+
order.observers.some? # true
|
486
|
+
order.observers.count # 1
|
487
|
+
|
488
|
+
order.observers.off(OrderNotifications)
|
489
|
+
order.observers.some? # false
|
490
|
+
order.observers.count # 0
|
491
|
+
```
|
492
|
+
|
493
|
+
[⬆️ Back to Top](#table-of-contents-)
|
202
494
|
|
203
495
|
### ActiveRecord and ActiveModel integrations
|
204
496
|
|
205
|
-
To make use of this feature you need to require an additional module
|
497
|
+
To make use of this feature you need to require an additional module.
|
206
498
|
|
207
499
|
Gemfile example:
|
208
500
|
```ruby
|
@@ -211,16 +503,22 @@ gem 'u-observers', require: 'u-observers/for/active_record'
|
|
211
503
|
|
212
504
|
This feature will expose modules that could be used to add macros (static methods) that were designed to work with `ActiveModel`/`ActiveRecord` callbacks. e.g:
|
213
505
|
|
214
|
-
|
215
506
|
#### notify_observers_on()
|
216
507
|
|
217
|
-
The `notify_observers_on` allows you to
|
508
|
+
The `notify_observers_on` allows you to define one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your object observers.
|
218
509
|
|
219
510
|
```ruby
|
220
511
|
class Post < ActiveRecord::Base
|
221
512
|
include ::Micro::Observers::For::ActiveRecord
|
222
513
|
|
223
|
-
notify_observers_on(:after_commit) #
|
514
|
+
notify_observers_on(:after_commit) # using multiple callbacks. e.g. notify_observers_on(:before_save, :after_commit)
|
515
|
+
|
516
|
+
# The method above does the same as the commented example below.
|
517
|
+
#
|
518
|
+
# after_commit do |record|
|
519
|
+
# record.subject_changed!
|
520
|
+
# record.notify(:after_commit)
|
521
|
+
# end
|
224
522
|
end
|
225
523
|
|
226
524
|
module TitlePrinter
|
@@ -230,32 +528,39 @@ module TitlePrinter
|
|
230
528
|
end
|
231
529
|
|
232
530
|
module TitlePrinterWithContext
|
233
|
-
def self.after_commit(post,
|
234
|
-
puts "Title: #{post.title} (from: #{context[:from]})"
|
531
|
+
def self.after_commit(post, event)
|
532
|
+
puts "Title: #{post.title} (from: #{event.context[:from]})"
|
235
533
|
end
|
236
534
|
end
|
237
535
|
|
238
536
|
Post.transaction do
|
239
537
|
post = Post.new(title: 'Hello world')
|
240
|
-
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example
|
538
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #6' })
|
241
539
|
post.save
|
242
540
|
end
|
243
|
-
# The message below will be printed by the
|
541
|
+
# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):
|
244
542
|
# Title: Hello world
|
245
|
-
# Title: Hello world (from: example
|
543
|
+
# Title: Hello world (from: example #6)
|
246
544
|
```
|
247
545
|
|
248
|
-
[⬆️ Back to Top](#table-of-contents-)
|
546
|
+
[⬆️ Back to Top](#table-of-contents-)
|
249
547
|
|
250
548
|
#### notify_observers()
|
251
549
|
|
252
|
-
The `notify_observers` allows you to
|
550
|
+
The `notify_observers` allows you to define one or more *events*, that will be used to notify after the execution of some `ActiveModel`/`ActiveRecord` callback.
|
253
551
|
|
254
552
|
```ruby
|
255
553
|
class Post < ActiveRecord::Base
|
256
554
|
include ::Micro::Observers::For::ActiveRecord
|
257
555
|
|
258
556
|
after_commit(¬ify_observers(:transaction_completed))
|
557
|
+
|
558
|
+
# The method above does the same as the commented example below.
|
559
|
+
#
|
560
|
+
# after_commit do |record|
|
561
|
+
# record.subject_changed!
|
562
|
+
# record.notify(:transaction_completed)
|
563
|
+
# end
|
259
564
|
end
|
260
565
|
|
261
566
|
module TitlePrinter
|
@@ -265,24 +570,24 @@ module TitlePrinter
|
|
265
570
|
end
|
266
571
|
|
267
572
|
module TitlePrinterWithContext
|
268
|
-
def self.transaction_completed(post,
|
269
|
-
puts("Title: #{post.title} (from: #{
|
573
|
+
def self.transaction_completed(post, event)
|
574
|
+
puts("Title: #{post.title} (from: #{event.ctx[:from]})")
|
270
575
|
end
|
271
576
|
end
|
272
577
|
|
273
578
|
Post.transaction do
|
274
579
|
post = Post.new(title: 'Olá mundo')
|
275
|
-
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example
|
580
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #7' })
|
276
581
|
post.save
|
277
582
|
end
|
278
|
-
# The message below will be printed by the
|
583
|
+
# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):
|
279
584
|
# Title: Olá mundo
|
280
|
-
# Title: Olá mundo (from: example 5)
|
585
|
+
# Title: Olá mundo (from: example #5)
|
281
586
|
```
|
282
587
|
|
283
|
-
|
588
|
+
> **Note**: You can use `include ::Micro::Observers::For::ActiveModel` if your class only makes use of the `ActiveModel` and all the previous examples will work.
|
284
589
|
|
285
|
-
[⬆️ Back to Top](#table-of-contents-)
|
590
|
+
[⬆️ Back to Top](#table-of-contents-)
|
286
591
|
|
287
592
|
## Development
|
288
593
|
|