dry-events 0.1.0 → 0.1.1
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.yml +4 -0
- data/CHANGELOG.md +31 -1
- data/Gemfile +2 -1
- data/README.md +4 -6
- data/lib/dry/events/bus.rb +8 -9
- data/lib/dry/events/event.rb +0 -5
- data/lib/dry/events/filter.rb +72 -0
- data/lib/dry/events/listener.rb +1 -1
- data/lib/dry/events/publisher.rb +12 -9
- data/lib/dry/events/version.rb +1 -1
- data/spec/spec_helper.rb +4 -1
- data/spec/unit/dry/events/filter_spec.rb +74 -0
- data/spec/unit/dry/events/listener_spec.rb +18 -4
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f18471031f2c5dcd9d2ea488ab48f5d42d9bd44de9158241dbb43324dd6fc0b4
|
4
|
+
data.tar.gz: '01160358743e52191c19bfc8b3a0550b4a201967f5fe96a8c60dfe50b560b2e1'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5d2e2a9920e5fe2241d5ae0cf42337169f4836353475aeb1bf4d8e621eac86b33143440a160ba5addf0b3cae548772f02c1b42bd86d88283e466835714f42bd
|
7
|
+
data.tar.gz: 6b4b8cdc169c69523dc8ba29362113d4fd7b127d3532f80572bf687c44b07c02fbe95788d2356a1316b363e54fab1ad8db3cd4801823aecce0f59926f9ec5e9b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
-
# v0.1.
|
1
|
+
# v0.1.1 2019-03-22
|
2
|
+
|
3
|
+
## Added
|
4
|
+
|
5
|
+
- Subscription filters can be more complex: nested hash inclusion, array inclusion, and proc checks were added (flash-gordon)
|
6
|
+
```ruby
|
7
|
+
# nested hash check
|
8
|
+
subscribe(:event, logger: { level: :info })
|
9
|
+
# pass
|
10
|
+
trigger(:event, logger: { level: :info, output: :stdin })
|
11
|
+
# filtered out
|
12
|
+
trigger(:event, logger: { level: :debug })
|
13
|
+
trigger(:event, something: :else)
|
14
|
+
|
15
|
+
# array inclusion
|
16
|
+
subscribe(:event, logger: { level: %i(info warn error) })
|
17
|
+
# pass
|
18
|
+
trigger(:event, logger: { level: :info })
|
19
|
+
trigger(:event, logger: { level: :error })
|
20
|
+
trigger(:event, logger: { level: :info, output: :stdin })
|
21
|
+
# filtered out
|
22
|
+
trigger(:event, logger: { level: :debug })
|
23
|
+
|
24
|
+
# proc checks
|
25
|
+
# here acts as array inclusion example
|
26
|
+
subscribe(:event, logger: { level: -> level { %i(info warn error).include?(level) })
|
27
|
+
```
|
28
|
+
|
29
|
+
[Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-events/compare/v0.1.0...v0.1.1)
|
30
|
+
|
31
|
+
# v0.1.0 2018-01-02
|
2
32
|
|
3
33
|
First public release
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-events
|
2
2
|
[travis]: https://travis-ci.org/dry-rb/dry-events
|
3
|
-
[gemnasium]: https://gemnasium.com/dry-rb/dry-events
|
4
3
|
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-events
|
5
4
|
[coveralls]: https://coveralls.io/r/dry-rb/dry-events
|
6
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-events
|
@@ -9,18 +8,17 @@
|
|
9
8
|
|
10
9
|
[][gem]
|
11
10
|
[][travis]
|
12
|
-
[][gemnasium]
|
13
11
|
[][codeclimate]
|
14
12
|
[][codeclimate]
|
15
13
|
[][inchpages]
|
16
14
|
|
17
15
|
Standalone pub/sub system.
|
18
16
|
|
19
|
-
##
|
17
|
+
## Links
|
20
18
|
|
21
|
-
|
22
|
-
|
19
|
+
* [User docs](http://dry-rb.org/gems/dry-events)
|
20
|
+
* [API docs](http://rubydoc.info/gems/dry-events)
|
23
21
|
|
24
|
-
##
|
22
|
+
## LICENSE
|
25
23
|
|
26
24
|
See `LICENSE` file.
|
data/lib/dry/events/bus.rb
CHANGED
@@ -18,7 +18,6 @@ module Dry
|
|
18
18
|
|
19
19
|
# Initialize a new event bus
|
20
20
|
#
|
21
|
-
# @param [Symbol] id The bus identifier
|
22
21
|
# @param [Hash] events A hash with events
|
23
22
|
# @param [Hash] listeners A hash with listeners
|
24
23
|
#
|
@@ -29,11 +28,11 @@ module Dry
|
|
29
28
|
end
|
30
29
|
|
31
30
|
# @api private
|
32
|
-
def process(event_id, payload
|
33
|
-
listeners[event_id].each do |
|
31
|
+
def process(event_id, payload)
|
32
|
+
listeners[event_id].each do |listener, filter|
|
34
33
|
event = events[event_id].payload(payload)
|
35
34
|
|
36
|
-
if
|
35
|
+
if filter.(payload)
|
37
36
|
yield(event, listener)
|
38
37
|
end
|
39
38
|
end
|
@@ -47,12 +46,12 @@ module Dry
|
|
47
46
|
end
|
48
47
|
|
49
48
|
# @api private
|
50
|
-
def attach(listener,
|
49
|
+
def attach(listener, filter)
|
51
50
|
events.each do |id, event|
|
52
51
|
meth = event.listener_method
|
53
52
|
|
54
53
|
if listener.respond_to?(meth)
|
55
|
-
listeners[id] << [listener.method(meth),
|
54
|
+
listeners[id] << [listener.method(meth), filter]
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|
@@ -68,14 +67,14 @@ module Dry
|
|
68
67
|
end
|
69
68
|
|
70
69
|
# @api private
|
71
|
-
def subscribe(event_id,
|
72
|
-
listeners[event_id] << [block,
|
70
|
+
def subscribe(event_id, filter, &block)
|
71
|
+
listeners[event_id] << [block, filter]
|
73
72
|
self
|
74
73
|
end
|
75
74
|
|
76
75
|
# @api private
|
77
76
|
def subscribed?(listener)
|
78
|
-
listeners.values.any? { |value| value.any? { |
|
77
|
+
listeners.values.any? { |value| value.any? { |block, _| block.equal?(listener) } }
|
79
78
|
end
|
80
79
|
end
|
81
80
|
end
|
data/lib/dry/events/event.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Events
|
5
|
+
# Event filter
|
6
|
+
#
|
7
|
+
# A filter cherry-picks probes payload of events.
|
8
|
+
# Events not matching the predicates don't fire callbacks.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Filter
|
12
|
+
NO_MATCH = Object.new.freeze
|
13
|
+
|
14
|
+
# @!attribute [r] events
|
15
|
+
# @return [Array] A list of lambdas checking payloads
|
16
|
+
attr_reader :checks
|
17
|
+
|
18
|
+
# Create a new filter
|
19
|
+
#
|
20
|
+
# @param [Hash] filter Source filter
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def initialize(filter)
|
24
|
+
@checks = build_checks(filter)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Test event payload against the checks
|
28
|
+
#
|
29
|
+
# @param [Hash] payload Event payload
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def call(payload = EMPTY_HASH)
|
33
|
+
checks.all? { |check| check.(payload) }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Recursively build checks
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
def build_checks(filter, checks = EMPTY_ARRAY, keys = EMPTY_ARRAY)
|
40
|
+
if filter.is_a?(Hash)
|
41
|
+
filter.reduce(checks) do |cs, (key, value)|
|
42
|
+
build_checks(value, cs, [*keys, key])
|
43
|
+
end
|
44
|
+
else
|
45
|
+
[*checks, method(:compare).curry.(keys, predicate(filter))]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def compare(path, predicate, payload)
|
51
|
+
value = path.reduce(payload) do |acc, key|
|
52
|
+
if acc.is_a?(Hash) && acc.key?(key)
|
53
|
+
acc[key]
|
54
|
+
else
|
55
|
+
break NO_MATCH
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
predicate.(value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
def predicate(value)
|
64
|
+
case value
|
65
|
+
when Proc then value
|
66
|
+
when Array then value.method(:include?)
|
67
|
+
else value.method(:==)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/dry/events/listener.rb
CHANGED
data/lib/dry/events/publisher.rb
CHANGED
@@ -5,6 +5,7 @@ require 'dry/core/class_attributes'
|
|
5
5
|
require 'dry/events/constants'
|
6
6
|
require 'dry/events/event'
|
7
7
|
require 'dry/events/bus'
|
8
|
+
require 'dry/events/filter'
|
8
9
|
|
9
10
|
module Dry
|
10
11
|
module Events
|
@@ -116,13 +117,13 @@ module Dry
|
|
116
117
|
# Subscribe to an event
|
117
118
|
#
|
118
119
|
# @param [Symbol,String] event_id The event identifier
|
119
|
-
# @param [Hash]
|
120
|
+
# @param [Hash] filter_hash An optional filter for conditional listeners
|
120
121
|
#
|
121
122
|
# @return [Class] publisher class
|
122
123
|
#
|
123
124
|
# @api public
|
124
|
-
def subscribe(event_id,
|
125
|
-
listeners[event_id] << [block,
|
125
|
+
def subscribe(event_id, filter_hash = EMPTY_HASH, &block)
|
126
|
+
listeners[event_id] << [block, Filter.new(filter_hash)]
|
126
127
|
self
|
127
128
|
end
|
128
129
|
|
@@ -180,19 +181,21 @@ module Dry
|
|
180
181
|
|
181
182
|
# Subscribe to events.
|
182
183
|
#
|
183
|
-
# If the
|
184
|
+
# If the filter parameter is provided, filters events by payload.
|
184
185
|
#
|
185
186
|
# @param [Symbol,String,Object] object_or_event_id The event identifier or a listener object
|
186
|
-
# @param [Hash]
|
187
|
+
# @param [Hash] filter_hash An optional event filter
|
187
188
|
#
|
188
189
|
# @return [Object] self
|
189
190
|
#
|
190
191
|
# @api public
|
191
|
-
def subscribe(object_or_event_id,
|
192
|
+
def subscribe(object_or_event_id, filter_hash = EMPTY_HASH, &block)
|
193
|
+
filter = Filter.new(filter_hash)
|
194
|
+
|
192
195
|
if block
|
193
|
-
__bus__.subscribe(object_or_event_id,
|
196
|
+
__bus__.subscribe(object_or_event_id, filter, &block)
|
194
197
|
else
|
195
|
-
__bus__.attach(object_or_event_id,
|
198
|
+
__bus__.attach(object_or_event_id, filter)
|
196
199
|
end
|
197
200
|
self
|
198
201
|
end
|
@@ -217,7 +220,7 @@ module Dry
|
|
217
220
|
|
218
221
|
# Utility method which yields event with each of its listeners
|
219
222
|
#
|
220
|
-
# Listeners are already filtered out when
|
223
|
+
# Listeners are already filtered out when filter was provided during
|
221
224
|
# subscription
|
222
225
|
#
|
223
226
|
# @param [Symbol,String] event_id The event identifier
|
data/lib/dry/events/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -6,7 +6,8 @@ if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
|
|
6
6
|
end
|
7
7
|
|
8
8
|
begin
|
9
|
-
require '
|
9
|
+
require 'pry'
|
10
|
+
require 'pry-byebug'
|
10
11
|
rescue LoadError; end
|
11
12
|
|
12
13
|
require 'dry-events'
|
@@ -17,7 +18,9 @@ Dir[SPEC_ROOT.join('shared/**/*.rb')].each(&method(:require))
|
|
17
18
|
Dir[SPEC_ROOT.join('support/**/*.rb')].each(&method(:require))
|
18
19
|
|
19
20
|
RSpec.configure do |config|
|
21
|
+
config.warnings = true
|
20
22
|
config.disable_monkey_patching!
|
23
|
+
config.filter_run_when_matching :focus
|
21
24
|
|
22
25
|
config.after(:example) do
|
23
26
|
Dry::Events::Publisher.instance_variable_set(:@__registry__, Concurrent::Map.new)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
RSpec.describe Dry::Events::Filter do
|
2
|
+
subject(:filter) { described_class.new(query) }
|
3
|
+
|
4
|
+
context 'nested hash' do
|
5
|
+
let(:query) do
|
6
|
+
{ logger: { level: :info } }
|
7
|
+
end
|
8
|
+
|
9
|
+
specify do
|
10
|
+
expect(filter.()).to be false
|
11
|
+
expect(filter.(logger: { level: :info, output: :stdin })).to be true
|
12
|
+
expect(filter.(logger: { level: :debug })).to be false
|
13
|
+
expect(filter.(logger: :debug)).to be false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'multi-value check' do
|
18
|
+
let(:query) do
|
19
|
+
{ logger: { level: :info, output: :stdin } }
|
20
|
+
end
|
21
|
+
|
22
|
+
specify do
|
23
|
+
expect(filter.()).to be false
|
24
|
+
expect(filter.(logger: { level: :info, output: :stdin })).to be true
|
25
|
+
expect(filter.(logger: { level: :info })).to be false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'top-level array' do
|
30
|
+
let(:query) { %i(error fatal) }
|
31
|
+
|
32
|
+
specify do
|
33
|
+
expect(filter.()).to be false
|
34
|
+
expect(filter.(random: :hash)).to be false
|
35
|
+
expect(filter.(:error)).to be true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'nested array' do
|
40
|
+
let(:query) do
|
41
|
+
{ logger: { level: %i(info warn error fatal) } }
|
42
|
+
end
|
43
|
+
|
44
|
+
specify do
|
45
|
+
expect(filter.()).to be false
|
46
|
+
expect(filter.(logger: { level: :info, output: :stdin })).to be true
|
47
|
+
expect(filter.(logger: { level: :fatal })).to be true
|
48
|
+
expect(filter.(logger: { level: :debug })).to be false
|
49
|
+
expect(filter.(level: :debug)).to be false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'nested proc' do
|
54
|
+
let(:query) do
|
55
|
+
{ logger: { level: -> level { %i(error fatal).include?(level) } } }
|
56
|
+
end
|
57
|
+
|
58
|
+
specify do
|
59
|
+
expect(filter.()).to be false
|
60
|
+
expect(filter.(logger: { level: :error, output: :stdin })).to be true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'top-level proc' do
|
65
|
+
let(:query) do
|
66
|
+
-> level: :debug, ** { %i(error fatal).include?(level) }
|
67
|
+
end
|
68
|
+
|
69
|
+
specify do
|
70
|
+
expect(filter.()).to be false
|
71
|
+
expect(filter.(level: :error, output: :stdin)).to be true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -16,16 +16,30 @@ RSpec.describe Dry::Events::Listener do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
describe '.subscribe' do
|
19
|
-
|
20
|
-
result = []
|
19
|
+
let(:captured) { [] }
|
21
20
|
|
21
|
+
it 'subscribes a listener at class level' do
|
22
22
|
listener.subscribe(:test_event) do |event|
|
23
|
-
|
23
|
+
captured << event.id
|
24
24
|
end
|
25
25
|
|
26
26
|
publisher.publish(:test_event)
|
27
27
|
|
28
|
-
expect(
|
28
|
+
expect(captured).to eql([:test_event])
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'filters' do
|
32
|
+
it 'filters events' do
|
33
|
+
listener.subscribe(:test_event, level: :info) do |event|
|
34
|
+
captured << event.payload
|
35
|
+
end
|
36
|
+
|
37
|
+
publisher.publish(:test_event)
|
38
|
+
publisher.publish(:test_event, level: :debug)
|
39
|
+
publisher.publish(:test_event, level: :info)
|
40
|
+
|
41
|
+
expect(captured).to eql([level: :info])
|
42
|
+
end
|
29
43
|
end
|
30
44
|
end
|
31
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-events
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -116,10 +116,12 @@ files:
|
|
116
116
|
- lib/dry/events/bus.rb
|
117
117
|
- lib/dry/events/constants.rb
|
118
118
|
- lib/dry/events/event.rb
|
119
|
+
- lib/dry/events/filter.rb
|
119
120
|
- lib/dry/events/listener.rb
|
120
121
|
- lib/dry/events/publisher.rb
|
121
122
|
- lib/dry/events/version.rb
|
122
123
|
- spec/spec_helper.rb
|
124
|
+
- spec/unit/dry/events/filter_spec.rb
|
123
125
|
- spec/unit/dry/events/listener_spec.rb
|
124
126
|
- spec/unit/dry/events/publisher_spec.rb
|
125
127
|
homepage: https://github.com/dry-rb/dry-events
|
@@ -141,12 +143,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
143
|
- !ruby/object:Gem::Version
|
142
144
|
version: '0'
|
143
145
|
requirements: []
|
144
|
-
|
145
|
-
rubygems_version: 2.7.3
|
146
|
+
rubygems_version: 3.0.1
|
146
147
|
signing_key:
|
147
148
|
specification_version: 4
|
148
149
|
summary: Pub/sub system
|
149
150
|
test_files:
|
150
151
|
- spec/spec_helper.rb
|
152
|
+
- spec/unit/dry/events/filter_spec.rb
|
151
153
|
- spec/unit/dry/events/listener_spec.rb
|
152
154
|
- spec/unit/dry/events/publisher_spec.rb
|