u-observers 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|