u-observers 0.9.0 → 1.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.
- checksums.yaml +4 -4
- data/.travis.sh +3 -0
- data/Gemfile +10 -8
- data/README.md +271 -11
- data/lib/micro/observers.rb +1 -20
- data/lib/micro/observers/events.rb +13 -5
- data/lib/micro/observers/for/active_model.rb +36 -0
- data/lib/micro/observers/for/active_record.rb +16 -0
- data/lib/micro/observers/manager.rb +52 -35
- data/lib/micro/observers/utils.rb +1 -0
- data/lib/micro/observers/version.rb +1 -1
- data/lib/u-observers/for/active_model.rb +2 -0
- data/lib/u-observers/for/active_record.rb +2 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c90f3338948cf8db02bdb1a882e3191adfd52f106fc2b4c1acf873447abd82a
|
4
|
+
data.tar.gz: 2db659fbaee101e1af2838bdb89dc85838f0e9686d14fe14fd9782fc568ee1f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 674b8dabdc6cfca0a982c1f72582bf1fcaab34ead0f0de29d4975cc62e164c13bbded5e6118152951584fb5855b6c539510ca393b36f9cf56622bf663d7b22e2
|
7
|
+
data.tar.gz: ff1e33f5029a40745172c7196d7e21552e8f4d534c71f057bf5643e3262fe8fa243fb55219eed352195a09abc51c337476f9f8bfcd5bce5f61cee416c2ec8de7
|
data/.travis.sh
CHANGED
data/Gemfile
CHANGED
@@ -23,16 +23,18 @@ simplecov_version =
|
|
23
23
|
else '~> 0.19'
|
24
24
|
end
|
25
25
|
|
26
|
-
sqlite3 =
|
27
|
-
case activerecord
|
28
|
-
when /\A6\.0/, nil then '~> 1.4.0'
|
29
|
-
else '~> 1.3.0'
|
30
|
-
end
|
31
|
-
|
32
26
|
group :test do
|
33
27
|
gem 'minitest', activerecord_version < '4.1' ? '~> 4.2' : '~> 5.0'
|
34
28
|
gem 'simplecov', simplecov_version, require: false
|
35
29
|
|
36
|
-
|
37
|
-
|
30
|
+
if activerecord
|
31
|
+
sqlite3 =
|
32
|
+
case activerecord
|
33
|
+
when /\A6\.0/, nil then '~> 1.4.0'
|
34
|
+
else '~> 1.3.0'
|
35
|
+
end
|
36
|
+
|
37
|
+
gem 'sqlite3', sqlite3
|
38
|
+
gem 'activerecord', activerecord, require: 'active_record'
|
39
|
+
end
|
38
40
|
end
|
data/README.md
CHANGED
@@ -1,28 +1,288 @@
|
|
1
|
-
|
1
|
+
<p align="center">
|
2
|
+
<h1 align="center">👀 μ-observers</h1>
|
3
|
+
<p align="center"><i>Simple and powerful implementation of the observer pattern.</i></p>
|
4
|
+
<br>
|
5
|
+
</p>
|
2
6
|
|
3
|
-
|
7
|
+
<p align="center">
|
8
|
+
<img src="https://img.shields.io/badge/ruby->%3D%202.2.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
|
4
9
|
|
5
|
-
|
10
|
+
<a href="https://rubygems.org/gems/u-observers">
|
11
|
+
<img alt="Gem" src="https://img.shields.io/gem/v/u-observers.svg?style=flat-square">
|
12
|
+
</a>
|
6
13
|
|
7
|
-
|
14
|
+
<a href="https://travis-ci.com/serradura/u-observers">
|
15
|
+
<img alt="Build Status" src="https://travis-ci.com/serradura/u-observers.svg?branch=main">
|
16
|
+
</a>
|
8
17
|
|
9
|
-
|
18
|
+
<a href="https://codeclimate.com/github/serradura/u-observers/maintainability">
|
19
|
+
<img alt="Maintainability" src="https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/maintainability">
|
20
|
+
</a>
|
21
|
+
|
22
|
+
<a href="https://codeclimate.com/github/serradura/u-observers/test_coverage">
|
23
|
+
<img alt="Test Coverage" src="https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/test_coverage">
|
24
|
+
</a>
|
25
|
+
</p>
|
26
|
+
|
27
|
+
This gem implements the observer pattern [[1]](https://en.wikipedia.org/wiki/Observer_pattern)[[2]](https://refactoring.guru/design-patterns/observer) (also known as publish/subscribe). It provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.
|
28
|
+
|
29
|
+
Ruby's standard library [has an abstraction](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html) that enables you to use this pattern. But its design can conflict with other mainstream libraries, like the [`ActiveModel`/`ActiveRecord`](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed), which also has the [`changed`](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html#method-i-changed) method. In this case, the behavior of the Stdlib will be been 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).
|
32
|
+
|
33
|
+
# Table of contents <!-- omit in toc -->
|
34
|
+
- [Installation](#installation)
|
35
|
+
- [Compatibility](#compatibility)
|
36
|
+
- [Usage](#usage)
|
37
|
+
- [Passing a context for your observers](#passing-a-context-for-your-observers)
|
38
|
+
- [Calling the observers](#calling-the-observers)
|
39
|
+
- [Notifying observers without marking them as changed](#notifying-observers-without-marking-them-as-changed)
|
40
|
+
- [ActiveRecord and ActiveModel integrations](#activerecord-and-activemodel-integrations)
|
41
|
+
- [notify_observers_on()](#notify_observers_on)
|
42
|
+
- [notify_observers()](#notify_observers)
|
43
|
+
- [Development](#development)
|
44
|
+
- [Contributing](#contributing)
|
45
|
+
- [License](#license)
|
46
|
+
- [Code of Conduct](#code-of-conduct)
|
47
|
+
|
48
|
+
# Installation
|
49
|
+
|
50
|
+
Add this line to your application's Gemfile and `bundle install`:
|
10
51
|
|
11
52
|
```ruby
|
12
|
-
gem '
|
53
|
+
gem 'u-observers'
|
13
54
|
```
|
14
55
|
|
15
|
-
|
56
|
+
# Compatibility
|
16
57
|
|
17
|
-
|
58
|
+
| u-observers | branch | ruby | activerecord |
|
59
|
+
| ----------- | ------- | -------- | ------------- |
|
60
|
+
| 1.0.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
|
18
61
|
|
19
|
-
|
62
|
+
> **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
63
|
|
21
|
-
|
64
|
+
[⬆️ Back to Top](#table-of-contents-)
|
22
65
|
|
23
66
|
## Usage
|
24
67
|
|
25
|
-
|
68
|
+
Any class with `Micro::Observers` module included can notify events to attached observers.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
require 'securerandom'
|
72
|
+
|
73
|
+
class Order
|
74
|
+
include Micro::Observers
|
75
|
+
|
76
|
+
attr_reader :code
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
@code, @status = SecureRandom.alphanumeric, :draft
|
80
|
+
end
|
81
|
+
|
82
|
+
def canceled?
|
83
|
+
@status == :canceled
|
84
|
+
end
|
85
|
+
|
86
|
+
def cancel!
|
87
|
+
return self if canceled?
|
88
|
+
|
89
|
+
@status = :canceled
|
90
|
+
|
91
|
+
observers.subject_changed!
|
92
|
+
observers.notify(:canceled) and return self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module OrderEvents
|
97
|
+
def self.canceled(order)
|
98
|
+
puts "The order #(#{order.code}) has been canceled."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
order = Order.new
|
103
|
+
#<Order:0x00007fb5dd8fce70 @code="X0o9yf1GsdQFvLR4", @status=:draft>
|
104
|
+
|
105
|
+
order.observers.attach(OrderEvents) # attaching multiple observers. e.g. observers.attach(A, B, C)
|
106
|
+
# <#Micro::Observers::Manager @subject=#<Order:0x00007fb5dd8fce70> @subject_changed=false @subscribers=[OrderEvents]
|
107
|
+
|
108
|
+
order.canceled?
|
109
|
+
# false
|
110
|
+
|
111
|
+
order.cancel!
|
112
|
+
# The message below will be printed by the observer (OrderEvents):
|
113
|
+
# The order #(X0o9yf1GsdQFvLR4) has been canceled
|
114
|
+
|
115
|
+
order.canceled?
|
116
|
+
# true
|
117
|
+
```
|
118
|
+
|
119
|
+
**Highlights of the previous example:**
|
120
|
+
|
121
|
+
To avoid an undesired behavior, do you need to mark the subject as changed before notify your observers about some event.
|
122
|
+
|
123
|
+
You can do this when using the `#subject_changed!` method. It will automatically mark the subject as changed.
|
124
|
+
|
125
|
+
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)`
|
126
|
+
|
127
|
+
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.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
order.observers.notify
|
131
|
+
# ArgumentError (no events (expected at least 1))
|
132
|
+
```
|
133
|
+
|
134
|
+
[⬆️ Back to Top](#table-of-contents-)
|
135
|
+
|
136
|
+
### Passing a context for your observers
|
137
|
+
|
138
|
+
To pass a context (any kind of Ruby object) for one or more observers, you will need to use the `context:` keyword as the last argument of the `#attach` method.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
class Order
|
142
|
+
include Micro::Observers
|
143
|
+
|
144
|
+
def cancel!
|
145
|
+
observers.subject_changed!
|
146
|
+
observers.notify(:canceled)
|
147
|
+
self
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
module OrderEvents
|
152
|
+
def self.canceled(order, context)
|
153
|
+
puts "The order #(#{order.code}) has been canceled. (from: #{context[:from]})"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
order = Order.new
|
158
|
+
order.observers.attach(OrderEvents, context: { from: 'example #2' ) # attaching multiple observers. e.g. observers.attach(A, B, context: {hello: :world})
|
159
|
+
order.cancel!
|
160
|
+
# The message below will be printed by the observer (OrderEvents):
|
161
|
+
# The order #(70196221441820) has been canceled. (from: example #2)
|
162
|
+
```
|
163
|
+
|
164
|
+
[⬆️ Back to Top](#table-of-contents-)
|
165
|
+
|
166
|
+
### Calling the observers
|
167
|
+
|
168
|
+
You can use a callable (a class, module, or object that responds to the call method) to be your observers.
|
169
|
+
To do this, you only need make use of the method `#call` instead of `#notify`.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class Order
|
173
|
+
include Micro::Observers
|
174
|
+
|
175
|
+
def cancel!
|
176
|
+
observers.subject_changed!
|
177
|
+
observers.call # in practice, this is a shortcut to observers.notify(:call)
|
178
|
+
self
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
OrderCancellation = -> (order) { puts "The order #(#{order.object_id}) has been canceled." }
|
183
|
+
|
184
|
+
order = Order.new
|
185
|
+
order.observers.attach(OrderCancellation)
|
186
|
+
order.cancel!
|
187
|
+
# The message below will be printed by the observer (OrderEvents):
|
188
|
+
# The order #(70196221441820) has been canceled.
|
189
|
+
```
|
190
|
+
|
191
|
+
PS: The `observers.call` can receive one or more events, but in this case, the default event (`call`) won't be transmitted.a
|
192
|
+
|
193
|
+
[⬆️ Back to Top](#table-of-contents-)
|
194
|
+
|
195
|
+
### Notifying observers without marking them as changed
|
196
|
+
|
197
|
+
This feature needs to be used with caution!
|
198
|
+
|
199
|
+
If you use the methods `#notify!` or `#call!` you won't need to mark observers with `#subject_changed`.
|
200
|
+
|
201
|
+
[⬆️ Back to Top](#table-of-contents-)
|
202
|
+
|
203
|
+
### ActiveRecord and ActiveModel integrations
|
204
|
+
|
205
|
+
To make use of this feature you need to require an additional module (`require 'u-observers/for/active_record'`).
|
206
|
+
|
207
|
+
Gemfile example:
|
208
|
+
```ruby
|
209
|
+
gem 'u-observers', require: 'u-observers/for/active_record'
|
210
|
+
```
|
211
|
+
|
212
|
+
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:
|
213
|
+
|
214
|
+
|
215
|
+
#### notify_observers_on()
|
216
|
+
|
217
|
+
The `notify_observers_on` allows you to pass one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your object observers.
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
class Post < ActiveRecord::Base
|
221
|
+
include ::Micro::Observers::For::ActiveRecord
|
222
|
+
|
223
|
+
notify_observers_on(:after_commit) # passing multiple callbacks. e.g. notify_observers_on(:before_save, :after_commit)
|
224
|
+
end
|
225
|
+
|
226
|
+
module TitlePrinter
|
227
|
+
def self.after_commit(post)
|
228
|
+
puts "Title: #{post.title}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
module TitlePrinterWithContext
|
233
|
+
def self.after_commit(post, context)
|
234
|
+
puts "Title: #{post.title} (from: #{context[:from]})"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
Post.transaction do
|
239
|
+
post = Post.new(title: 'Hello world')
|
240
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example 4' })
|
241
|
+
post.save
|
242
|
+
end
|
243
|
+
# The message below will be printed by the observer (OrderEvents):
|
244
|
+
# Title: Hello world
|
245
|
+
# Title: Hello world (from: example 4)
|
246
|
+
```
|
247
|
+
|
248
|
+
[⬆️ Back to Top](#table-of-contents-)
|
249
|
+
|
250
|
+
#### notify_observers()
|
251
|
+
|
252
|
+
The `notify_observers` allows you to pass one or more *events*, that will be used to notify after the execution of some `ActiveModel`/`ActiveRecord` callback.
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
class Post < ActiveRecord::Base
|
256
|
+
include ::Micro::Observers::For::ActiveRecord
|
257
|
+
|
258
|
+
after_commit(¬ify_observers(:transaction_completed))
|
259
|
+
end
|
260
|
+
|
261
|
+
module TitlePrinter
|
262
|
+
def self.transaction_completed(post)
|
263
|
+
puts("Title: #{post.title}")
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
module TitlePrinterWithContext
|
268
|
+
def self.transaction_completed(post, context)
|
269
|
+
puts("Title: #{post.title} (from: #{context[:from]})")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
Post.transaction do
|
274
|
+
post = Post.new(title: 'Olá mundo')
|
275
|
+
post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example 5' })
|
276
|
+
post.save
|
277
|
+
end
|
278
|
+
# The message below will be printed by the observer (OrderEvents):
|
279
|
+
# Title: Olá mundo
|
280
|
+
# Title: Olá mundo (from: example 5)
|
281
|
+
```
|
282
|
+
|
283
|
+
PS: You can use `include ::Micro::Observers::For::ActiveModel` if your class only makes use of the `ActiveModel` and all the previous examples will work.
|
284
|
+
|
285
|
+
[⬆️ Back to Top](#table-of-contents-)
|
26
286
|
|
27
287
|
## Development
|
28
288
|
|
data/lib/micro/observers.rb
CHANGED
@@ -6,27 +6,8 @@ module Micro
|
|
6
6
|
require 'micro/observers/events'
|
7
7
|
require 'micro/observers/manager'
|
8
8
|
|
9
|
-
module ClassMethods
|
10
|
-
def notify_observers!(events)
|
11
|
-
proc do |object|
|
12
|
-
object.observers.subject_changed!
|
13
|
-
object.observers.send(:notify!, events)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def notify_observers(*events)
|
18
|
-
notify_observers!(Events[events])
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.included(base)
|
23
|
-
base.extend(ClassMethods)
|
24
|
-
base.send(:private_class_method, :notify_observers!)
|
25
|
-
end
|
26
|
-
|
27
9
|
def observers
|
28
|
-
@
|
10
|
+
@__observers ||= Observers::Manager.for(self)
|
29
11
|
end
|
30
|
-
|
31
12
|
end
|
32
13
|
end
|
@@ -4,15 +4,23 @@ module Micro
|
|
4
4
|
module Observers
|
5
5
|
|
6
6
|
module Events
|
7
|
-
|
8
|
-
|
9
|
-
def self.[](value)
|
7
|
+
def self.[](value, default: Utils::EMPTY_ARRAY)
|
10
8
|
values = Utils.compact_array(value)
|
11
9
|
|
12
|
-
values.empty? ?
|
10
|
+
values.empty? ? default : values
|
11
|
+
end
|
12
|
+
|
13
|
+
NO_EVENTS_MSG = 'no events (expected at least 1)'.freeze
|
14
|
+
|
15
|
+
def self.fetch(value)
|
16
|
+
values = self[value]
|
17
|
+
|
18
|
+
return values unless values.empty?
|
19
|
+
|
20
|
+
raise ArgumentError, NO_EVENTS_MSG
|
13
21
|
end
|
14
22
|
|
15
|
-
private_constant :
|
23
|
+
private_constant :NO_EVENTS_MSG
|
16
24
|
end
|
17
25
|
|
18
26
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
module For
|
6
|
+
|
7
|
+
module ActiveModel
|
8
|
+
module ClassMethods
|
9
|
+
def notify_observers!(events)
|
10
|
+
proc do |object|
|
11
|
+
object.observers.subject_changed!
|
12
|
+
object.observers.send(:broadcast_if_subject_changed, events)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def notify_observers(*events)
|
17
|
+
notify_observers!(Events.fetch(events))
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify_observers_on(*callback_methods)
|
21
|
+
Utils.compact_array(callback_methods).each do |callback_method|
|
22
|
+
self.public_send(callback_method, ¬ify_observers!([callback_method]))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.included(base)
|
28
|
+
base.extend(ClassMethods)
|
29
|
+
base.send(:private_class_method, :notify_observers!)
|
30
|
+
base.send(:include, ::Micro::Observers)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
module For
|
6
|
+
require 'micro/observers/for/active_model'
|
7
|
+
|
8
|
+
module ActiveRecord
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:include, ::Micro::Observers::For::ActiveModel)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -4,19 +4,16 @@ module Micro
|
|
4
4
|
module Observers
|
5
5
|
|
6
6
|
class Manager
|
7
|
-
|
7
|
+
MapSubscriber = -> (observer, options) { [:observer, observer, options[:context]] }
|
8
8
|
|
9
9
|
MapSubscribers = -> (value) do
|
10
10
|
array = Utils.compact_array(value.kind_of?(Array) ? value : [])
|
11
|
-
array.map { |observer|
|
11
|
+
array.map { |observer| MapSubscriber[observer, Utils::EMPTY_HASH] }
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
handler == observer
|
18
|
-
end
|
19
|
-
end
|
14
|
+
GetObserver = -> subscriber { subscriber[0] == :observer ? subscriber[1] : subscriber[2][0] }
|
15
|
+
|
16
|
+
EqualTo = -> (observer) { -> subscriber { GetObserver[subscriber] == observer } }
|
20
17
|
|
21
18
|
def self.for(subject)
|
22
19
|
new(subject)
|
@@ -46,7 +43,7 @@ module Micro
|
|
46
43
|
@subject_changed
|
47
44
|
end
|
48
45
|
|
49
|
-
|
46
|
+
INVALID_BOOLEAN_MSG = 'expected a boolean (true, false)'.freeze
|
50
47
|
|
51
48
|
def subject_changed(state)
|
52
49
|
if state == true || state == false
|
@@ -55,7 +52,7 @@ module Micro
|
|
55
52
|
return self
|
56
53
|
end
|
57
54
|
|
58
|
-
raise ArgumentError,
|
55
|
+
raise ArgumentError, INVALID_BOOLEAN_MSG
|
59
56
|
end
|
60
57
|
|
61
58
|
def subject_changed!
|
@@ -70,7 +67,7 @@ module Micro
|
|
70
67
|
options = args.last.is_a?(Hash) ? args.pop : Utils::EMPTY_HASH
|
71
68
|
|
72
69
|
Utils.compact_array(args).each do |observer|
|
73
|
-
@subscribers <<
|
70
|
+
@subscribers << MapSubscriber[observer, options] unless included?(observer)
|
74
71
|
end
|
75
72
|
|
76
73
|
self
|
@@ -95,44 +92,71 @@ module Micro
|
|
95
92
|
end
|
96
93
|
|
97
94
|
def notify(*events)
|
98
|
-
|
95
|
+
broadcast_if_subject_changed(Events.fetch(events))
|
96
|
+
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def notify!(*events)
|
101
|
+
broadcast(Events.fetch(events))
|
102
|
+
|
103
|
+
self
|
99
104
|
end
|
100
105
|
|
106
|
+
CALL_EVENT = [:call].freeze
|
107
|
+
|
101
108
|
def call(*events)
|
102
|
-
|
109
|
+
broadcast_if_subject_changed(Events[events, default: CALL_EVENT])
|
110
|
+
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def call!(*events)
|
115
|
+
broadcast(Events[events, default: CALL_EVENT])
|
103
116
|
|
104
117
|
self
|
105
118
|
end
|
106
119
|
|
107
|
-
|
120
|
+
def inspect
|
121
|
+
subs = @subscribers.empty? ? @subscribers : @subscribers.map(&GetObserver)
|
122
|
+
|
123
|
+
'<#%s @subject=%s @subject_changed=%p @subscribers=%p>' % [self.class, @subject, @subject_changed, subs]
|
124
|
+
end
|
108
125
|
|
109
|
-
|
110
|
-
|
126
|
+
private
|
127
|
+
|
128
|
+
def broadcast_if_subject_changed(events)
|
129
|
+
return unless subject_changed?
|
111
130
|
|
112
131
|
broadcast(events)
|
113
132
|
|
114
133
|
subject_changed(false)
|
115
|
-
|
116
|
-
self
|
117
134
|
end
|
118
135
|
|
119
|
-
private
|
120
|
-
|
121
136
|
def broadcast(events)
|
137
|
+
return if @subscribers.empty?
|
138
|
+
|
122
139
|
events.each do |event|
|
123
|
-
@subscribers.each
|
140
|
+
@subscribers.each do |strategy, observer, context|
|
141
|
+
case strategy
|
142
|
+
when :observer then notify_observer(observer, event, context)
|
143
|
+
when :callable then notify_callable(observer, event, context)
|
144
|
+
end
|
145
|
+
end
|
124
146
|
end
|
125
147
|
end
|
126
148
|
|
127
|
-
def
|
128
|
-
|
149
|
+
def notify_observer(observer, method_name, context)
|
150
|
+
return unless observer.respond_to?(method_name)
|
129
151
|
|
130
|
-
|
152
|
+
handler = observer.is_a?(Proc) ? observer : observer.method(method_name)
|
131
153
|
|
132
|
-
|
154
|
+
handler.arity == 1 ? handler.call(@subject) : handler.call(@subject, context)
|
133
155
|
end
|
134
156
|
|
135
|
-
def
|
157
|
+
def notify_callable(expected_event, event, context)
|
158
|
+
return if expected_event != event
|
159
|
+
|
136
160
|
callable, with = context[0], context[1]
|
137
161
|
|
138
162
|
arg = with.is_a?(Proc) ? with.call(@subject) : (with || @subject)
|
@@ -140,15 +164,8 @@ module Micro
|
|
140
164
|
callable.call(arg)
|
141
165
|
end
|
142
166
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
handler = observer.is_a?(Proc) ? observer : observer.method(method_name)
|
147
|
-
|
148
|
-
handler.arity == 1 ? handler.call(@subject) : handler.call(@subject, context)
|
149
|
-
end
|
150
|
-
|
151
|
-
private_constant :MapObserver, :MapSubscribers, :EqualTo, :INVALID_BOOLEAN_ERROR
|
167
|
+
private_constant :INVALID_BOOLEAN_MSG, :CALL_EVENT
|
168
|
+
private_constant :MapSubscriber, :MapSubscribers, :GetObserver, :EqualTo
|
152
169
|
end
|
153
170
|
|
154
171
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: u-observers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Serradura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -58,10 +58,14 @@ files:
|
|
58
58
|
- bin/setup
|
59
59
|
- lib/micro/observers.rb
|
60
60
|
- lib/micro/observers/events.rb
|
61
|
+
- lib/micro/observers/for/active_model.rb
|
62
|
+
- lib/micro/observers/for/active_record.rb
|
61
63
|
- lib/micro/observers/manager.rb
|
62
64
|
- lib/micro/observers/utils.rb
|
63
65
|
- lib/micro/observers/version.rb
|
64
66
|
- lib/u-observers.rb
|
67
|
+
- lib/u-observers/for/active_model.rb
|
68
|
+
- lib/u-observers/for/active_record.rb
|
65
69
|
- test.sh
|
66
70
|
- u-observers.gemspec
|
67
71
|
homepage: https://github.com/serradura/u-observers
|