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
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).
|