u-observers 1.0.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/README.pt-BR.md
ADDED
@@ -0,0 +1,610 @@
|
|
1
|
+
<p align="center">
|
2
|
+
<h1 align="center">👀 μ-observers</h1>
|
3
|
+
<p align="center"><i>Implementação simples e poderosa do padrão observer.</i></p>
|
4
|
+
<br>
|
5
|
+
</p>
|
6
|
+
|
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">
|
9
|
+
|
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>
|
13
|
+
|
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>
|
17
|
+
|
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
|
+
Esta gem implementa o padrão observer[[1]](https://en.wikipedia.org/wiki/Observer_pattern)[[2]](https://refactoring.guru/design-patterns/observer) (também conhecido como publicar/assinar). Ela fornece um mecanismo simples para um objeto informar um conjunto de objetos de terceiros interessados quando seu estado muda.
|
28
|
+
|
29
|
+
A biblioteca padrão do Ruby [tem uma abstração](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html) que permite usar esse padrão, mas seu design pode entrar em conflito com outras bibliotecas convencionais, como [`ActiveModel`/`ActiveRecord`](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed), que também tem o método [`changed`](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html#method-i-changed). Nesse caso, o comportamento ficaria comprometido por conta dessa sobrescrita de métodos.
|
30
|
+
|
31
|
+
Por causa desse problema, decidi criar uma gem que encapsula o padrão sem alterar tanto a implementação do objeto. O `Micro::Observers` inclui apenas um método de instância na classe de destino (sua instância será o sujeito/objeto observado).
|
32
|
+
|
33
|
+
# Índice <!-- omit in toc -->
|
34
|
+
|
35
|
+
- [Instalação](#instalação)
|
36
|
+
- [Compatibilidade](#compatibilidade)
|
37
|
+
- [Uso](#uso)
|
38
|
+
- [Compartilhando um contexto com seus observadores](#compartilhando-um-contexto-com-seus-observadores)
|
39
|
+
- [Compartilhando dados ao notificar os observadores](#compartilhando-dados-ao-notificar-os-observadores)
|
40
|
+
- [O que é `Micro::Observers::Event`?](#o-que-é-microobserversevent)
|
41
|
+
- [Usando um callable como um observador](#usando-um-callable-como-um-observador)
|
42
|
+
- [Chamando os observadores](#chamando-os-observadores)
|
43
|
+
- [Notificar observadores sem marcá-los como alterados](#notificar-observadores-sem-marcá-los-como-alterados)
|
44
|
+
- [Definindo observers que executam apenas uma vez](#definindo-observers-que-executam-apenas-uma-vez)
|
45
|
+
- [`observers.attach(*args, perform_once: true)`](#observersattachargs-perform_once-true)
|
46
|
+
- [`observers.once(event:, call:, ...)`](#observersonceevent-call-)
|
47
|
+
- [Definindo observers com blocos](#definindo-observers-com-blocos)
|
48
|
+
- [order.observers.on()](#orderobserverson)
|
49
|
+
- [order.observers.on()](#orderobserverson-1)
|
50
|
+
- [Substituindo um bloco por um `lambda`/`proc`](#substituindo-um-bloco-por-um-lambdaproc)
|
51
|
+
- [Desanexando observers](#desanexando-observers)
|
52
|
+
- [Integrações ActiveRecord e ActiveModel](#integrações-activerecord-e-activemodel)
|
53
|
+
- [notify_observers_on()](#notify_observers_on)
|
54
|
+
- [notify_observers()](#notify_observers)
|
55
|
+
- [Desenvolvimento](#desenvolvimento)
|
56
|
+
- [Contribuindo](#contribuindo)
|
57
|
+
- [License](#license)
|
58
|
+
- [Código de conduta](#código-de-conduta)
|
59
|
+
|
60
|
+
# Instalação
|
61
|
+
|
62
|
+
Adicione esta linha ao Gemfile da sua aplicação e execute `bundle install`:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
gem 'u-observers'
|
66
|
+
```
|
67
|
+
|
68
|
+
# Compatibilidade
|
69
|
+
|
70
|
+
| u-observers | branch | ruby | activerecord |
|
71
|
+
| ----------- | ------- | -------- | ------------- |
|
72
|
+
| unreleased | main | >= 2.2.0 | >= 3.2, < 6.1 |
|
73
|
+
| 2.3.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
74
|
+
| 1.0.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
75
|
+
|
76
|
+
> **Nota**: O ActiveRecord não é uma dependência, mas você pode adicionar um módulo para habilitar alguns métodos estáticos que foram projetados para serem usados com seus [callbacks](https://guides.rubyonrails.org/active_record_callbacks.html).
|
77
|
+
|
78
|
+
[⬆️ Voltar para o índice](#índice-)
|
79
|
+
|
80
|
+
## Uso
|
81
|
+
|
82
|
+
Qualquer classe com o `Micro::Observers` incluído pode notificar eventos para observadores anexados.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
require 'securerandom'
|
86
|
+
|
87
|
+
class Order
|
88
|
+
include Micro::Observers
|
89
|
+
|
90
|
+
attr_reader :code
|
91
|
+
|
92
|
+
def initialize
|
93
|
+
@code, @status = SecureRandom.alphanumeric, :draft
|
94
|
+
end
|
95
|
+
|
96
|
+
def canceled?
|
97
|
+
@status == :canceled
|
98
|
+
end
|
99
|
+
|
100
|
+
def cancel!
|
101
|
+
return self if canceled?
|
102
|
+
|
103
|
+
@status = :canceled
|
104
|
+
|
105
|
+
observers.subject_changed!
|
106
|
+
observers.notify(:canceled) and return self
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module OrderEvents
|
111
|
+
def self.canceled(order)
|
112
|
+
puts "The order #(#{order.code}) has been canceled."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
order = Order.new
|
117
|
+
#<Order:0x00007fb5dd8fce70 @code="X0o9yf1GsdQFvLR4", @status=:draft>
|
118
|
+
|
119
|
+
order.observers.attach(OrderEvents) # anexando vários observadores. Exemplo: observers.attach(A, B, C)
|
120
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[OrderEvents]>
|
121
|
+
|
122
|
+
order.canceled?
|
123
|
+
# false
|
124
|
+
|
125
|
+
order.cancel!
|
126
|
+
# A mensagem abaixo será impressa pelo observador (OrderEvents):
|
127
|
+
# The order #(X0o9yf1GsdQFvLR4) has been canceled
|
128
|
+
|
129
|
+
order.canceled?
|
130
|
+
# true
|
131
|
+
|
132
|
+
order.observers.detach(OrderEvents) # desanexando vários observadores. Exemplo: observers.detach(A, B, C)
|
133
|
+
# <#Micro::Observers::Set @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[]>
|
134
|
+
|
135
|
+
order.canceled?
|
136
|
+
# true
|
137
|
+
|
138
|
+
order.observers.subject_changed!
|
139
|
+
order.observers.notify(:canceled) # nada acontecerá, pois não há observadores vinculados (observers.attach)
|
140
|
+
```
|
141
|
+
|
142
|
+
**Destaques do exemplo anterior:**
|
143
|
+
|
144
|
+
Para evitar um comportamento indesejado, você precisa marcar o "subject" (sujeito) como alterado antes de notificar seus observadores sobre algum evento.
|
145
|
+
|
146
|
+
Você pode fazer isso ao usar o método `#subject_changed!`. Ele marcará automaticamente o sujeito como alterado.
|
147
|
+
|
148
|
+
Mas se você precisar aplicar alguma condicional para marcar uma mudança, você pode usar o método `#subject_changed`. Exemplo: `observers.subject_changed(name != new_name)`
|
149
|
+
|
150
|
+
O método `#notify` sempre requer um evento para fazer uma transmissão. Portanto, se você tentar usá-lo sem nenhum evento, você obterá uma exceção.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
order.observers.notify
|
154
|
+
# ArgumentError (no events (expected at least 1))
|
155
|
+
```
|
156
|
+
|
157
|
+
[⬆️ Voltar para o índice](#índice-)
|
158
|
+
|
159
|
+
### Compartilhando um contexto com seus observadores
|
160
|
+
|
161
|
+
Para compartilhar um valor de contexto (qualquer tipo de objeto Ruby) com um ou mais observadores, você precisará usar a palavra-chave `:context` como o último argumento do método `#attach`. Este recurso oferece a você uma oportunidade única de compartilhar um valor no momento de anexar um *observer*.
|
162
|
+
|
163
|
+
Quando o método do observer receber dois argumentos, o primeiro será o sujeito e o segundo uma instância `Micro::Observers::Event` que terá o valor do contexto.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class Order
|
167
|
+
include Micro::Observers
|
168
|
+
|
169
|
+
def cancel!
|
170
|
+
observers.subject_changed!
|
171
|
+
observers.notify(:canceled)
|
172
|
+
self
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
module OrderEvents
|
177
|
+
def self.canceled(order, event)
|
178
|
+
puts "The order #(#{order.object_id}) has been canceled. (from: #{event.context[:from]})" # event.ctx é um alias para event.context
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
order = Order.new
|
183
|
+
order.observers.attach(OrderEvents, context: { from: 'example #2' }) # anexando vários observadores. Exemplo: observers.attach(A, B, context: {hello:: world})
|
184
|
+
order.cancel!
|
185
|
+
# A mensagem abaixo será impressa pelo observador (OrderEvents):
|
186
|
+
# The order #(70196221441820) has been canceled. (from: example #2)
|
187
|
+
```
|
188
|
+
|
189
|
+
[⬆️ Voltar para o índice](#índice-)
|
190
|
+
|
191
|
+
### Compartilhando dados ao notificar os observadores
|
192
|
+
|
193
|
+
Como mencionado anteriormente, o [`event context`](#compartilhando-um-contexto-com-seus-observadores) é um valor armazenado quando você anexa seu *observer*. Mas, às vezes, será útil enviar alguns dados adicionais ao transmitir um evento aos seus *observers*. O `event data` dá a você esta oportunidade única de compartilhar algum valor no momento da notificação.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
class Order
|
197
|
+
include Micro::Observers
|
198
|
+
end
|
199
|
+
|
200
|
+
module OrderHandler
|
201
|
+
def self.changed(order, event)
|
202
|
+
puts "The order #(#{order.object_id}) received the number #{event.data} from #{event.ctx[:from]}."
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
order = Order.new
|
207
|
+
order.observers.attach(OrderHandler, context: { from: 'example #3' })
|
208
|
+
order.observers.subject_changed!
|
209
|
+
order.observers.notify(:changed, data: 1)
|
210
|
+
|
211
|
+
# A mensagem abaixo será impressa pelo observador (OrderHandler):
|
212
|
+
# The order #(70196221441820) received the number 1 from example #3.
|
213
|
+
```
|
214
|
+
|
215
|
+
[⬆️ Voltar para o índice](#índice-)
|
216
|
+
|
217
|
+
### O que é `Micro::Observers::Event`?
|
218
|
+
|
219
|
+
O `Micro::Observers::Event` é o payload do evento. Veja abaixo todas as suas propriedades:
|
220
|
+
- `#name` será o evento transmitido.
|
221
|
+
- `#subject` será o sujeito observado.
|
222
|
+
- `#context` serão [os dados de contexto](#compartilhando-um-contexto-com-seus-observadores) que foram definidos no momento em que você anexa o *observer*.
|
223
|
+
- `#data` será [o valor compartilhado na notificação dos observadores](#compartilhando-dados-ao-notificar-os-observadores).
|
224
|
+
- `#ctx` é um apelido para o método `#context`.
|
225
|
+
- `#subj` é um *alias* para o método `#subject`.
|
226
|
+
|
227
|
+
[⬆️ Voltar para o índice](#índice-)
|
228
|
+
|
229
|
+
### Usando um callable como um observador
|
230
|
+
|
231
|
+
O método `observers.on()` permite que você anexe um callable (objeto que responda ao método `call`) como um observador.
|
232
|
+
|
233
|
+
Normalmente, um callable tem uma responsabilidade bem definida (faz apenas uma coisa), por isso, tende a ser mais amigável com o [SRP (princípio de responsabilidade única)](https://en.wikipedia.org/wiki/Single-responsibility_principle) do que um observador convencional (que poderia ter N métodos para responder a diferentes tipos de notificação).
|
234
|
+
|
235
|
+
Este método recebe as opções abaixo:
|
236
|
+
1. `:event` o nome do evento esperado.
|
237
|
+
2. `:call` o próprio callable.
|
238
|
+
3. `:with` (opcional) pode definir o valor que será usado como argumento do objeto callable. Portanto, se for um `Proc`, uma instância de `Micro::Observers::Event` será recebida como o argumento `Proc` e sua saída será o argumento que pode ser chamado. Mas se essa opção não for definida, a instância `Micro::Observers::Event` será o argumento do callable.
|
239
|
+
4. `:context` serão os dados de contexto que foram definidos no momento em que você anexa o *observer*.
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
class Person
|
243
|
+
include Micro::Observers
|
244
|
+
|
245
|
+
attr_reader :name
|
246
|
+
|
247
|
+
def initialize(name)
|
248
|
+
@name = name
|
249
|
+
end
|
250
|
+
|
251
|
+
def name=(new_name)
|
252
|
+
return unless observers.subject_changed(new_name != @name)
|
253
|
+
|
254
|
+
@name = new_name
|
255
|
+
|
256
|
+
observers.notify(:name_has_been_changed)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
PrintPersonName = -> (data) do
|
261
|
+
puts("Person name: #{data.fetch(:person).name}, number: #{data.fetch(:number)}")
|
262
|
+
end
|
263
|
+
|
264
|
+
person = Person.new('Aristóteles')
|
265
|
+
|
266
|
+
person.observers.on(
|
267
|
+
event: :name_has_been_changed,
|
268
|
+
call: PrintPersonName,
|
269
|
+
with: -> event { {person: event.subject, number: event.context} },
|
270
|
+
context: rand
|
271
|
+
)
|
272
|
+
|
273
|
+
person.name = 'Coutinho'
|
274
|
+
|
275
|
+
# A mensagem abaixo será impressa pelo observador (PrintPersonName):
|
276
|
+
# Person name: Coutinho, number: 0.5018509191706862
|
277
|
+
```
|
278
|
+
|
279
|
+
[⬆️ Voltar para o índice](#índice-)
|
280
|
+
|
281
|
+
### Chamando os observadores
|
282
|
+
|
283
|
+
Você pode usar um callable (uma classe, módulo ou objeto que responda ao método `call`) para ser seu *observer*. Para fazer isso, você só precisa usar o método `#call` em vez de `#notify`.
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
class Order
|
287
|
+
include Micro::Observers
|
288
|
+
|
289
|
+
def cancel!
|
290
|
+
observers.subject_changed!
|
291
|
+
observers.call # na prática, este é um alias para observers.notify(:call)
|
292
|
+
self
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
OrderCancellation = -> (order) { puts "The order #(#{order.object_id}) has been canceled." }
|
297
|
+
|
298
|
+
order = Order.new
|
299
|
+
order.observers.attach(OrderCancellation)
|
300
|
+
order.cancel!
|
301
|
+
|
302
|
+
# A mensagem abaixo será impressa pelo observador (OrderCancellation):
|
303
|
+
# The order #(70196221441820) has been canceled.
|
304
|
+
```
|
305
|
+
|
306
|
+
> **Nota**: O `observers.call` pode receber um ou mais eventos, mas no caso de receber eventos/argumentos, o evento padrão (`call`) não será transmitido.
|
307
|
+
|
308
|
+
[⬆️ Voltar para o índice](#índice-)
|
309
|
+
|
310
|
+
### Notificar observadores sem marcá-los como alterados
|
311
|
+
|
312
|
+
Este recurso deve ser usado com cuidado!
|
313
|
+
|
314
|
+
Se você usar os métodos `#notify!` ou `#call!` você não precisará marcar observers com `#subject_changed`.
|
315
|
+
|
316
|
+
[⬆️ Voltar para o índice](#índice-)
|
317
|
+
|
318
|
+
### Definindo observers que executam apenas uma vez
|
319
|
+
|
320
|
+
Existem duas formas de anexar um observer e definir que ele executará apenas uma vez.
|
321
|
+
|
322
|
+
A primeira forma de fazer isso é passando a opção `perform_once: true` para o método `observers.attach()`. Exemplo:
|
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
|
+
A segunda forma de conseguir isso é usando o método `observers.once()` que tem a mesma API do [`observers.on()`](#usando-um-callable-como-um-observador). Mas a diferença é que o método `#once()` removerá o observer após a sua execução.
|
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
|
+
[⬆️ Voltar para o índice](#índice-)
|
381
|
+
|
382
|
+
### Definindo observers com blocos
|
383
|
+
|
384
|
+
Os métodos `#on()` e `#once()` podem receber um evento (a `symbol`) e um bloco para definir 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
|
+
#### Substituindo um bloco por um `lambda`/`proc`
|
433
|
+
|
434
|
+
Ruby permite que você substitua qualquer bloco com um `lambda`/`proc`. Exemplo:
|
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
|
+
[⬆️ Voltar para o índice](#índice-)
|
458
|
+
|
459
|
+
### Desanexando observers
|
460
|
+
|
461
|
+
Como mostrado no primeiro exemplo, você pode usar o `observers.detach()` para remove observers.
|
462
|
+
|
463
|
+
Mas, existe uma alternativa a esse método que permite remover objetos observers ou remover callables pelo nome de seus eventos. O método para fazer isso é: `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
|
+
[⬆️ Voltar para o índice](#índice-)
|
494
|
+
|
495
|
+
### Integrações ActiveRecord e ActiveModel
|
496
|
+
|
497
|
+
Para fazer uso deste recurso, você precisa de um módulo adicional.
|
498
|
+
|
499
|
+
Exemplo de Gemfile:
|
500
|
+
```ruby
|
501
|
+
gem 'u-observers', require: 'u-observers/for/active_record'
|
502
|
+
```
|
503
|
+
|
504
|
+
Este recurso irá expor módulos que podem ser usados para adicionar macros (métodos estáticos) que foram projetados para funcionar com os callbacks do `ActiveModel`/`ActiveRecord`. Exemplo:
|
505
|
+
|
506
|
+
#### notify_observers_on()
|
507
|
+
|
508
|
+
O `notify_observers_on` permite que você defina um ou mais callbacks do `ActiveModel`/`ActiveRecord`, que serão usados para notificar seus *observers*.
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
class Post < ActiveRecord::Base
|
512
|
+
include ::Micro::Observers::For::ActiveRecord
|
513
|
+
|
514
|
+
notify_observers_on(:after_commit) # usando vários callbacks. Exemplo: notificar_observadores_on(:before_save, :after_commit)
|
515
|
+
|
516
|
+
# O método acima faz o mesmo que o exemplo comentado abaixo.
|
517
|
+
#
|
518
|
+
# after_commit do | record |
|
519
|
+
# record.subject_changed!
|
520
|
+
# record.notify (:after_commit)
|
521
|
+
# end
|
522
|
+
end
|
523
|
+
|
524
|
+
module TitlePrinter
|
525
|
+
def self.after_commit(post)
|
526
|
+
puts "Title: #{post.title}"
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
module TitlePrinterWithContext
|
531
|
+
def self.after_commit(post, event)
|
532
|
+
puts "Title: #{post.title} (from: #{event.context[:from]})"
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
Post.transaction do
|
537
|
+
post = Post.new(title: 'Hello world')
|
538
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #6' })
|
539
|
+
post.save
|
540
|
+
end
|
541
|
+
|
542
|
+
# A mensagem abaixo será impressa pelos observadores (TitlePrinter, TitlePrinterWithContext):
|
543
|
+
# Title: Hello world
|
544
|
+
# Title: Hello world (de: exemplo # 6)
|
545
|
+
```
|
546
|
+
|
547
|
+
[⬆️ Voltar para o índice](#índice-)
|
548
|
+
|
549
|
+
#### notify_observers()
|
550
|
+
|
551
|
+
O `notify_observers` permite definir um ou mais eventos, que serão utilizados para notificar após a execução de algum callback do `ActiveModel`/`ActiveRecord`.
|
552
|
+
|
553
|
+
```ruby
|
554
|
+
class Post < ActiveRecord::Base
|
555
|
+
include ::Micro::Observers::For::ActiveRecord
|
556
|
+
|
557
|
+
after_commit(¬ify_observers(:transaction_completed))
|
558
|
+
|
559
|
+
# O método acima faz o mesmo que o exemplo comentado abaixo.
|
560
|
+
#
|
561
|
+
# after_commit do | record |
|
562
|
+
# record.subject_changed!
|
563
|
+
# record.notify (:transaction_completed)
|
564
|
+
# end
|
565
|
+
end
|
566
|
+
|
567
|
+
module TitlePrinter
|
568
|
+
def self.transaction_completed(post)
|
569
|
+
puts("Title: #{post.title}")
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
module TitlePrinterWithContext
|
574
|
+
def self.transaction_completed(post, event)
|
575
|
+
puts("Title: #{post.title} (from: #{event.ctx[:from]})")
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
Post.transaction do
|
580
|
+
post = Post.new(title: 'Olá mundo')
|
581
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #7' })
|
582
|
+
post.save
|
583
|
+
end
|
584
|
+
|
585
|
+
# A mensagem abaixo será impressa pelos observadores (TitlePrinter, TitlePrinterWithContext):
|
586
|
+
# Title: Olá mundo
|
587
|
+
# Title: Olá mundo (from: example # 5)
|
588
|
+
```
|
589
|
+
|
590
|
+
> **Observação**: você pode usar `include ::Micro::Observers::For::ActiveModel` se sua classe apenas fizer uso do `ActiveModel` e todos os exemplos anteriores funcionarão.
|
591
|
+
|
592
|
+
[⬆️ Voltar para o índice](#índice-)
|
593
|
+
|
594
|
+
## Desenvolvimento
|
595
|
+
|
596
|
+
Depois de verificar o repositório, execute `bin/setup` para instalar as dependências. Em seguida, execute `rake test` para executar os testes. Você também pode executar `bin/console` um prompt interativo que permitirá que você experimente.
|
597
|
+
|
598
|
+
Para instalar esta gem em sua máquina local, execute `bundle exec rake install`. Para lançar uma nova versão, atualize o número da versão em `version.rb` e execute `bundle exec rake release`, que criará uma tag git para a versão, envie os commits ao git e envie e envie o arquivo `.gem` para [rubygems.org](https://rubygems.org).
|
599
|
+
|
600
|
+
## Contribuindo
|
601
|
+
|
602
|
+
Reportar bugs e solicitações de pull-requests são bem-vindos no GitHub em https://github.com/serradura/u-observers. Este projeto pretende ser um espaço seguro e acolhedor para colaboração, e espera-se que os colaboradores sigam o [código de conduta](https://github.com/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).
|
603
|
+
|
604
|
+
## License
|
605
|
+
|
606
|
+
A gem está disponível como código aberto sob os termos da [Licença MIT](https://opensource.org/licenses/MIT).
|
607
|
+
|
608
|
+
## Código de conduta
|
609
|
+
|
610
|
+
Espera-se que todos que interagem nas bases de código do projeto `Micro::Observers`, rastreadores de problemas, salas de bate-papo e listas de discussão sigam o [código de conduta](https://github.com/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).
|