omnes 0.1.0 → 0.2.2
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/.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
|