u-observers 2.3.0 → 3.0.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.
data/README.md CHANGED
@@ -1,38 +1,38 @@
1
1
  <p align="center">
2
2
  <h1 align="center">👀 μ-observers</h1>
3
3
  <p align="center"><i>Simple and powerful implementation of the observer pattern.</i></p>
4
- <br>
5
4
  </p>
6
5
 
7
6
  <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
7
  <a href="https://rubygems.org/gems/u-observers">
11
8
  <img alt="Gem" src="https://img.shields.io/gem/v/u-observers.svg?style=flat-square">
12
9
  </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">
10
+ <a href="https://github.com/u-gems/u-observers/actions/workflows/ci.yml">
11
+ <img alt="Build Status" src="https://github.com/u-gems/u-observers/actions/workflows/ci.yml/badge.svg">
24
12
  </a>
13
+ <br/>
14
+ <a href="https://qlty.sh/gh/u-gems/projects/u-observers"><img src="https://qlty.sh/gh/u-gems/projects/u-observers/maintainability.svg" alt="Maintainability" /></a>
15
+ <a href="https://qlty.sh/gh/u-gems/projects/u-observers"><img src="https://qlty.sh/gh/u-gems/projects/u-observers/coverage.svg" alt="Code Coverage" /></a>
16
+ <br/>
17
+ <img src="https://img.shields.io/badge/Ruby%20%3E%3D%202.7%2C%20%3C%3D%20Head-ruby.svg?colorA=444&colorB=333" alt="Ruby">
18
+ <img src="https://img.shields.io/badge/Rails%20%3E%3D%206.0%2C%20%3C%3D%20Edge-rails.svg?colorA=444&colorB=333" alt="Rails">
25
19
  </p>
26
20
 
21
+ > [!IMPORTANT]
22
+ > **No breaking API changes — ever.** `u-observers` is a dependency-free gem that many projects rely on (directly or transitively). Its role is to remain a stable, backward-compatible foundation — every change keeps existing code working.
23
+ >
24
+ > Major version bumps signal only that a Ruby or Rails version was dropped from the supported matrix — per SemVer, a dependency-floor change. Your code keeps working.
25
+
27
26
  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
27
 
29
28
  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
29
 
31
30
  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
31
 
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).
32
+ > **Note:** Você entende português? 🇧🇷&nbsp;🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/u-gems/u-observers/blob/main/README.pt-BR.md).
34
33
 
35
34
  # Table of contents <!-- omit in toc -->
