omnes 0.1.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +25 -1
- data/README.md +134 -27
- data/lib/omnes/bus.rb +55 -13
- data/lib/omnes/configurable.rb +108 -0
- data/lib/omnes/errors.rb +39 -1
- data/lib/omnes/event.rb +2 -2
- data/lib/omnes/publication.rb +5 -14
- data/lib/omnes/publication_context.rb +41 -0
- data/lib/omnes/subscriber/adapter/active_job.rb +10 -6
- data/lib/omnes/subscriber/adapter/method.rb +2 -2
- data/lib/omnes/subscriber/adapter/sidekiq.rb +22 -8
- data/lib/omnes/subscriber/adapter.rb +6 -0
- data/lib/omnes/subscriber/state.rb +29 -12
- data/lib/omnes/subscriber.rb +14 -12
- data/lib/omnes/subscription.rb +25 -4
- data/lib/omnes/version.rb +1 -1
- data/lib/omnes.rb +10 -37
- data/omnes.gemspec +2 -4
- metadata +13 -26
- data/Gemfile.lock +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d69bcb6ece79550d662ea1ed7cf734cc47940a39d432c58c849f8c15415472a
|
4
|
+
data.tar.gz: 459ff5514f310cc52492bb2fabc6bf8786e3a9119f760288aeef6329b609b4d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f395f3f775f8c44d54e17ad8502b0bf40c201edd861170242ac1ab29db90c2ace38273aceea5a257ed5e2910dcaca43370feec8e9563090847febe039ce99805
|
7
|
+
data.tar.gz: d2be4901c0fcc8bdd7bd8f1a1887c32445877300f1bf46f1396e7c16312c221f9c295fef1ee58756320a1be20c69f2a0e4dc4d0048451043998f51d89bb2c68c
|
data/.github/workflows/test.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
2
|
+
TargetRubyVersion: 2.5
|
3
3
|
NewCops: enable
|
4
4
|
SuggestExtensions: false
|
5
5
|
|
@@ -29,7 +29,7 @@ Layout/LineLength:
|
|
29
29
|
Max: 120
|
30
30
|
|
31
31
|
Naming/MethodName:
|
32
|
-
|
32
|
+
AllowedPatterns:
|
33
33
|
- '.*Type'
|
34
34
|
|
35
35
|
Lint/ConstantDefinitionInBlock:
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.2.2] - 2022-05-03
|
10
|
+
|
11
|
+
### Added
|
12
|
+
- Support Ruby 2.5 & 2.6 [#6](https://github.com/nebulab/omnes/pull/6).
|
13
|
+
|
14
|
+
## [0.2.1] - 2022-04-19
|
15
|
+
|
16
|
+
### Added
|
17
|
+
- Added `Omnes::Bus#clear` for autoloading [#4](https://github.com/nebulab/omnes/pull/4).
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
- Fix re-adding autodiscovered subscriptions on subsequent calls [#5](https://github.com/nebulab/omnes/pull/5).
|
21
|
+
|
22
|
+
## [0.2.0] - 2022-04-15
|
23
|
+
|
24
|
+
### Added
|
25
|
+
- Be able to fetch subscriptions by id from the bus [#1](https://github.com/nebulab/omnes/pull/1).
|
26
|
+
- Use ad-hoc configuration system (and make Omnes zero-deps) [#2](https://github.com/nebulab/omnes/pull/2).
|
27
|
+
- Bind a publication context to subscriptions [#3](https://github.com/nebulab/omnes/pull/3).
|
28
|
+
|
9
29
|
## [0.1.0] - 2022-03-23
|
10
30
|
|
11
|
-
[Unreleased]: https://github.com/nebulab/omnes/compare/v0.
|
31
|
+
[Unreleased]: https://github.com/nebulab/omnes/compare/v0.2.2...HEAD
|
32
|
+
[0.2.1]: https://github.com/nebulab/omnes/compare/v0.2.1...v0.2.2
|
33
|
+
[0.2.1]: https://github.com/nebulab/omnes/compare/v0.2.0...v0.2.1
|
34
|
+
[0.2.0]: https://github.com/nebulab/omnes/compare/v0.1.0...v0.2.0
|
35
|
+
[0.1.0]: https://github.com/nebulab/omnes/releases/tag/v0.1.0
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ The following examples will use the direct `Omnes::Bus` instance. The only
|
|
37
37
|
difference for the mixing use case is that the methods are directly called in
|
38
38
|
the including instance.
|
39
39
|
|
40
|
-
|
40
|
+
## Registering events
|
41
41
|
|
42
42
|
Before being able to work with a given event, its name (which must be a
|
43
43
|
`Symbol`) must be registered:
|
@@ -46,9 +46,9 @@ Before being able to work with a given event, its name (which must be a
|
|
46
46
|
bus.register(:order_created)
|
47
47
|
```
|
48
48
|
|
49
|
-
|
49
|
+
## Publishing events
|
50
50
|
|
51
|
-
An event can be anything responding to a method `:
|
51
|
+
An event can be anything responding to a method `:omnes_event_name`, which must match with a
|
52
52
|
registered name.
|
53
53
|
|
54
54
|
Typically, there're two main ways to generate events.
|
@@ -73,8 +73,7 @@ features, such as event persistence, can't be reliably built on top of them.
|
|
73
73
|
|
74
74
|
You can also publish an instance of a class including
|
75
75
|
[`Omnes::Event`](lib/omnes/event.rb). The only fancy thing it provides is an
|
76
|
-
OOTB event name generated based on the class name.
|
77
|
-
anything responding to `#omnes_event_name`.
|
76
|
+
OOTB event name generated based on the class name.
|
78
77
|
|
79
78
|
```ruby
|
80
79
|
class OrderCreatedEvent
|
@@ -105,7 +104,7 @@ Omnes.config.event.name_builder = event_name_as_class
|
|
105
104
|
Instance-backed events provide a well-defined structure, and other features,
|
106
105
|
like event persistence, can be added on top of them.
|
107
106
|
|
108
|
-
|
107
|
+
## Subscribing to events
|
109
108
|
|
110
109
|
You can subscribe to a specific event to run some code whenever it's published.
|
111
110
|
The event is yielded to the subscription block:
|
@@ -150,7 +149,7 @@ bus.subscribe(:order_created, OrderCreationEmailSubscription.new)
|
|
150
149
|
However, see [Event subscribers](#event-subscribers) section bellow for a more powerful way
|
151
150
|
to define standalone event handlers.
|
152
151
|
|
153
|
-
|
152
|
+
### Global subscriptions
|
154
153
|
|
155
154
|
You can also create a subscription that will run for all events:
|
156
155
|
|
@@ -163,27 +162,43 @@ class LogEventsSubscription
|
|
163
162
|
end
|
164
163
|
|
165
164
|
def call(event)
|
166
|
-
logger.info("Event #{event.
|
165
|
+
logger.info("Event #{event.omnes_event_name} published")
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
170
169
|
bus.subscribe_to_all(LogEventsSubscription.new)
|
171
170
|
```
|
172
171
|
|
173
|
-
|
172
|
+
### Custom matcher subscriptions
|
174
173
|
|
175
174
|
Custom event matchers can be defined. A matcher is something responding to
|
176
175
|
`#call` and taking the event as an argument. It must return `true` or `false`
|
177
176
|
to match or ignore the event.
|
178
177
|
|
179
178
|
```ruby
|
180
|
-
ORDER_EVENTS_MATCHER = ->(event) { event.
|
179
|
+
ORDER_EVENTS_MATCHER = ->(event) { event.omnes_event_name.start_with?(:order) }
|
181
180
|
|
182
181
|
bus.subscribe_with_matcher(ORDER_EVENTS_MATCHER) do |event|
|
183
182
|
# ...
|
184
183
|
end
|
185
184
|
```
|
186
185
|
|
186
|
+
### Referencing subscriptions
|
187
|
+
|
188
|
+
For all subscription methods we've seen, an `Omnes::Subscription` instance is
|
189
|
+
returned. Holding that reference can be useful for [debugging](#debugging) and
|
190
|
+
[testing](#testing) purposes.
|
191
|
+
|
192
|
+
Often though, you won't have the reference at hand when you need it.
|
193
|
+
Thankfully, you can provide a subscription identifier on subscription time and
|
194
|
+
use it later to fetch the subscription instance from the bus. A subscription
|
195
|
+
identifier needs to be a `Symbol`:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
bus.subscribe(:order_created, OrderCreationEmailSubscription.new, id: :order_created_email)
|
199
|
+
subscription = bus.subscription(:send_confirmation_email)
|
200
|
+
```
|
201
|
+
|
187
202
|
## Event subscribers
|
188
203
|
|
189
204
|
Events subscribers offer a way to define event subscriptions from a custom
|
@@ -207,7 +222,11 @@ class OrderCreationEmailSubscriber
|
|
207
222
|
service.send(number: event.number, email: event.user_email)
|
208
223
|
end
|
209
224
|
end
|
225
|
+
```
|
226
|
+
|
227
|
+
You add the subscriptions by calling the `#subscribe_to` method on an instance:
|
210
228
|
|
229
|
+
```ruby
|
211
230
|
OrderCreationEmailSubscriber.new.subscribe_to(bus)
|
212
231
|
```
|
213
232
|
|
@@ -227,7 +246,7 @@ class LogEventsSubscriber
|
|
227
246
|
end
|
228
247
|
|
229
248
|
def log_event(event)
|
230
|
-
logger.info("Event #{event.
|
249
|
+
logger.info("Event #{event.omnes_event_name} published")
|
231
250
|
end
|
232
251
|
end
|
233
252
|
```
|
@@ -246,6 +265,21 @@ class OrderSubscriber
|
|
246
265
|
end
|
247
266
|
```
|
248
267
|
|
268
|
+
Likewise, you can provide [identifiers to reference
|
269
|
+
subscriptions](#referencing-subscriptions):
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
handle :order_created, with: :send_confirmation_email, id: :order_creation_email_subscriber
|
273
|
+
```
|
274
|
+
|
275
|
+
As you can subscribe multiple instances of a subscriber to the same bus, you
|
276
|
+
might need to create a different identifier for each of them. For those cases,
|
277
|
+
you can pass a lambda taking the subscriber instance:
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
handle :order_created, with: :send_confirmation_email, id: ->(subscriber) { :"#{subscriber.id}_order_creation_email_subscriber" }
|
281
|
+
```
|
282
|
+
|
249
283
|
### Autodiscovering event handlers
|
250
284
|
|
251
285
|
You can let the event handlers to be automatically discovered.You need to
|
@@ -292,6 +326,12 @@ class OrderCreationEmailSubscriber
|
|
292
326
|
end
|
293
327
|
```
|
294
328
|
|
329
|
+
The strategy can also be globally set:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
Omnes.config.subscriber.autodiscover_strategy = AUTODISCOVER_STRATEGY
|
333
|
+
```
|
334
|
+
|
295
335
|
### Adapters
|
296
336
|
|
297
337
|
Subscribers are not limited to use a method as event handler. They can interact
|
@@ -389,7 +429,8 @@ Omnes.config.subscriber.adapter.active_job.serializer = :serialized_payload.to_p
|
|
389
429
|
#### Custom adapters
|
390
430
|
|
391
431
|
Custom adapters can be built. They need to implement a method `#call` taking
|
392
|
-
the instance of `Omnes::Subscriber
|
432
|
+
the instance of `Omnes::Subscriber`, the event and, optionally, the publication
|
433
|
+
context (see [debugging subscriptions](#subscription)).
|
393
434
|
|
394
435
|
Here's a custom adapter executing a subscriber method in a different
|
395
436
|
thread (we add an extra argument for the method name, and we partially apply it
|
@@ -402,7 +443,6 @@ end
|
|
402
443
|
|
403
444
|
class OrderCreationEmailSubscriber
|
404
445
|
include Omnes::Subscriber
|
405
|
-
include Sidekiq::Job
|
406
446
|
|
407
447
|
handle :order_created, with: THREAD_ADAPTER.curry[:order_created]
|
408
448
|
|
@@ -413,8 +453,8 @@ end
|
|
413
453
|
```
|
414
454
|
|
415
455
|
Alternatively, adapters can be curried and only take the instance as an
|
416
|
-
argument, returning a
|
417
|
-
|
456
|
+
argument, returning a callable taking the event. For instance, we could also
|
457
|
+
have defined the thread adapter like this:
|
418
458
|
|
419
459
|
```ruby
|
420
460
|
class ThreadAdapter
|
@@ -436,20 +476,29 @@ handle :order_created, with: ThreadAdapter.new(:order_created)
|
|
436
476
|
# ...
|
437
477
|
```
|
438
478
|
|
439
|
-
##
|
440
|
-
|
441
|
-
### Unsubscribing
|
479
|
+
## Unsubscribing & clearing
|
442
480
|
|
443
|
-
|
444
|
-
[
|
445
|
-
|
446
|
-
debug some behavior.
|
481
|
+
You can unsubscribe a given subscription by passing its
|
482
|
+
[reference](#referencing-subscriptions) to `Omnes::Bus#unsubscribe` (see how to
|
483
|
+
[reference subscriptions](#referencing-subscriptions)):
|
447
484
|
|
448
485
|
```ruby
|
449
486
|
subscription = bus.subscribe(:order_created, OrderCreationEmailSubscription.new)
|
450
487
|
bus.unsubscribe(subscription)
|
451
488
|
```
|
452
489
|
|
490
|
+
Sometimes you might need to leave your bus in a pristine state, with no events
|
491
|
+
registered or active subscriptions. That can be useful for autoloading in
|
492
|
+
development:
|
493
|
+
|
494
|
+
```ruby
|
495
|
+
bus.clear
|
496
|
+
bus.registry.event_names # => []
|
497
|
+
bus.subscriptions # => []
|
498
|
+
```
|
499
|
+
|
500
|
+
## Debugging
|
501
|
+
|
453
502
|
### Registration
|
454
503
|
|
455
504
|
Whenever you register an event, you get back an [`Omnes::Registry::Registration`](lib/omnes/registry.rb)
|
@@ -478,10 +527,10 @@ When you publish an event, you get back an
|
|
478
527
|
attributes that allow observing what happened:
|
479
528
|
|
480
529
|
- `#event` contains the event instance that has been published.
|
481
|
-
- `#caller_location` refers to the publication caller.
|
482
|
-
- `#time` is the time stamp for the publication.
|
483
530
|
- `#executions` contains an array of
|
484
531
|
`Omnes::Execution`(lib/omnes/execution.rb). Read more below.
|
532
|
+
- `#context` is an instance of
|
533
|
+
[`Omnes::PublicationContext`](lib/omnes/publication_context.rb).
|
485
534
|
|
486
535
|
`Omnes::Execution` represents a subscription individual execution. It contains
|
487
536
|
the following attributes:
|
@@ -491,6 +540,40 @@ the following attributes:
|
|
491
540
|
- `#benchmark` of the operation.
|
492
541
|
- `#time` is the time where the execution started.
|
493
542
|
|
543
|
+
`Omnes::PublicationContext` represents the shared context for all triggered
|
544
|
+
executions. See [Subscription][#subscription] for details.
|
545
|
+
|
546
|
+
### Subscription
|
547
|
+
|
548
|
+
If your subscription block or callable object takes a second argument, it'll
|
549
|
+
contain an instance of an
|
550
|
+
[`Omnes::PublicationContext`](lib/omnes/publication_context.rb). It allows you
|
551
|
+
to inspect what triggered a given execution from within that execution code. It
|
552
|
+
contains:
|
553
|
+
|
554
|
+
- `#caller_location` refers to the publication caller.
|
555
|
+
- `#time` is the time stamp for the publication.
|
556
|
+
|
557
|
+
```ruby
|
558
|
+
class OrderCreationEmailSubscriber
|
559
|
+
include Omnes::Subscriber
|
560
|
+
|
561
|
+
handle :order_created, with: :send_confirmation_email
|
562
|
+
|
563
|
+
def send_confirmation_email(event, publication_context)
|
564
|
+
# debugging
|
565
|
+
abort(publication_context.caller_location.inspect)
|
566
|
+
|
567
|
+
OrderCreationEmail.send(number: event.number, email: event.user_email)
|
568
|
+
end
|
569
|
+
end
|
570
|
+
```
|
571
|
+
|
572
|
+
In case you're developing your own async adapter, you can call `#serialized` on
|
573
|
+
an instance of `Omnes::PublicationContext` to get a serialized version of it.
|
574
|
+
It'll return a `Hash` with `"caller_location"` and `"time"` keys, and the
|
575
|
+
respective `String` representations as values.
|
576
|
+
|
494
577
|
## Testing
|
495
578
|
|
496
579
|
Ideally, you wouldn't need big setups to test your event-driven behavior. You
|
@@ -521,11 +604,30 @@ end
|
|
521
604
|
bus.publish(:order_deleted, number: order.number) # `deletion_subscription` will run
|
522
605
|
```
|
523
606
|
|
524
|
-
Remember that
|
607
|
+
Remember that you can get previous [subscription
|
608
|
+
references](#referencing-subscriptions) thanks to
|
609
|
+
subscription identifiers.
|
525
610
|
|
526
611
|
There's also a specialized `Omnes::Bus#performing_nothing` method that runs no
|
527
612
|
subscriptions for the duration of the block.
|
528
613
|
|
614
|
+
## Configuration
|
615
|
+
|
616
|
+
We've seen the relevant configurable settings in the corresponding sections.
|
617
|
+
You can also access the configuration in the habitual block syntax:
|
618
|
+
|
619
|
+
```ruby
|
620
|
+
Omnes.configure do |config|
|
621
|
+
config.subscriber.adapter.sidekiq.serializer = :serialized_payload.to_proc
|
622
|
+
end
|
623
|
+
```
|
624
|
+
|
625
|
+
Finally, nested settings can also be set directly from the affected class. E.g.:
|
626
|
+
|
627
|
+
```ruby
|
628
|
+
Omnes::Subscriber::Adapter::Sidekiq.config.serializer = :serialized_payload.to_proc
|
629
|
+
```
|
630
|
+
|
529
631
|
## Recipes
|
530
632
|
|
531
633
|
### Rails
|
@@ -538,9 +640,14 @@ require "omnes"
|
|
538
640
|
Omnes.config.subscriber.autodiscover = true
|
539
641
|
|
540
642
|
Bus = Omnes::Bus.new
|
541
|
-
Bus.register(:order_created)
|
542
643
|
|
543
|
-
|
644
|
+
Rails.application.config.to_prepare do
|
645
|
+
Bus.clear
|
646
|
+
|
647
|
+
Bus.register(:order_created)
|
648
|
+
|
649
|
+
OrderCreationEmailSubscriber.new.subscribe_to(Bus)
|
650
|
+
end
|
544
651
|
```
|
545
652
|
|
546
653
|
We can define `OrderCreationEmailSubscriber` in
|
data/lib/omnes/bus.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "omnes/publication"
|
4
|
+
require "omnes/publication_context"
|
4
5
|
require "omnes/registry"
|
5
6
|
require "omnes/subscription"
|
6
7
|
require "omnes/unstructured_event"
|
@@ -22,8 +23,8 @@ module Omnes
|
|
22
23
|
# bus.register(:foo)
|
23
24
|
# ```
|
24
25
|
#
|
25
|
-
# An event can be anything responding to a method `:
|
26
|
-
# say, must match with a registered name.
|
26
|
+
# An event can be anything responding to a method `:omes_event_name` which,
|
27
|
+
# needless to say, must match with a registered name.
|
27
28
|
#
|
28
29
|
# Typically, there're two main ways to generate events.
|
29
30
|
#
|
@@ -104,6 +105,21 @@ module Omnes
|
|
104
105
|
#
|
105
106
|
# bus.subscribe_with_matcher(matcher, MySubscription.new)
|
106
107
|
# ```
|
108
|
+
#
|
109
|
+
# For all previous subscription methods, a subscription object is returned.
|
110
|
+
# You can supply a subscription id to it to be able to fetch it from the bus
|
111
|
+
# later on:
|
112
|
+
#
|
113
|
+
# ```
|
114
|
+
# subscription = bus.subscribe(:foo, MySubscription.new, id: :foo_sub)
|
115
|
+
# bus.subscription(:foo_sub) == subscription #=> true
|
116
|
+
# ```
|
117
|
+
#
|
118
|
+
# A subscription can be referenced when you want to unsubscribe:
|
119
|
+
#
|
120
|
+
# ```
|
121
|
+
# bus.unsubscribe(subscription)
|
122
|
+
# ```
|
107
123
|
class Bus
|
108
124
|
# @api private
|
109
125
|
def self.EventType(value, **payload)
|
@@ -168,13 +184,13 @@ module Omnes
|
|
168
184
|
publication_time = Time.now.utc
|
169
185
|
event = self.class.EventType(event, **payload)
|
170
186
|
registry.check_event_name(event.omnes_event_name)
|
171
|
-
|
187
|
+
publication_context = PublicationContext.new(caller_location: caller_location, time: publication_time)
|
188
|
+
executions = execute_subscriptions_for_event(event, publication_context)
|
172
189
|
|
173
190
|
Publication.new(
|
174
191
|
event: event,
|
175
192
|
executions: executions,
|
176
|
-
|
177
|
-
time: publication_time
|
193
|
+
context: publication_context
|
178
194
|
)
|
179
195
|
end
|
180
196
|
|
@@ -182,37 +198,42 @@ module Omnes
|
|
182
198
|
#
|
183
199
|
# @param event_name [Symbol] Name of the event
|
184
200
|
# @param callable [#call] Subscription callback taking the event
|
201
|
+
# @param id [Symbol] Unique identifier for the subscription
|
185
202
|
# @yield [event] Subscription callback if callable is not given
|
186
203
|
#
|
187
204
|
# @return [Omnes::Subscription]
|
188
205
|
#
|
189
206
|
# @raise [Omnes::UnknownEventError] When event name has not been registered
|
190
|
-
def subscribe(event_name, callable = nil, &block)
|
207
|
+
def subscribe(event_name, callable = nil, id: Subscription.random_id, &block)
|
191
208
|
registry.check_event_name(event_name)
|
192
209
|
|
193
|
-
subscribe_with_matcher(Subscription::SINGLE_EVENT_MATCHER.curry[event_name], callable, &block)
|
210
|
+
subscribe_with_matcher(Subscription::SINGLE_EVENT_MATCHER.curry[event_name], callable, id: id, &block)
|
194
211
|
end
|
195
212
|
|
196
213
|
# Adds a subscription for all events
|
197
214
|
#
|
198
215
|
# @param callable [#call] Subscription callback taking the event
|
216
|
+
# @param id [Symbol] Unique identifier for the subscription
|
199
217
|
# @yield [event] Subscription callback if callable is not given
|
200
218
|
#
|
201
219
|
# @return [Omnes::Subscription]
|
202
|
-
def subscribe_to_all(callable = nil, &block)
|
203
|
-
subscribe_with_matcher(Subscription::ALL_EVENTS_MATCHER, callable, &block)
|
220
|
+
def subscribe_to_all(callable = nil, id: Subscription.random_id, &block)
|
221
|
+
subscribe_with_matcher(Subscription::ALL_EVENTS_MATCHER, callable, id: id, &block)
|
204
222
|
end
|
205
223
|
|
206
224
|
# Adds a subscription with given matcher
|
207
225
|
#
|
208
226
|
# @param matcher [#call] Callable taking the event and returning a boolean
|
209
227
|
# @param callable [#call] Subscription callback taking the event
|
228
|
+
# @param id [Symbol] Unique identifier for the subscription
|
210
229
|
# @yield [event] Subscription callback if callable is not given
|
211
230
|
#
|
212
231
|
# @return [Omnes::Subscription]
|
213
|
-
def subscribe_with_matcher(matcher, callable = nil, &block)
|
232
|
+
def subscribe_with_matcher(matcher, callable = nil, id: Subscription.random_id, &block)
|
233
|
+
raise DuplicateSubscriptionIdError.new(id: id, bus: self) if subscription(id)
|
234
|
+
|
214
235
|
callback = callable || block
|
215
|
-
Subscription.new(matcher: matcher, callback: callback).tap do |subscription|
|
236
|
+
Subscription.new(matcher: matcher, callback: callback, id: id).tap do |subscription|
|
216
237
|
@subscriptions << subscription
|
217
238
|
end
|
218
239
|
end
|
@@ -257,11 +278,32 @@ module Omnes
|
|
257
278
|
performing_only(&block)
|
258
279
|
end
|
259
280
|
|
281
|
+
# Fetch a subscription by its identifier
|
282
|
+
#
|
283
|
+
# @param id [Symbol] Subscription identifier
|
284
|
+
#
|
285
|
+
# @return [Omnes::Subscription]
|
286
|
+
def subscription(id)
|
287
|
+
subscriptions.find { |subscription| subscription.id == id }
|
288
|
+
end
|
289
|
+
|
290
|
+
# Clears all registered events and subscriptions
|
291
|
+
#
|
292
|
+
# Useful for code reloading.
|
293
|
+
#
|
294
|
+
# @return [Omnes::Bus]
|
295
|
+
def clear
|
296
|
+
tap do
|
297
|
+
@subscriptions = []
|
298
|
+
@registry = Registry.new
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
260
302
|
private
|
261
303
|
|
262
|
-
def execute_subscriptions_for_event(event)
|
304
|
+
def execute_subscriptions_for_event(event, publication_context)
|
263
305
|
subscriptions_for_event(event).map do |subscription|
|
264
|
-
subscription.(event)
|
306
|
+
subscription.(event, publication_context)
|
265
307
|
end
|
266
308
|
end
|
267
309
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Omnes
|
4
|
+
# Ad-hoc configurable behavior for Omnes
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# ```
|
9
|
+
# Omnes.configure do |config|
|
10
|
+
# config.event.name_builder = MY_NAME_BUILDER
|
11
|
+
# end
|
12
|
+
# ```
|
13
|
+
#
|
14
|
+
# or
|
15
|
+
#
|
16
|
+
# ```
|
17
|
+
# Omnes::Event.config.name_builder = MY_NAME_BUILDER
|
18
|
+
# ```
|
19
|
+
module Configurable
|
20
|
+
# Class where readers and writers are defined
|
21
|
+
class Config
|
22
|
+
# @api private
|
23
|
+
attr_reader :settings
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def initialize
|
27
|
+
@_mutex = Mutex.new
|
28
|
+
@settings = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
def add_setting(name, default)
|
33
|
+
@_mutex.synchronize do
|
34
|
+
@settings[name] = default
|
35
|
+
define_setting_reader(name)
|
36
|
+
define_setting_writter(name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def add_nesting(constant, name = default_nesting_name(constant))
|
42
|
+
@_mutex.synchronize do
|
43
|
+
define_nesting_reader(constant, name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def define_setting_reader(name)
|
50
|
+
define_singleton_method(name) { @settings[name] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def define_setting_writter(name)
|
54
|
+
define_singleton_method(:"#{name}=") do |value|
|
55
|
+
@_mutex.synchronize do
|
56
|
+
@settings[name] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_nesting_reader(constant, name)
|
62
|
+
define_singleton_method(name) { constant.config }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
def self.extended(klass)
|
68
|
+
klass.instance_variable_set(:@config, Config.new)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the configuration class
|
72
|
+
#
|
73
|
+
# Use this class to access readers and writers for the defined settings or
|
74
|
+
# nested configurations
|
75
|
+
#
|
76
|
+
# @return [Configurable::Config]
|
77
|
+
def config
|
78
|
+
@config
|
79
|
+
end
|
80
|
+
|
81
|
+
# Yields the configuration class
|
82
|
+
#
|
83
|
+
# @see #config
|
84
|
+
def configure
|
85
|
+
yield @config
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
def setting(name, default:)
|
90
|
+
config.add_setting(name, default)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
def nest_config(constant, name: default_nesting_name(constant))
|
95
|
+
config.add_nesting(constant, name)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def default_nesting_name(constant)
|
101
|
+
constant.name
|
102
|
+
.split("::")
|
103
|
+
.last
|
104
|
+
.gsub(/([[:alpha:]])([[:upper:]])/, '\1_\2')
|
105
|
+
.downcase
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/omnes/errors.rb
CHANGED
@@ -18,7 +18,7 @@ module Omnes
|
|
18
18
|
def default_message
|
19
19
|
<<~MSG
|
20
20
|
'#{event_name}' event is not registered.
|
21
|
-
#{suggestions_message}
|
21
|
+
#{suggestions_message if defined?(DidYouMean::PlainFormatter)}
|
22
22
|
|
23
23
|
All known events are:
|
24
24
|
|
@@ -98,4 +98,42 @@ module Omnes
|
|
98
98
|
MSG
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
# Raised when given subscription id is already in use
|
103
|
+
class DuplicateSubscriptionIdError < Error
|
104
|
+
attr_reader :id, :bus
|
105
|
+
|
106
|
+
def initialize(id:, bus:)
|
107
|
+
@id = id
|
108
|
+
@bus = bus
|
109
|
+
super(default_message)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def default_message
|
115
|
+
<<~MSG
|
116
|
+
#{id} has already been used as a subscription identifier
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Raised when trying to set an invalid subscription identifier
|
122
|
+
class InvalidSubscriptionNameError < Error
|
123
|
+
attr_reader :id
|
124
|
+
|
125
|
+
def initialize(id:)
|
126
|
+
@id = id
|
127
|
+
super(default_message)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def default_message
|
133
|
+
<<~MSG
|
134
|
+
#{id.inspect} is not a valid subscription identifier. Only symbols are
|
135
|
+
#allowed.
|
136
|
+
MSG
|
137
|
+
end
|
138
|
+
end
|
101
139
|
end
|