u-observers 0.8.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b67e1c24feb02de944be44aacb6bba935d9dc1dfac6c53de2ba95dcffbd93ee
4
- data.tar.gz: aead6fb10e5ada014fed52383f031d140363cbf4f7ef9b21f5ee7c9e9f54c792
3
+ metadata.gz: e7da28034b714bb64baf033cf43705ed585fa007659964dc36bec440436292f0
4
+ data.tar.gz: cd2b40e92cdfd12708ab36f1ed8a7726ecfbc2c6e96223ca090aedc7758f44f8
5
5
  SHA512:
6
- metadata.gz: a77a575068bdee0f9312e25e5623a4031bb1369dee11df5d039121e84cf7cab418b8b37c3c93ff46265528304b64f80fc1be61ca42e78d9436e9e779c390b2bd
7
- data.tar.gz: f90045332776294e6bc3bf6e70c34b0f24f40551f32a4d9be029ecd5fdfcad4b68d33b6f5c81802e9e257dedf9d1e16395bda434ee2dd48f38f9e291099a8a4e
6
+ metadata.gz: 51be920a8f6f0123e0a94fbf180fb85517ddccb107f729f6eaa38ac840e14a9d207cb82e8f3f76e018e3836f4a7cc0b47b7ea6320ffd0615d22df49c6b693d73
7
+ data.tar.gz: 66c73487b724149f640e096b24a1e9ecf5e5d8780e9679f1d182823eda377e8b289ed88c98d73f6a81147e945eba7a8ce24278244ab50c1a191ebba1b912e265
@@ -0,0 +1 @@
1
+ ruby 2.6.5
@@ -0,0 +1,34 @@
1
+ #!/bin/bash
2
+
3
+ ruby_v=$(ruby -v)
4
+
5
+ bundle update
6
+ bundle exec rake test
7
+
8
+ ACTIVERECORD_VERSION='3.2' bundle update
9
+ ACTIVERECORD_VERSION='3.2' bundle exec rake test
10
+
11
+ ACTIVERECORD_VERSION='4.0' bundle update
12
+ ACTIVERECORD_VERSION='4.0' bundle exec rake test
13
+
14
+ ACTIVERECORD_VERSION='4.1' bundle update
15
+ ACTIVERECORD_VERSION='4.1' bundle exec rake test
16
+
17
+ ACTIVERECORD_VERSION='4.2' bundle update
18
+ ACTIVERECORD_VERSION='4.2' bundle exec rake test
19
+
20
+ ACTIVERECORD_VERSION='5.0' bundle update
21
+ ACTIVERECORD_VERSION='5.0' bundle exec rake test
22
+
23
+ ACTIVERECORD_VERSION='5.1' bundle update
24
+ ACTIVERECORD_VERSION='5.1' bundle exec rake test
25
+
26
+ if [[ ! $ruby_v =~ '2.2.0' ]]; then
27
+ ACTIVERECORD_VERSION='5.2' bundle update
28
+ ACTIVERECORD_VERSION='5.2' bundle exec rake test
29
+ fi
30
+
31
+ if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]; then
32
+ ACTIVERECORD_VERSION='6.0' bundle update
33
+ ACTIVERECORD_VERSION='6.0' bundle exec rake test
34
+ fi
@@ -1,6 +1,30 @@
1
1
  ---
2
2
  language: ruby
3
- cache: bundler
3
+
4
+ sudo: false
5
+
4
6
  rvm:
5
- - 2.6.5
6
- before_install: gem install bundler -v 2.1.4
7
+ - 2.2.0
8
+ - 2.3.0
9
+ - 2.4.0
10
+ - 2.5.0
11
+ - 2.6.0
12
+ - 2.7.0
13
+
14
+ cache: bundler
15
+
16
+ before_install:
17
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
18
+ - gem install bundler -v '< 2'
19
+
20
+ install: bundle install --jobs=3 --retry=3
21
+
22
+ before_script:
23
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
24
+ - chmod +x ./cc-test-reporter
25
+ - "./cc-test-reporter before-build"
26
+
27
+ script: "./.travis.sh"
28
+
29
+ after_success:
30
+ - "./cc-test-reporter after-build -t simplecov"
data/Gemfile CHANGED
@@ -3,12 +3,38 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in u-observers.gemspec
4
4
  gemspec
5
5
 
6
- gem 'rake', '~> 12.0'
7
- gem 'minitest', '~> 5.0'
6
+ activerecord_version = ENV.fetch('ACTIVERECORD_VERSION', '6.1')
7
+
8
+ activerecord = case activerecord_version
9
+ when '3.2' then '3.2.22'
10
+ when '4.0' then '4.0.13'
11
+ when '4.1' then '4.1.16'
12
+ when '4.2' then '4.2.11'
13
+ when '5.0' then '5.0.7'
14
+ when '5.1' then '5.1.7'
15
+ when '5.2' then '5.2.3'
16
+ when '6.0' then '6.0.3'
17
+ end
18
+
19
+ simplecov_version =
20
+ case RUBY_VERSION
21
+ when /\A2.[23]/ then '~> 0.17.1'
22
+ when /\A2.4/ then '~> 0.18.5'
23
+ else '~> 0.19'
24
+ end
8
25
 
9
26
  group :test do
10
- gem 'activerecord', require: 'active_record'
11
- gem 'sqlite3'
27
+ gem 'minitest', activerecord_version < '4.1' ? '~> 4.2' : '~> 5.0'
28
+ gem 'simplecov', simplecov_version, require: false
29
+
30
+ if activerecord
31
+ sqlite3 =
32
+ case activerecord
33
+ when /\A6\.0/, nil then '~> 1.4.0'
34
+ else '~> 1.3.0'
35
+ end
12
36
 
13
- gem 'simplecov', '~> 0.19', require: false
37
+ gem 'sqlite3', sqlite3
38
+ gem 'activerecord', activerecord, require: 'active_record'
39
+ end
14
40
  end
data/README.md CHANGED
@@ -1,28 +1,512 @@
1
- # Micro::Observers
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/micro/observers`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- ## Installation
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
- Add this line to your application's Gemfile:
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? 🇧🇷&nbsp;🇵🇹 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 'micro-observers'
62
+ gem 'u-observers'
13
63
  ```
14
64
 
15
- And then execute:
65
+ # Compatibility
16
66
 
17
- $ bundle install
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
- Or install it yourself as:
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
- $ gem install u-observers
75
+ [⬆️ &nbsp; Back to Top](#table-of-contents-)
22
76
 
23
77
  ## Usage
24
78
 
25
- TODO: Write usage instructions here
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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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
+ [⬆️ &nbsp; 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(&notify_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
+ [⬆️ &nbsp; Back to Top](#table-of-contents-)
26
510
 
27
511
  ## Development
28
512