ruby_event_store 0.25.2 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Makefile +2 -1
- data/lib/ruby_event_store/client.rb +87 -14
- data/lib/ruby_event_store/in_memory_repository.rb +11 -8
- data/lib/ruby_event_store/pub_sub/broker.rb +25 -1
- data/lib/ruby_event_store/spec/event_broker_lint.rb +67 -2
- data/lib/ruby_event_store/version.rb +1 -1
- data/ruby_event_store.gemspec +1 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a57244de5351622091fa442885288c1775dc5ad0
|
4
|
+
data.tar.gz: 2280e2361f7c8466484caa459822c28e213d1b4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d64e4f0c8d49aa4161df43acddd7e16ff16ea9c7e193826a3817224035b27f9953ec943da86126b8cb94f9e41ac3fd5d4ab29d8058038d19be3a0c4fd8e94523
|
7
|
+
data.tar.gz: e3cc5306c6ae098ed5d3544000a1a1aa87ae672a9c9a66510e64141ad813ae8b28123b8b24cf69125e0393c446a17aca7c077a5c4098849405e02607379d7c33
|
data/Makefile
CHANGED
@@ -5,6 +5,7 @@ IGNORE = RubyEventStore.const_missing \
|
|
5
5
|
RubyEventStore::InMemoryRepository\#append_with_synchronize \
|
6
6
|
RubyEventStore::InMemoryRepository\#normalize_to_array \
|
7
7
|
RubyEventStore::Client\#normalize_to_array \
|
8
|
+
RubyEventStore::Client::Within\#normalize_to_array \
|
8
9
|
RubyEventStore::SerializedRecord \
|
9
10
|
RubyEventStore::Projection\#read_events_from_stream \
|
10
11
|
RubyEventStore::Projection\#read_events_from_all_streams
|
@@ -20,7 +21,7 @@ test: ## Run unit tests
|
|
20
21
|
|
21
22
|
mutate: test ## Run mutation tests
|
22
23
|
@echo "Running mutation tests"
|
23
|
-
@bundle exec mutant --include lib \
|
24
|
+
@MUTATING=true bundle exec mutant --include lib \
|
24
25
|
$(addprefix --require ,$(REQUIRE)) \
|
25
26
|
$(addprefix --ignore-subject ,$(IGNORE)) \
|
26
27
|
--use rspec "$(SUBJECT)"
|
@@ -82,18 +82,99 @@ module RubyEventStore
|
|
82
82
|
@repository.get_all_streams
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
|
86
|
+
DEPRECATED_WITHIN = "subscribe(subscriber, event_types, &task) has been deprecated. Use within(&task).subscribe(subscriber, to: event_types).call instead"
|
87
|
+
DEPRECATED_TO = "subscribe(subscriber, event_types) has been deprecated. Use subscribe(subscriber, to: event_types) instead"
|
88
|
+
# OLD:
|
89
|
+
# subscribe(subscriber, event_types, &within)
|
90
|
+
# subscribe(subscriber, event_types)
|
91
|
+
# NEW:
|
92
|
+
# subscribe(subscriber, to:)
|
93
|
+
# subscribe(to:, &subscriber)
|
94
|
+
def subscribe(subscriber = nil, event_types = nil, to: nil, &proc)
|
95
|
+
if to
|
96
|
+
raise ArgumentError, "subscriber must be first argument or block, cannot be both" if subscriber && proc
|
97
|
+
raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber || proc
|
98
|
+
raise ArgumentError, "list of event types must be second argument or named argument to: , it cannot be both" if event_types
|
99
|
+
subscriber ||= proc
|
100
|
+
@event_broker.add_subscriber(subscriber, to)
|
101
|
+
else
|
102
|
+
if proc
|
103
|
+
warn(DEPRECATED_WITHIN)
|
104
|
+
within(&proc).subscribe(subscriber, to: event_types).call
|
105
|
+
-> {}
|
106
|
+
else
|
107
|
+
warn(DEPRECATED_TO)
|
108
|
+
subscribe(subscriber, to: event_types)
|
109
|
+
end
|
88
110
|
end
|
89
111
|
end
|
90
112
|
|
91
|
-
|
92
|
-
|
93
|
-
|
113
|
+
DEPRECATED_ALL_WITHIN = "subscribe_to_all_events(subscriber, &task) has been deprecated. Use within(&task).subscribe_to_all_events(subscriber).call instead."
|
114
|
+
# OLD:
|
115
|
+
# subscribe_to_all_events(subscriber, &within)
|
116
|
+
# subscribe_to_all_events(subscriber)
|
117
|
+
# NEW:
|
118
|
+
# subscribe_to_all_events(subscriber)
|
119
|
+
# subscribe_to_all_events(&subscriber)
|
120
|
+
def subscribe_to_all_events(subscriber = nil, &proc)
|
121
|
+
if subscriber
|
122
|
+
if proc
|
123
|
+
warn(DEPRECATED_ALL_WITHIN)
|
124
|
+
within(&proc).subscribe_to_all_events(subscriber).call
|
125
|
+
-> {}
|
126
|
+
else
|
127
|
+
@event_broker.add_global_subscriber(subscriber)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
@event_broker.add_global_subscriber(proc)
|
94
131
|
end
|
95
132
|
end
|
96
133
|
|
134
|
+
class Within
|
135
|
+
def initialize(block, event_broker)
|
136
|
+
@block = block
|
137
|
+
@event_broker = event_broker
|
138
|
+
@global_subscribers = []
|
139
|
+
@subscribers = Hash.new {[]}
|
140
|
+
end
|
141
|
+
|
142
|
+
def subscribe_to_all_events(*handlers, &handler2)
|
143
|
+
handlers << handler2 if handler2
|
144
|
+
@global_subscribers += handlers
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
def subscribe(handler=nil, to:, &handler2)
|
149
|
+
raise ArgumentError if handler && handler2
|
150
|
+
@subscribers[handler || handler2] += normalize_to_array(to)
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def call
|
155
|
+
unsubs = @global_subscribers.map do |s|
|
156
|
+
@event_broker.add_thread_global_subscriber(s)
|
157
|
+
end
|
158
|
+
unsubs += @subscribers.map do |handler, types|
|
159
|
+
@event_broker.add_thread_subscriber(handler, types)
|
160
|
+
end
|
161
|
+
@block.call
|
162
|
+
ensure
|
163
|
+
unsubs.each(&:call)
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def normalize_to_array(objs)
|
169
|
+
return *objs
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def within(&block)
|
174
|
+
raise ArgumentError if block.nil?
|
175
|
+
Within.new(block, @event_broker)
|
176
|
+
end
|
177
|
+
|
97
178
|
private
|
98
179
|
|
99
180
|
def normalize_to_array(events)
|
@@ -108,14 +189,6 @@ module RubyEventStore
|
|
108
189
|
# event.class.new(event_id: event.event_id, metadata: metadata, data: event.data)
|
109
190
|
end
|
110
191
|
|
111
|
-
def handle_subscribe(unsub, &proc)
|
112
|
-
begin
|
113
|
-
proc.call
|
114
|
-
ensure
|
115
|
-
unsub.()
|
116
|
-
end if proc
|
117
|
-
end
|
118
|
-
|
119
192
|
class Page
|
120
193
|
def initialize(repository, start, count)
|
121
194
|
if start.instance_of?(Symbol)
|
@@ -73,21 +73,20 @@ module RubyEventStore
|
|
73
73
|
def add_to_stream(events, expected_version, stream_name, include_global)
|
74
74
|
raise InvalidExpectedVersion if !expected_version.equal?(:any) && stream_name.eql?(GLOBAL_STREAM)
|
75
75
|
events = normalize_to_array(events)
|
76
|
-
stream = read_stream_events_forward(stream_name)
|
77
76
|
expected_version = case expected_version
|
78
77
|
when :none
|
79
78
|
-1
|
80
|
-
when :auto
|
81
|
-
|
82
|
-
when Integer
|
79
|
+
when :auto
|
80
|
+
read_stream_events_forward(stream_name).size - 1
|
81
|
+
when Integer, :any
|
83
82
|
expected_version
|
84
83
|
else
|
85
84
|
raise InvalidExpectedVersion
|
86
85
|
end
|
87
|
-
append_with_synchronize(events, expected_version,
|
86
|
+
append_with_synchronize(events, expected_version, stream_name, include_global)
|
88
87
|
end
|
89
88
|
|
90
|
-
def append_with_synchronize(events, expected_version,
|
89
|
+
def append_with_synchronize(events, expected_version, stream_name, include_global)
|
91
90
|
# expected_version :auto assumes external lock is used
|
92
91
|
# which makes reading stream before writing safe.
|
93
92
|
#
|
@@ -97,11 +96,15 @@ module RubyEventStore
|
|
97
96
|
# not for the whole read+write algorithm.
|
98
97
|
Thread.pass
|
99
98
|
@mutex.synchronize do
|
100
|
-
|
99
|
+
if expected_version == :any
|
100
|
+
expected_version = read_stream_events_forward(stream_name).size - 1
|
101
|
+
end
|
102
|
+
append(events, expected_version, stream_name, include_global)
|
101
103
|
end
|
102
104
|
end
|
103
105
|
|
104
|
-
def append(events, expected_version,
|
106
|
+
def append(events, expected_version, stream_name, include_global)
|
107
|
+
stream = read_stream_events_forward(stream_name)
|
105
108
|
raise WrongExpectedEventVersion unless (stream.size - 1).equal?(expected_version)
|
106
109
|
events.each do |event|
|
107
110
|
raise EventDuplicatedInStream if stream.any?{|ev| ev.event_id.eql?(event.event_id) }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
1
3
|
module RubyEventStore
|
2
4
|
module PubSub
|
3
5
|
class Broker
|
@@ -6,6 +8,12 @@ module RubyEventStore
|
|
6
8
|
def initialize(dispatcher: DEFAULT_DISPATCHER)
|
7
9
|
@subscribers = Hash.new {|hsh, key| hsh[key] = [] }
|
8
10
|
@global_subscribers = []
|
11
|
+
|
12
|
+
@thread_global_subscribers = Concurrent::ThreadLocalVar.new([])
|
13
|
+
@thread_subscribers = Concurrent::ThreadLocalVar.new do
|
14
|
+
Hash.new {|hsh, key| hsh[key] = [] }
|
15
|
+
end
|
16
|
+
|
9
17
|
@dispatcher = dispatcher
|
10
18
|
end
|
11
19
|
|
@@ -21,6 +29,19 @@ module RubyEventStore
|
|
21
29
|
->() { @global_subscribers.delete(subscriber) }
|
22
30
|
end
|
23
31
|
|
32
|
+
def add_thread_global_subscriber(subscriber)
|
33
|
+
verify_subscriber(subscriber)
|
34
|
+
@thread_global_subscribers.value += [subscriber]
|
35
|
+
|
36
|
+
->() { @thread_global_subscribers.value -= [subscriber] }
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_thread_subscriber(subscriber, event_types)
|
40
|
+
verify_subscriber(subscriber)
|
41
|
+
event_types.each{ |type| @thread_subscribers.value[type.name] << subscriber }
|
42
|
+
->() {event_types.each{ |type| @thread_subscribers.value.fetch(type.name).delete(subscriber) } }
|
43
|
+
end
|
44
|
+
|
24
45
|
def notify_subscribers(event)
|
25
46
|
all_subscribers_for(event.class).each do |subscriber|
|
26
47
|
@dispatcher.call(subscriber, event)
|
@@ -40,7 +61,10 @@ module RubyEventStore
|
|
40
61
|
end
|
41
62
|
|
42
63
|
def all_subscribers_for(event_type)
|
43
|
-
@subscribers[event_type.name] +
|
64
|
+
@subscribers[event_type.name] +
|
65
|
+
@global_subscribers +
|
66
|
+
@thread_global_subscribers.value +
|
67
|
+
@thread_subscribers.value[event_type.name]
|
44
68
|
end
|
45
69
|
end
|
46
70
|
end
|
@@ -35,10 +35,12 @@ RSpec.shared_examples :event_broker do |broker_class|
|
|
35
35
|
|
36
36
|
it 'raise error when no subscriber' do
|
37
37
|
expect { broker.add_subscriber(nil, [])}.to raise_error(RubyEventStore::SubscriberNotExist)
|
38
|
+
expect { broker.add_thread_subscriber(nil, [])}.to raise_error(RubyEventStore::SubscriberNotExist)
|
38
39
|
expect { broker.add_global_subscriber(nil)}.to raise_error(RubyEventStore::SubscriberNotExist)
|
40
|
+
expect { broker.add_thread_global_subscriber(nil)}.to raise_error(RubyEventStore::SubscriberNotExist)
|
39
41
|
end
|
40
42
|
|
41
|
-
it '
|
43
|
+
it 'notifies subscribed handlers' do
|
42
44
|
handler = TestHandler.new
|
43
45
|
another_handler = TestHandler.new
|
44
46
|
global_handler = TestHandler.new
|
@@ -60,6 +62,28 @@ RSpec.shared_examples :event_broker do |broker_class|
|
|
60
62
|
expect(global_handler.events).to eq([event1,event2,event3])
|
61
63
|
end
|
62
64
|
|
65
|
+
it 'notifies subscribed thread handlers' do
|
66
|
+
handler = TestHandler.new
|
67
|
+
another_handler = TestHandler.new
|
68
|
+
global_handler = TestHandler.new
|
69
|
+
|
70
|
+
broker.add_thread_subscriber(handler, [Test1DomainEvent, Test3DomainEvent])
|
71
|
+
broker.add_thread_subscriber(another_handler, [Test2DomainEvent])
|
72
|
+
broker.add_thread_global_subscriber(global_handler)
|
73
|
+
|
74
|
+
event1 = Test1DomainEvent.new
|
75
|
+
event2 = Test2DomainEvent.new
|
76
|
+
event3 = Test3DomainEvent.new
|
77
|
+
|
78
|
+
[event1, event2, event3].each do |ev|
|
79
|
+
broker.notify_subscribers(ev)
|
80
|
+
end
|
81
|
+
|
82
|
+
expect(handler.events).to eq([event1,event3])
|
83
|
+
expect(another_handler.events).to eq([event2])
|
84
|
+
expect(global_handler.events).to eq([event1,event2,event3])
|
85
|
+
end
|
86
|
+
|
63
87
|
it 'raises error when no valid method on handler' do
|
64
88
|
subscriber = InvalidTestHandler.new
|
65
89
|
expect do
|
@@ -74,13 +98,27 @@ RSpec.shared_examples :event_broker do |broker_class|
|
|
74
98
|
end.to raise_error(RubyEventStore::InvalidHandler)
|
75
99
|
end
|
76
100
|
|
101
|
+
it 'raises error when no valid method on thread handler' do
|
102
|
+
subscriber = InvalidTestHandler.new
|
103
|
+
expect do
|
104
|
+
broker.add_thread_subscriber(subscriber, [Test1DomainEvent])
|
105
|
+
end.to raise_error(RubyEventStore::InvalidHandler)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'raises error when no valid method on global thread handler' do
|
109
|
+
subscriber = InvalidTestHandler.new
|
110
|
+
expect do
|
111
|
+
broker.add_thread_global_subscriber(subscriber)
|
112
|
+
end.to raise_error(RubyEventStore::InvalidHandler)
|
113
|
+
end
|
114
|
+
|
77
115
|
it 'returns lambda as an output of global subscribe methods' do
|
78
116
|
handler = TestHandler.new
|
79
117
|
result = broker.add_global_subscriber(handler)
|
80
118
|
expect(result).to respond_to(:call)
|
81
119
|
end
|
82
120
|
|
83
|
-
it '
|
121
|
+
it 'returns lambda as an output of subscribe methods' do
|
84
122
|
handler = TestHandler.new
|
85
123
|
result = broker.add_subscriber(handler, [Test1DomainEvent, Test2DomainEvent])
|
86
124
|
expect(result).to respond_to(:call)
|
@@ -112,6 +150,32 @@ RSpec.shared_examples :event_broker do |broker_class|
|
|
112
150
|
expect(handler.events).to eq([event1])
|
113
151
|
end
|
114
152
|
|
153
|
+
it 'revokes thread global subscription' do
|
154
|
+
handler = TestHandler.new
|
155
|
+
event1 = Test1DomainEvent.new
|
156
|
+
event2 = Test2DomainEvent.new
|
157
|
+
|
158
|
+
revoke = broker.add_thread_global_subscriber(handler)
|
159
|
+
broker.notify_subscribers(event1)
|
160
|
+
expect(handler.events).to eq([event1])
|
161
|
+
revoke.()
|
162
|
+
broker.notify_subscribers(event2)
|
163
|
+
expect(handler.events).to eq([event1])
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'revokes thread subscription' do
|
167
|
+
handler = TestHandler.new
|
168
|
+
event1 = Test1DomainEvent.new
|
169
|
+
event2 = Test2DomainEvent.new
|
170
|
+
|
171
|
+
revoke = broker.add_thread_subscriber(handler, [Test1DomainEvent, Test2DomainEvent])
|
172
|
+
broker.notify_subscribers(event1)
|
173
|
+
expect(handler.events).to eq([event1])
|
174
|
+
revoke.()
|
175
|
+
broker.notify_subscribers(event2)
|
176
|
+
expect(handler.events).to eq([event1])
|
177
|
+
end
|
178
|
+
|
115
179
|
it 'allows to provide a custom dispatcher' do
|
116
180
|
dispatcher = TestDispatcher.new
|
117
181
|
handler = TestHandler.new
|
@@ -123,6 +187,7 @@ RSpec.shared_examples :event_broker do |broker_class|
|
|
123
187
|
end
|
124
188
|
|
125
189
|
private
|
190
|
+
|
126
191
|
class HandlerClass
|
127
192
|
@@received = nil
|
128
193
|
def self.received
|
data/ruby_event_store.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
|
23
23
|
spec.add_dependency 'activesupport'
|
24
|
+
spec.add_dependency 'concurrent-ruby', '~> 1.0'
|
24
25
|
spec.add_development_dependency 'bundler', '~> 1.15'
|
25
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
27
|
spec.add_development_dependency 'rspec', '~> 3.6'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_event_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkency
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
143
|
version: '0'
|
130
144
|
requirements: []
|
131
145
|
rubyforge_project:
|
132
|
-
rubygems_version: 2.
|
146
|
+
rubygems_version: 2.6.13
|
133
147
|
signing_key:
|
134
148
|
specification_version: 4
|
135
149
|
summary: Event Store in Ruby
|