35
+
36
36
  - [Installation](#installation)
37
37
  - [Compatibility](#compatibility)
38
38
  - [Usage](#usage)
@@ -46,13 +46,14 @@ Because of this issue, I decided to create a gem that encapsulates the pattern w
46
46
  - [`observers.attach(*args, perform_once: true)`](#observersattachargs-perform_once-true)
47
47
  - [`observers.once(event:, call:, ...)`](#observersonceevent-call-)
48
48
  - [Defining observers using blocks](#defining-observers-using-blocks)
49
- - [order.observers.on()](#orderobserverson)
50
- - [order.observers.on()](#orderobserverson-1)
49
+ - [`observers.on()`](#observerson)
50
+ - [`observers.once()`](#observersonce)
51
51
  - [Replacing a block by a `lambda`/`proc`](#replacing-a-block-by-a-lambdaproc)
52
52
  - [Detaching observers](#detaching-observers)
53
53
  - [ActiveRecord and ActiveModel integrations](#activerecord-and-activemodel-integrations)
54
- - [notify_observers_on()](#notify_observers_on)
55
- - [notify_observers()](#notify_observers)
54
+ - [`.notify_observers_on()`](#notify_observers_on)
55
+ - [Attaching observers at the class level (`.notify_observers!()`)](#attaching-observers-at-the-class-level-notify_observers)
56
+ - [`.notify_observers()`](#notify_observers)
56
57
  - [Development](#development)
57
58
  - [Contributing](#contributing)
58
59
  - [License](#license)
@@ -68,11 +69,24 @@ gem 'u-observers'
68
69
 
69
70
  # Compatibility
70
71
 
71
- | u-observers | branch | ruby | activerecord |
72
- | ----------- | ------- | -------- | ------------- |
73
- | unreleased | main | >= 2.2.0 | >= 3.2, < 6.1 |
74
- | 2.3.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
75
- | 1.0.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
72
+ | u-observers | branch | ruby | activerecord |
73
+ | ----------- | ------ | -------- | --------------- |
74
+ | 3.0.0 | main | >= 2.7.0 | >= 6.0, <= Edge |
75
+ | 2.3.0 | v2.x | >= 2.2.0 | >= 3.2, < 6.1 |
76
+ | 1.0.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
77
+
78
+ This library is tested (CI matrix) against:
79
+
80
+ | Ruby / Rails | 6.0 | 6.1 | 7.0 | 7.1 | 7.2 | 8.0 | 8.1 | Edge |
81
+ | ------------ | --- | --- | --- | --- | --- | --- | --- | ---- |
82
+ | 2.7 | ✅ | ✅ | ✅ | ✅ | | | | |
83
+ | 3.0 | ✅ | ✅ | ✅ | ✅ | | | | |
84
+ | 3.1 | | | ✅ | ✅ | ✅ | | | |
85
+ | 3.2 | | | ✅ | ✅ | ✅ | ✅ | | |
86
+ | 3.3 | | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
87
+ | 3.4 | | | | | ✅ | ✅ | ✅ | ✅ |
88
+ | 4.x | | | | | | | ✅ | ✅ |
89
+ | Head | | | | | | | ✅ | ✅ |
76
90
 
77
91
  > **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).
78
92
 
@@ -219,8 +233,8 @@ order.observers.notify(:changed, data: 1)
219
233
  The `Micro::Observers::Event` is the event payload. Follow below all of its properties:
220
234
 
221
235
  - `#name` will be the broadcasted event.
222
- - `#subject` will be the observed subject.
223
- - `#context` will be [the context data](#sharing-a-context-with-your-observers) that was defined in the moment that you attach the observer.
236
+ - `#subject` will be the observed object.
237
+ - `#context` will be [the context data](#sharing-a-context-with-your-observers) that was defined at the moment that you attach the observer.
224
238
  - `#data` will be [the value that was shared in the observers' notification](#sharing-data-when-notifying-the-observers).
225
239
  - `#ctx` is an alias for the `#context` method.
226
240
  - `#subj` is an alias for the `#subject` method.
@@ -231,9 +245,10 @@ The `Micro::Observers::Event` is the event payload. Follow below all of its prop
231
245
 
232
246
  The `observers.on()` method enables you to attach a callable as an observer.
233
247
 
234
- Usually, a callable has a well-defined responsibility (do only one thing), because of this, it tends to be more [SRP (Single-responsibility principle)](https://en.wikipedia.org/wiki/Single-responsibility_principle). friendly than a conventional observer (that could have N methods to respond to different kinds of notification).
248
+ Usually, a callable has a well-defined responsibility (do only one thing), because of this, it tends to be more [SRP (Single-responsibility principle)](https://en.wikipedia.org/wiki/Single-responsibility_principle) friendly than a conventional observer (that could have N methods to respond to different kinds of notification).
235
249
 
236
250
  This method receives the below options:
251
+
237
252
  1. `:event` the expected event name.
238
253
  2. `:call` the callable object itself.
239
254
  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.
@@ -381,9 +396,9 @@ order.cancel! # Nothing will happen because there aren't observers.
381
396
 
382
397
  ### Defining observers using blocks
383
398
 
384
- The methods `#on()` and `#once()` can receive the event name (a `symbol`) and a block to define observers.
399
+ The methods `#on()` and `#once()` can receive an event (`symbol`) and a block to define observers.
385
400
 
386
- #### order.observers.on()
401
+ #### `observers.on()`
387
402
 
388
403
  ```ruby
389
404
  class Order
@@ -406,7 +421,7 @@ order.cancel! # The order #(70301497466060) has been canceled.
406
421
  order.observers.some? # true
407
422
  ```
408
423
 
409
- #### order.observers.on()
424
+ #### `observers.once()`
410
425
 
411
426
  ```ruby
412
427
  class Order
@@ -431,7 +446,7 @@ order.observers.some? # false
431
446
 
432
447
  #### Replacing a block by a `lambda`/`proc`
433
448
 
434
- Ruby allows you to replace any block with a `lambda`/`proc`. e.g.
449
+ Ruby allows you to replace any block with a `lambda`/`proc`. So, it will be possible to use this kind of feature to define your observers. e.g.
435
450
 
436
451
  ```ruby
437
452
  class Order
@@ -475,11 +490,12 @@ module OrderNotifications
475
490
  end
476
491
 
477
492
  order = Order.new
493
+ order.observers.on(:canceled) { |_event| }
478
494
  order.observers.on(event: :canceled, call: NotifyAfterCancel)
479
495
  order.observers.attach(OrderNotifications)
480
496
 
481
497
  order.observers.some? # true
482
- order.observers.count # 2
498
+ order.observers.count # 3
483
499
 
484
500
  order.observers.off(:canceled) # removing the callable (NotifyAfterCancel).
485
501
  order.observers.some? # true
@@ -497,15 +513,16 @@ order.observers.count # 0
497
513
  To make use of this feature you need to require an additional module.
498
514
 
499
515
  Gemfile example:
516
+
500
517
  ```ruby
501
518
  gem 'u-observers', require: 'u-observers/for/active_record'
502
519
  ```
503
520
 
504
521
  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:
505
522
 
506
- #### notify_observers_on()
523
+ #### `.notify_observers_on()`
507
524
 
508
- The `notify_observers_on` allows you to define one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your object observers.
525
+ The `notify_observers_on` allows you to define one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your observers.
509
526
 
510
527
  ```ruby
511
528
  class Post < ActiveRecord::Base
@@ -545,9 +562,64 @@ end
545
562
 
546
563
  [⬆️ &nbsp; Back to Top](#table-of-contents-)
547
564
 
548
- #### notify_observers()
565
+ #### Attaching observers at the class level (`.notify_observers!()`)
566
+
567
+ While `notify_observers_on` only wires the callback to a broadcast (you still `attach` the observers on every instance), `notify_observers!` also **binds the observers to the model at the class level** through the required `with:` option — so you never call `observers.attach` yourself. The `event:` option names the callback to hook; use `context:` to forward a context to those observers, and pass any extra option (e.g. `on:`) straight through to the underlying callback.
568
+
569
+ ```ruby
570
+ class Post < ActiveRecord::Base
571
+ include ::Micro::Observers::For::ActiveRecord
572
+
573
+ # Attach TitlePrinter (and TitlePrinterWithContext) on every after_commit
574
+ # triggered by an update — no per-instance `observers.attach` needed.
575
+ notify_observers!(
576
+ on: :update,
577
+ with: [TitlePrinter, TitlePrinterWithContext],
578
+ event: :after_commit,
579
+ context: { from: 'class-level' }
580
+ )
581
+
582
+ # Equivalent to:
583
+ #
584
+ # after_commit(on: :update) do |record|
585
+ # record.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'class-level' })
586
+ # record.observers.subject_changed!
587
+ # record.observers.notify(:after_commit)
588
+ # end
589
+ end
590
+
591
+ Post.transaction { Post.create(title: 'Hello world') } # nothing — `on: :update`
592
+
593
+ post = Post.first
594
+ Post.transaction { post.update(title: 'Hello again') }
595
+ # Title: Hello again
596
+ # Title: Hello again (from: class-level)
597
+ ```
598
+
599
+ > **Note**: `event:` and `with:` are required (`with:` accepts a single observer or an array). Without observers to attach, use `notify_observers_on` instead.
600
+
601
+ The declared observers are introspectable and detachable at the class level:
602
+
603
+ ```ruby
604
+ Post.observers_to_notify
605
+ # { after_commit: [TitlePrinter, TitlePrinterWithContext] }
606
+
607
+ # Stop notifying a given observer (from every callback, or scope it with `from:`)
608
+ Post.detach_observers_to_notify(TitlePrinterWithContext)
609
+ # { after_commit: [TitlePrinter] }
610
+
611
+ Post.detach_observers_to_notify(TitlePrinter, from: :after_commit)
612
+ # {}
613
+
614
+ # With no observers, clears the callback(s) entirely:
615
+ Post.detach_observers_to_notify(from: :after_commit)
616
+ ```
617
+
618
+ [⬆️ &nbsp; Back to Top](#table-of-contents-)
619
+
620
+ #### `.notify_observers()`
549
621
 
550
- The `notify_observers` allows you to define one or more *events*, that will be used to notify after the execution of some `ActiveModel`/`ActiveRecord` callback.
622
+ 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.
551
623
 
552
624
  ```ruby
553
625
  class Post < ActiveRecord::Base
@@ -563,12 +635,6 @@ class Post < ActiveRecord::Base
563
635
  # end
564
636
  end
565
637
 
566
- module TitlePrinter
567
- def self.transaction_completed(post)
568
- puts("Title: #{post.title}")
569
- end
570
- end
571
-
572
638
  module TitlePrinterWithContext
573
639
  def self.transaction_completed(post, event)
574
640
  puts("Title: #{post.title} (from: #{event.ctx[:from]})")
@@ -577,7 +643,11 @@ end
577
643
 
578
644
  Post.transaction do
579
645
  post = Post.new(title: 'Olá mundo')
580
- post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #7' })
646
+
647
+ post.observers.on(:transaction_completed) { |event| puts("Title: #{event.subject.title}") }
648
+
649
+ post.observers.attach(TitlePrinterWithContext, context: { from: 'example #7' })
650
+
581
651
  post.save
582
652
  end
583
653
  # The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):
@@ -597,8 +667,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
597
667
 
598
668
  ## Contributing
599
669
 
600
- Bug reports and pull requests are welcome on GitHub at https://github.com/serradura/u-observers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).
601
-
670
+ Bug reports and pull requests are welcome on GitHub at https://github.com/u-gems/u-observers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/u-gems/u-observers/blob/master/CODE_OF_CONDUCT.md).
602
671
 
603
672
  ## License
604
673
 
@@ -606,4 +675,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
606
675
 
607
676
  ## Code of Conduct
608
677
 
609
- Everyone interacting in the `Micro::Observers` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).
678
+ Everyone interacting in the `Micro::Observers` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/u-gems/u-observers/blob/master/CODE_OF_CONDUCT.md).
data/README.pt-BR.md CHANGED
@@ -1,29 +1,28 @@
1
1
  <p align="center">
2
2
  <h1 align="center">👀 μ-observers</h1>
3
3
  <p align="center"><i>Implementação simples e poderosa do padrão observer.</i></p>
4
- <br>
5
4
  </p>
6
5
 
7
6
  <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
7
  <a href="https://rubygems.org/gems/u-observers">
11
8
  <img alt="Gem" src="https://img.shields.io/gem/v/u-observers.svg?style=flat-square">
12
9
  </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">
10
+ <a href="https://github.com/u-gems/u-observers/actions/workflows/ci.yml">
11
+ <img alt="Build Status" src="https://github.com/u-gems/u-observers/actions/workflows/ci.yml/badge.svg">
24
12
  </a>
13
+ <br/>
14
+ <a href="https://qlty.sh/gh/u-gems/projects/u-observers"><img src="https://qlty.sh/gh/u-gems/projects/u-observers/maintainability.svg" alt="Maintainability" /></a>
15
+ <a href="https://qlty.sh/gh/u-gems/projects/u-observers"><img src="https://qlty.sh/gh/u-gems/projects/u-observers/coverage.svg" alt="Code Coverage" /></a>
16
+ <br/>
17
+ <img src="https://img.shields.io/badge/Ruby%20%3E%3D%202.7%2C%20%3C%3D%20Head-ruby.svg?colorA=444&colorB=333" alt="Ruby">
18
+ <img src="https://img.shields.io/badge/Rails%20%3E%3D%206.0%2C%20%3C%3D%20Edge-rails.svg?colorA=444&colorB=333" alt="Rails">
25
19
  </p>
26
20
 
21
+ > [!IMPORTANT]
22
+ > **Nenhuma mudança que quebre a API — nunca.** `u-observers` é uma gem sem dependências da qual muitos projetos dependem (direta ou transitivamente). Seu papel é permanecer uma base estável e retrocompatível — toda mudança mantém o código existente funcionando.
23
+ >
24
+ > Saltos de versão major sinalizam apenas que uma versão de Ruby ou Rails foi removida da matriz suportada — pela SemVer, uma mudança no piso de dependências. Seu código continua funcionando.
25
+
27
26
  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
27
 
29
28
  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.
@@ -45,13 +44,14 @@ Por causa desse problema, decidi criar uma gem que encapsula o padrão sem alter
45
44
  - [`observers.attach(*args, perform_once: true)`](#observersattachargs-perform_once-true)
46
45
  - [`observers.once(event:, call:, ...)`](#observersonceevent-call-)
47
46
  - [Definindo observers com blocos](#definindo-observers-com-blocos)
48
- - [order.observers.on()](#orderobserverson)
49
- - [order.observers.on()](#orderobserverson-1)
47
+ - [`observers.on()`](#observerson)
48
+ - [`observers.once()`](#observersonce)
50
49
  - [Substituindo um bloco por um `lambda`/`proc`](#substituindo-um-bloco-por-um-lambdaproc)
51
50
  - [Desanexando observers](#desanexando-observers)
52
51
  - [Integrações ActiveRecord e ActiveModel](#integrações-activerecord-e-activemodel)
53
- - [notify_observers_on()](#notify_observers_on)
54
- - [notify_observers()](#notify_observers)
52
+ - [`.notify_observers_on()`](#notify_observers_on)
53
+ - [Anexando observers no nível da classe (`.notify_observers!()`)](#anexando-observers-no-nível-da-classe-notify_observers)
54
+ - [`.notify_observers()`](#notify_observers)
55
55
  - [Desenvolvimento](#desenvolvimento)
56
56
  - [Contribuindo](#contribuindo)
57
57
  - [License](#license)
@@ -67,11 +67,24 @@ gem 'u-observers'
67
67
 
68
68
  # Compatibilidade
69
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 |
70
+ | u-observers | branch | ruby | activerecord |
71
+ | ----------- | ------ | -------- | --------------- |
72
+ | 3.0.0 | main | >= 2.7.0 | >= 6.0, <= Edge |
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
+ Esta biblioteca é testada (CI matrix) contra:
77
+
78
+ | Ruby / Rails | 6.0 | 6.1 | 7.0 | 7.1 | 7.2 | 8.0 | 8.1 | Edge |
79
+ | ------------ | --- | --- | --- | --- | --- | --- | --- | ---- |
80
+ | 2.7 | ✅ | ✅ | ✅ | ✅ | | | | |
81
+ | 3.0 | ✅ | ✅ | ✅ | ✅ | | | | |
82
+ | 3.1 | | | ✅ | ✅ | ✅ | | | |
83
+ | 3.2 | | | ✅ | ✅ | ✅ | ✅ | | |
84
+ | 3.3 | | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
85
+ | 3.4 | | | | | ✅ | ✅ | ✅ | ✅ |
86
+ | 4.x | | | | | | | ✅ | ✅ |
87
+ | Head | | | | | | | ✅ | ✅ |
75
88
 
76
89
  > **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
90
 
@@ -158,7 +171,7 @@ order.observers.notify
158
171
 
159
172
  ### Compartilhando um contexto com seus observadores
160
173
 
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*.
174
+ 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
175
 
163
176
  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
177
 
@@ -190,7 +203,7 @@ order.cancel!
190
203
 
191
204
  ### Compartilhando dados ao notificar os observadores
192
205
 
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.
206
+ 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
207
 
195
208
  ```ruby
196
209
  class Order
@@ -217,12 +230,13 @@ order.observers.notify(:changed, data: 1)
217
230
  ### O que é `Micro::Observers::Event`?
218
231
 
219
232
  O `Micro::Observers::Event` é o payload do evento. Veja abaixo todas as suas propriedades:
233
+
220
234
  - `#name` será o evento transmitido.
221
235
  - `#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*.
236
+ - `#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
237
  - `#data` será [o valor compartilhado na notificação dos observadores](#compartilhando-dados-ao-notificar-os-observadores).
224
238
  - `#ctx` é um apelido para o método `#context`.
225
- - `#subj` é um *alias* para o método `#subject`.
239
+ - `#subj` é um _alias_ para o método `#subject`.
226
240
 
227
241
  [⬆️ Voltar para o índice](#índice-)
228
242
 
@@ -233,10 +247,11 @@ O método `observers.on()` permite que você anexe um callable (objeto que respo
233
247
  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
248
 
235
249
  Este método recebe as opções abaixo:
250
+
236
251
  1. `:event` o nome do evento esperado.
237
252
  2. `:call` o próprio callable.
238
253
  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*.
254
+ 4. `:context` serão os dados de contexto que foram definidos no momento em que você anexa o _observer_.
240
255
 
241
256
  ```ruby
242
257
  class Person
@@ -280,7 +295,7 @@ person.name = 'Coutinho'
280
295
 
281
296
  ### Chamando os observadores
282
297
 
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`.
298
+ 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
299
 
285
300
  ```ruby
286
301
  class Order
@@ -383,7 +398,7 @@ order.cancel! # Nothing will happen because there aren't observers.
383
398
 
384
399
  Os métodos `#on()` e `#once()` podem receber um evento (a `symbol`) e um bloco para definir observers.
385
400
 
386
- #### order.observers.on()
401
+ #### `observers.on()`
387
402
 
388
403
  ```ruby
389
404
  class Order
@@ -406,7 +421,7 @@ order.cancel! # The order #(70301497466060) has been canceled.
406
421
  order.observers.some? # true
407
422
  ```
408
423
 
409
- #### order.observers.on()
424
+ #### `observers.once()`
410
425
 
411
426
  ```ruby
412
427
  class Order
@@ -431,7 +446,7 @@ order.observers.some? # false
431
446
 
432
447
  #### Substituindo um bloco por um `lambda`/`proc`
433
448
 
434
- Ruby permite que você substitua qualquer bloco com um `lambda`/`proc`. Exemplo:
449
+ Ruby permite que você substitua qualquer bloco com um `lambda`/`proc`. Portanto, será possível usar este tipo de recurso para definir seus observers. Exemplo:
435
450
 
436
451
  ```ruby
437
452
  class Order
@@ -475,11 +490,12 @@ module OrderNotifications
475
490
  end
476
491
 
477
492
  order = Order.new
493
+ order.observers.on(:canceled) { |_event| }
478
494
  order.observers.on(event: :canceled, call: NotifyAfterCancel)
479
495
  order.observers.attach(OrderNotifications)
480
496
 
481
497
  order.observers.some? # true
482
- order.observers.count # 2
498
+ order.observers.count # 3
483
499
 
484
500
  order.observers.off(:canceled) # removing the callable (NotifyAfterCancel).
485
501
  order.observers.some? # true
@@ -497,15 +513,16 @@ order.observers.count # 0
497
513
  Para fazer uso deste recurso, você precisa de um módulo adicional.
498
514
 
499
515
  Exemplo de Gemfile:
516
+
500
517
  ```ruby
501
518
  gem 'u-observers', require: 'u-observers/for/active_record'
502
519
  ```
503
520
 
504
521
  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
522
 
506
- #### notify_observers_on()
523
+ #### `.notify_observers_on()`
507
524
 
508
- O `notify_observers_on` permite que você defina um ou mais callbacks do `ActiveModel`/`ActiveRecord`, que serão usados ​​para notificar seus *observers*.
525
+ O `notify_observers_on` permite que você defina um ou mais callbacks do `ActiveModel`/`ActiveRecord`, que serão usados ​​para notificar seus _observers_.
509
526
 
510
527
  ```ruby
511
528
  class Post < ActiveRecord::Base
@@ -546,7 +563,62 @@ end
546
563
 
547
564
  [⬆️ Voltar para o índice](#índice-)
548
565
 
549
- #### notify_observers()
566
+ #### Anexando observers no nível da classe (`.notify_observers!()`)
567
+
568
+ Enquanto o `notify_observers_on` apenas conecta o callback a um broadcast (você ainda precisa chamar `attach` em cada instância), o `notify_observers!` também **vincula os _observers_ ao modelo no nível da classe** através da opção obrigatória `with:` — assim você nunca chama `observers.attach`. A opção `event:` nomeia o callback a ser usado; use `context:` para encaminhar um contexto para esses _observers_, e passe qualquer opção extra (por exemplo, `on:`) diretamente para o callback subjacente.
569
+
570
+ ```ruby
571
+ class Post < ActiveRecord::Base
572
+ include ::Micro::Observers::For::ActiveRecord
573
+
574
+ # Anexa TitlePrinter (e TitlePrinterWithContext) em todo after_commit
575
+ # disparado por um update — sem precisar de `observers.attach` por instância.
576
+ notify_observers!(
577
+ on: :update,
578
+ with: [TitlePrinter, TitlePrinterWithContext],
579
+ event: :after_commit,
580
+ context: { from: 'class-level' }
581
+ )
582
+
583
+ # Equivalente a:
584
+ #
585
+ # after_commit(on: :update) do |record|
586
+ # record.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'class-level' })
587
+ # record.observers.subject_changed!
588
+ # record.observers.notify(:after_commit)
589
+ # end
590
+ end
591
+
592
+ Post.transaction { Post.create(title: 'Hello world') } # nada — `on: :update`
593
+
594
+ post = Post.first
595
+ Post.transaction { post.update(title: 'Hello again') }
596
+ # Title: Hello again
597
+ # Title: Hello again (de: class-level)
598
+ ```
599
+
600
+ > **Nota**: `event:` e `with:` são obrigatórios (`with:` aceita um único _observer_ ou um array). Sem _observers_ para anexar, use o `notify_observers_on`.
601
+
602
+ Os _observers_ declarados são inspecionáveis e removíveis no nível da classe:
603
+
604
+ ```ruby
605
+ Post.observers_to_notify
606
+ # { after_commit: [TitlePrinter, TitlePrinterWithContext] }
607
+
608
+ # Para de notificar um dado observer (de todos os callbacks, ou use `from:` para limitar)
609
+ Post.detach_observers_to_notify(TitlePrinterWithContext)
610
+ # { after_commit: [TitlePrinter] }
611
+
612
+ Post.detach_observers_to_notify(TitlePrinter, from: :after_commit)
613
+ # {}
614
+
615
+ # Sem observers, remove o(s) callback(s) por completo:
616
+ Post.detach_observers_to_notify(from: :after_commit)
617
+ ```
618
+
619
+ [⬆️ Voltar para o índice](#índice-)
620
+
621
+ #### `.notify_observers()`
550
622
 
551
623
  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
624
 
@@ -564,12 +636,6 @@ class Post < ActiveRecord::Base
564
636
  # end
565
637
  end
566
638
 
567
- module TitlePrinter
568
- def self.transaction_completed(post)
569
- puts("Title: #{post.title}")
570
- end
571
- end
572
-
573
639
  module TitlePrinterWithContext
574
640
  def self.transaction_completed(post, event)
575
641
  puts("Title: #{post.title} (from: #{event.ctx[:from]})")
@@ -578,7 +644,11 @@ end
578
644
 
579
645
  Post.transaction do
580
646
  post = Post.new(title: 'Olá mundo')
581
- post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #7' })
647
+
648
+ post.observers.on(:transaction_completed) { |event| puts("Title: #{event.subject.title}") }
649
+
650
+ post.observers.attach(TitlePrinterWithContext, context: { from: 'example #7' })
651
+
582
652
  post.save
583
653
  end
584
654
 
@@ -599,7 +669,7 @@ Para instalar esta gem em sua máquina local, execute `bundle exec rake install`
599
669
 
600
670
  ## Contribuindo
601
671
 
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).
672
+ Reportar bugs e solicitações de pull-requests são bem-vindos no GitHub em https://github.com/u-gems/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/u-gems/u-observers/blob/master/CODE_OF_CONDUCT.md).
603
673
 
604
674
  ## License
605
675
 
@@ -607,4 +677,4 @@ A gem está disponível como código aberto sob os termos da [Licença MIT](http
607
677
 
608
678
  ## Código de conduta
609
679
 
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).
680
+ 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/u-gems/u-observers/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -7,4 +7,34 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList['test/**/*_test.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ require 'appraisal/task'
11
+
12
+ Appraisal::Task.new
13
+
14
+ desc 'Run the full test suite against every supported Rails version'
15
+ task :matrix do
16
+ appraisals =
17
+ if RUBY_VERSION < '3.1'
18
+ %w[rails-6-0 rails-6-1 rails-7-0 rails-7-1]
19
+ elsif RUBY_VERSION < '3.2'
20
+ %w[rails-7-0 rails-7-1 rails-7-2]
21
+ elsif RUBY_VERSION < '3.3'
22
+ %w[rails-7-0 rails-7-1 rails-7-2 rails-8-0]
23
+ elsif RUBY_VERSION < '3.4'
24
+ %w[rails-7-0 rails-7-1 rails-7-2 rails-8-0 rails-8-1 rails-edge]
25
+ elsif RUBY_VERSION < '4.0'
26
+ %w[rails-7-2 rails-8-0 rails-8-1 rails-edge]
27
+ else
28
+ %w[rails-8-1 rails-edge]
29
+ end
30
+
31
+ # Baseline (no activerecord)
32
+ sh 'bundle exec rake test'
33
+
34
+ # Each activerecord appraisal
35
+ appraisals.each do |appraisal|
36
+ sh "bundle exec appraisal #{appraisal} rake test"
37
+ end
38
+ end
39
+
40
+ task default: :test
data/bin/matrix ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ ruby -v
7
+
8
+ rm -f Gemfile.lock
9
+
10
+ bundle install
11
+
12
+ bundle exec appraisal update
13
+
14
+ bundle exec rake matrix
15
+
16
+ ruby -v