u-observers 0.9.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.sh +3 -0
- data/Gemfile +10 -8
- data/README.md +495 -11
- data/README.pt-BR.md +529 -0
- data/lib/micro/observers.rb +5 -22
- data/lib/micro/observers/broadcast.rb +77 -0
- data/lib/micro/observers/event.rb +21 -0
- data/lib/micro/observers/event/names.rb +29 -0
- data/lib/micro/observers/for/active_model.rb +36 -0
- data/lib/micro/observers/for/active_record.rb +16 -0
- data/lib/micro/observers/set.rb +100 -0
- data/lib/micro/observers/subscribers.rb +110 -0
- data/lib/micro/observers/utils.rb +12 -2
- data/lib/micro/observers/version.rb +1 -1
- data/lib/u-observers/for/active_model.rb +2 -0
- data/lib/u-observers/for/active_record.rb +2 -0
- metadata +12 -4
- data/lib/micro/observers/events.rb +0 -19
- data/lib/micro/observers/manager.rb +0 -155
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7ebc064f3e8384d9322c640d3d52b1bf70bf6d5159ae67bf3cd34bdf67482eb
|
4
|
+
data.tar.gz: 398d1ab0fc5852a42416c5bc16c377c4a55d760e1e327e2149b58b7db23cae73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d805d69fb5cb7d08d1ef5aec34742d08845d2d544cec1262ec63d99788adb5053ed87558957849f1b9843b772929e9490f1ae243ee3c568d87b94e1b432a8c5
|
7
|
+
data.tar.gz: 932bfa8189f162fea8160779d521168fdebe253d36414c0b05fbccf2a8c661dc94e127fb5a942523e0ae415142d6090cd8a361124f60f550b7d2166c87106c0e
|
data/.travis.sh
CHANGED
data/Gemfile
CHANGED
@@ -23,16 +23,18 @@ simplecov_version =
|
|
23
23
|
else '~> 0.19'
|
24
24
|
end
|
25
25
|
|
26
|
-
sqlite3 =
|
27
|
-
case activerecord
|
28
|
-
when /\A6\.0/, nil then '~> 1.4.0'
|
29
|
-
else '~> 1.3.0'
|
30
|
-
end
|
31
|
-
|
32
26
|
group :test do
|
33
27
|
gem 'minitest', activerecord_version < '4.1' ? '~> 4.2' : '~> 5.0'
|
34
28
|
gem 'simplecov', simplecov_version, require: false
|
35
29
|
|
36
|
-
|
37
|
-
|
30
|
+
if activerecord
|
31
|
+
sqlite3 =
|
32
|
+
case activerecord
|
33
|
+
when /\A6\.0/, nil then '~> 1.4.0'
|
34
|
+
else '~> 1.3.0'
|
35
|
+
end
|
36
|
+
|
37
|
+
gem 'sqlite3', sqlite3
|
38
|
+
gem 'activerecord', activerecord, require: 'active_record'
|
39
|
+
end
|
38
40
|
end
|
data/README.md
CHANGED
@@ -1,28 +1,512 @@
|
|
1
|
-
|
1
|
+
<p align="center">
|
2
|
+
<h1 align="center">👀 μ-observers</h1>
|
3
|
+
<p align="center"><i>Simple and powerful implementation of the observer pattern.</i></p>
|
4
|
+
<br>
|
5
|
+
</p>
|
2
6
|
|
3
|
-
|
7
|
+
<p align="center">
|
8
|
+
<img src="https://img.shields.io/badge/ruby->%3D%202.2.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
|
4
9
|
|
5
|
-
|
10
|
+
<a href="https://rubygems.org/gems/u-observers">
|
11
|
+
<img alt="Gem" src="https://img.shields.io/gem/v/u-observers.svg?style=flat-square">
|
12
|
+
</a>
|
6
13
|
|
7
|
-
|
14
|
+
<a href="https://travis-ci.com/serradura/u-observers">
|
15
|
+
<img alt="Build Status" src="https://travis-ci.com/serradura/u-observers.svg?branch=main">
|
16
|
+
</a>
|
8
17
|
|
9
|
-
|
18
|
+
<a href="https://codeclimate.com/github/serradura/u-observers/maintainability">
|
19
|
+
<img alt="Maintainability" src="https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/maintainability">
|
20
|
+
</a>
|
21
|
+
|
22
|
+
<a href="https://codeclimate.com/github/serradura/u-observers/test_coverage">
|
23
|
+
<img alt="Test Coverage" src="https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/test_coverage">
|
24
|
+
</a>
|
25
|
+
</p>
|
26
|
+
|
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
|
+
|
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
|
+
|
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).
|
34
|
+
|
35
|
+
# Table of contents <!-- omit in toc -->
|
36
|
+
- [Installation](#installation)
|
37
|
+
- [Compatibility](#compatibility)
|
38
|
+
- [Usage](#usage)
|
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)
|
43
|
+
- [Calling the observers](#calling-the-observers)
|
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
|
+
- [Detaching observers](#detaching-observers)
|
49
|
+
- [ActiveRecord and ActiveModel integrations](#activerecord-and-activemodel-integrations)
|
50
|
+
- [notify_observers_on()](#notify_observers_on)
|
51
|
+
- [notify_observers()](#notify_observers)
|
52
|
+
- [Development](#development)
|
53
|
+
- [Contributing](#contributing)
|
54
|
+
- [License](#license)
|
55
|
+
- [Code of Conduct](#code-of-conduct)
|
56
|
+
|
57
|
+
# Installation
|
58
|
+
|
59
|
+
Add this line to your application's Gemfile and `bundle install`:
|
10
60
|
|
11
61
|
```ruby
|
12
|
-
gem '
|
62
|
+
gem 'u-observers'
|
13
63
|
```
|
14
64
|
|
15
|
-
|
65
|
+
# Compatibility
|
16
66
|
|
17
|
-
|
67
|
+
| u-observers | branch | ruby | activerecord |
|
68
|
+
| ----------- | ------- | -------- | ------------- |
|
69
|
+
| unreleased | main | >= 2.2.0 | >= 3.2, < 6.1 |
|
70
|
+
| 2.2.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
71
|
+
| 1.0.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
18
72
|
|
19
|
-
|
73
|
+
> **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).
|
20
74
|
|
21
|
-
|
75
|
+
[⬆️ Back to Top](#table-of-contents-)
|
22
76
|
|
23
77
|
## Usage
|
24
78
|
|
25
|
-
|
79
|
+
Any class with `Micro::Observers` module included can notify events to attached observers.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
require 'securerandom'
|
83
|
+
|
84
|
+
class Order
|
85
|
+
include Micro::Observers
|
86
|
+
|
87
|
+
attr_reader :code
|
88
|
+
|
89
|
+
def initialize
|
90
|
+
@code, @status = SecureRandom.alphanumeric, :draft
|
91
|
+
end
|
92
|
+
|
93
|
+
def canceled?
|
94
|
+
@status == :canceled
|
95
|
+
end
|
96
|
+
|
97
|
+
def cancel!
|
98
|
+
return self if canceled?
|
99
|
+
|
100
|
+
@status = :canceled
|
101
|
+
|
102
|
+
observers.subject_changed!
|
103
|
+
observers.notify(:canceled) and return self
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module OrderEvents
|
108
|
+
def self.canceled(order)
|
109
|
+
puts "The order #(#{order.code}) has been canceled."
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
order = Order.new
|
114
|
+
#<Order:0x00007fb5dd8fce70 @code="X0o9yf1GsdQFvLR4", @status=:draft>
|
115
|
+
|
116
|
+
order.observers.attach(OrderEvents) # attaching multiple observers. e.g. observers.attach(A, B, C)
|
117
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[OrderEvents]>
|
118
|
+
|
119
|
+
order.canceled?
|
120
|
+
# false
|
121
|
+
|
122
|
+
order.cancel!
|
123
|
+
# The message below will be printed by the observer (OrderEvents):
|
124
|
+
# The order #(X0o9yf1GsdQFvLR4) has been canceled
|
125
|
+
|
126
|
+
order.canceled?
|
127
|
+
# true
|
128
|
+
|
129
|
+
order.observers.detach(OrderEvents) # detaching multiple observers. e.g. observers.detach(A, B, C)
|
130
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[]>
|
131
|
+
|
132
|
+
order.canceled?
|
133
|
+
# true
|
134
|
+
|
135
|
+
order.observers.subject_changed!
|
136
|
+
order.observers.notify(:canceled) # nothing will happen, because there are no observers attached.
|
137
|
+
```
|
138
|
+
|
139
|
+
**Highlights of the previous example:**
|
140
|
+
|
141
|
+
To avoid an undesired behavior, you need to mark the subject as changed before notifying your observers about some event.
|
142
|
+
|
143
|
+
You can do this when using the `#subject_changed!` method. It will automatically mark the subject as changed.
|
144
|
+
|
145
|
+
But if you need to apply some conditional to mark a change, you can use the `#subject_changed` method. e.g. `observers.subject_changed(name != new_name)`
|
146
|
+
|
147
|
+
The `#notify` method always requires an event to make a broadcast. So, if you try to use it without one or more events (symbol values) you will get an exception.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
order.observers.notify
|
151
|
+
# ArgumentError (no events (expected at least 1))
|
152
|
+
```
|
153
|
+
|
154
|
+
[⬆️ Back to Top](#table-of-contents-)
|
155
|
+
|
156
|
+
### Sharing a context with your observers
|
157
|
+
|
158
|
+
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.
|
159
|
+
|
160
|
+
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.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class Order
|
164
|
+
include Micro::Observers
|
165
|
+
|
166
|
+
def cancel!
|
167
|
+
observers.subject_changed!
|
168
|
+
observers.notify(:canceled)
|
169
|
+
self
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
module OrderEvents
|
174
|
+
def self.canceled(order, event)
|
175
|
+
puts "The order #(#{order.object_id}) has been canceled. (from: #{event.context[:from]})" # event.ctx is an alias for event.context
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
order = Order.new
|
180
|
+
order.observers.attach(OrderEvents, context: { from: 'example #2' }) # attaching multiple observers. e.g. observers.attach(A, B, context: {hello: :world})
|
181
|
+
order.cancel!
|
182
|
+
# The message below will be printed by the observer (OrderEvents):
|
183
|
+
# The order #(70196221441820) has been canceled. (from: example #2)
|
184
|
+
```
|
185
|
+
|
186
|
+
[⬆️ Back to Top](#table-of-contents-)
|
187
|
+
|
188
|
+
### Sharing data when notifying the observers
|
189
|
+
|
190
|
+
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.
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class Order
|
194
|
+
include Micro::Observers
|
195
|
+
end
|
196
|
+
|
197
|
+
module OrderHandler
|
198
|
+
def self.changed(order, event)
|
199
|
+
puts "The order #(#{order.object_id}) received the number #{event.data} from #{event.ctx[:from]}."
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
order = Order.new
|
204
|
+
order.observers.attach(OrderHandler, context: { from: 'example #3' })
|
205
|
+
order.observers.subject_changed!
|
206
|
+
order.observers.notify(:changed, data: 1)
|
207
|
+
# The message below will be printed by the observer (OrderHandler):
|
208
|
+
# The order #(70196221441820) received the number 1 from example #3.
|
209
|
+
```
|
210
|
+
|
211
|
+
[⬆️ Back to Top](#table-of-contents-)
|
212
|
+
|
213
|
+
### What is a `Micro::Observers::Event`?
|
214
|
+
|
215
|
+
The `Micro::Observers::Event` is the event payload. Follow below all of its properties:
|
216
|
+
|
217
|
+
- `#name` will be the broadcasted event.
|
218
|
+
- `#subject` will be the observed subject.
|
219
|
+
- `#context` will be [the context data](#sharing-a-context-with-your-observers) that was defined in the moment that you attach the observer.
|
220
|
+
- `#data` will be [the value that was shared in the observers' notification](#sharing-data-when-notifying-the-observers).
|
221
|
+
- `#ctx` is an alias for the `#context` method.
|
222
|
+
- `#subj` is an alias for the `#subject` method.
|
223
|
+
|
224
|
+
[⬆️ Back to Top](#table-of-contents-)
|
225
|
+
|
226
|
+
### Using a callable as an observer
|
227
|
+
|
228
|
+
The `observers.on()` method enables you to attach a callable as an observer.
|
229
|
+
|
230
|
+
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).
|
231
|
+
|
232
|
+
This method receives the below options:
|
233
|
+
1. `:event` the expected event name.
|
234
|
+
2. `:call` the callable object itself.
|
235
|
+
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.
|
236
|
+
4. `:context` will be the context data that was defined in the moment that you attach the observer.
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
class Person
|
240
|
+
include Micro::Observers
|
241
|
+
|
242
|
+
attr_reader :name
|
243
|
+
|
244
|
+
def initialize(name)
|
245
|
+
@name = name
|
246
|
+
end
|
247
|
+
|
248
|
+
def name=(new_name)
|
249
|
+
return unless observers.subject_changed(new_name != @name)
|
250
|
+
|
251
|
+
@name = new_name
|
252
|
+
|
253
|
+
observers.notify(:name_has_been_changed)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
PrintPersonName = -> (data) do
|
258
|
+
puts("Person name: #{data.fetch(:person).name}, number: #{data.fetch(:number)}")
|
259
|
+
end
|
260
|
+
|
261
|
+
person = Person.new('Rodrigo')
|
262
|
+
|
263
|
+
person.observers.on(
|
264
|
+
event: :name_has_been_changed,
|
265
|
+
call: PrintPersonName,
|
266
|
+
with: -> event { {person: event.subject, number: event.context} },
|
267
|
+
context: rand
|
268
|
+
)
|
269
|
+
|
270
|
+
person.name = 'Serradura'
|
271
|
+
# The message below will be printed by the observer (PrintPersonName):
|
272
|
+
# Person name: Serradura, number: 0.5018509191706862
|
273
|
+
```
|
274
|
+
|
275
|
+
[⬆️ Back to Top](#table-of-contents-)
|
276
|
+
|
277
|
+
### Calling the observers
|
278
|
+
|
279
|
+
You can use a callable (a class, module, or object that responds to the call method) to be your observers.
|
280
|
+
To do this, you only need to make use of the method `#call` instead of `#notify`.
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
class Order
|
284
|
+
include Micro::Observers
|
285
|
+
|
286
|
+
def cancel!
|
287
|
+
observers.subject_changed!
|
288
|
+
observers.call # in practice, this is a shortcut to observers.notify(:call)
|
289
|
+
self
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
NotifyAfterCancel = -> (order) { puts "The order #(#{order.object_id}) has been canceled." }
|
294
|
+
|
295
|
+
order = Order.new
|
296
|
+
order.observers.attach(NotifyAfterCancel)
|
297
|
+
order.cancel!
|
298
|
+
# The message below will be printed by the observer (NotifyAfterCancel):
|
299
|
+
# The order #(70196221441820) has been canceled.
|
300
|
+
```
|
301
|
+
|
302
|
+
> **Note**: The `observers.call` can receive one or more events, but in this case, the default event (`call`) won't be transmitted.
|
303
|
+
|
304
|
+
[⬆️ Back to Top](#table-of-contents-)
|
305
|
+
|
306
|
+
### Notifying observers without marking them as changed
|
307
|
+
|
308
|
+
This feature needs to be used with caution!
|
309
|
+
|
310
|
+
If you use the methods `#notify!` or `#call!` you won't need to mark observers with `#subject_changed`.
|
311
|
+
|
312
|
+
[⬆️ Back to Top](#table-of-contents-)
|
313
|
+
|
314
|
+
### Defining observers that execute only once
|
315
|
+
|
316
|
+
There are two ways to attach an observer and define it to be performed only once.
|
317
|
+
|
318
|
+
The first way to do this is passing the `perform_once: true` option to the `observers.attach()` method. e.g.
|
319
|
+
|
320
|
+
#### `observers.attach(*args, perform_once: true)`
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
class Order
|
324
|
+
include Micro::Observers
|
325
|
+
|
326
|
+
def cancel!
|
327
|
+
observers.notify!(:canceled)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
module OrderNotifications
|
332
|
+
def self.canceled(order)
|
333
|
+
puts "The order #(#{order.object_id}) has been canceled."
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
order = Order.new
|
338
|
+
order.observers.attach(OrderNotifications, perform_once: true) # you can also pass an array of observers with this option
|
339
|
+
|
340
|
+
order.observers.some? # true
|
341
|
+
order.cancel! # The order #(70291642071660) has been canceled.
|
342
|
+
|
343
|
+
order.observers.some? # false
|
344
|
+
order.cancel! # Nothing will happen because there aren't observers.
|
345
|
+
```
|
346
|
+
|
347
|
+
#### `observers.once(event:, call:, ...)`
|
348
|
+
|
349
|
+
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.
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
class Order
|
353
|
+
include Micro::Observers
|
354
|
+
|
355
|
+
def cancel!
|
356
|
+
observers.notify!(:canceled)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
module NotifyAfterCancel
|
361
|
+
def self.call(event)
|
362
|
+
puts "The order #(#{event.subject.object_id}) has been canceled."
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
order = Order.new
|
367
|
+
order.observers.once(event: :canceled, call: NotifyAfterCancel)
|
368
|
+
|
369
|
+
order.observers.some? # true
|
370
|
+
order.cancel! # The order #(70301497466060) has been canceled.
|
371
|
+
|
372
|
+
order.observers.some? # false
|
373
|
+
order.cancel! # Nothing will happen because there aren't observers.
|
374
|
+
```
|
375
|
+
|
376
|
+
[⬆️ Back to Top](#table-of-contents-)
|
377
|
+
|
378
|
+
### Detaching observers
|
379
|
+
|
380
|
+
As shown in the first example, you can use the `observers.detach()` to remove observers.
|
381
|
+
|
382
|
+
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()`.
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
class Order
|
386
|
+
include Micro::Observers
|
387
|
+
end
|
388
|
+
|
389
|
+
NotifyAfterCancel = -> {}
|
390
|
+
|
391
|
+
module OrderNotifications
|
392
|
+
def self.canceled(_order)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
order = Order.new
|
397
|
+
order.observers.on(event: :canceled, call: NotifyAfterCancel)
|
398
|
+
order.observers.attach(OrderNotifications)
|
399
|
+
|
400
|
+
order.observers.some? # true
|
401
|
+
order.observers.count # 2
|
402
|
+
|
403
|
+
order.observers.off(:canceled) # removing the callable (NotifyAfterCancel).
|
404
|
+
order.observers.some? # true
|
405
|
+
order.observers.count # 1
|
406
|
+
|
407
|
+
order.observers.off(OrderNotifications)
|
408
|
+
order.observers.some? # false
|
409
|
+
order.observers.count # 0
|
410
|
+
```
|
411
|
+
|
412
|
+
[⬆️ Back to Top](#table-of-contents-)
|
413
|
+
|
414
|
+
### ActiveRecord and ActiveModel integrations
|
415
|
+
|
416
|
+
To make use of this feature you need to require an additional module.
|
417
|
+
|
418
|
+
Gemfile example:
|
419
|
+
```ruby
|
420
|
+
gem 'u-observers', require: 'u-observers/for/active_record'
|
421
|
+
```
|
422
|
+
|
423
|
+
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:
|
424
|
+
|
425
|
+
#### notify_observers_on()
|
426
|
+
|
427
|
+
The `notify_observers_on` allows you to define one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your object observers.
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
class Post < ActiveRecord::Base
|
431
|
+
include ::Micro::Observers::For::ActiveRecord
|
432
|
+
|
433
|
+
notify_observers_on(:after_commit) # using multiple callbacks. e.g. notify_observers_on(:before_save, :after_commit)
|
434
|
+
|
435
|
+
# The method above does the same as the commented example below.
|
436
|
+
#
|
437
|
+
# after_commit do |record|
|
438
|
+
# record.subject_changed!
|
439
|
+
# record.notify(:after_commit)
|
440
|
+
# end
|
441
|
+
end
|
442
|
+
|
443
|
+
module TitlePrinter
|
444
|
+
def self.after_commit(post)
|
445
|
+
puts "Title: #{post.title}"
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
module TitlePrinterWithContext
|
450
|
+
def self.after_commit(post, event)
|
451
|
+
puts "Title: #{post.title} (from: #{event.context[:from]})"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
Post.transaction do
|
456
|
+
post = Post.new(title: 'Hello world')
|
457
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #6' })
|
458
|
+
post.save
|
459
|
+
end
|
460
|
+
# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):
|
461
|
+
# Title: Hello world
|
462
|
+
# Title: Hello world (from: example #6)
|
463
|
+
```
|
464
|
+
|
465
|
+
[⬆️ Back to Top](#table-of-contents-)
|
466
|
+
|
467
|
+
#### notify_observers()
|
468
|
+
|
469
|
+
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.
|
470
|
+
|
471
|
+
```ruby
|
472
|
+
class Post < ActiveRecord::Base
|
473
|
+
include ::Micro::Observers::For::ActiveRecord
|
474
|
+
|
475
|
+
after_commit(¬ify_observers(:transaction_completed))
|
476
|
+
|
477
|
+
# The method above does the same as the commented example below.
|
478
|
+
#
|
479
|
+
# after_commit do |record|
|
480
|
+
# record.subject_changed!
|
481
|
+
# record.notify(:transaction_completed)
|
482
|
+
# end
|
483
|
+
end
|
484
|
+
|
485
|
+
module TitlePrinter
|
486
|
+
def self.transaction_completed(post)
|
487
|
+
puts("Title: #{post.title}")
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
module TitlePrinterWithContext
|
492
|
+
def self.transaction_completed(post, event)
|
493
|
+
puts("Title: #{post.title} (from: #{event.ctx[:from]})")
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
Post.transaction do
|
498
|
+
post = Post.new(title: 'Olá mundo')
|
499
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #7' })
|
500
|
+
post.save
|
501
|
+
end
|
502
|
+
# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):
|
503
|
+
# Title: Olá mundo
|
504
|
+
# Title: Olá mundo (from: example #5)
|
505
|
+
```
|
506
|
+
|
507
|
+
> **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.
|
508
|
+
|
509
|
+
[⬆️ Back to Top](#table-of-contents-)
|
26
510
|
|
27
511
|
## Development
|
28
512
|
|