active_event_store 0.1.0 → 1.0.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/CHANGELOG.md +19 -0
- data/LICENSE.txt +1 -1
- data/README.md +28 -7
- data/lib/active_event_store/config.rb +8 -2
- data/lib/active_event_store/engine.rb +6 -2
- data/lib/active_event_store/event.rb +13 -1
- data/lib/active_event_store/mapper.rb +13 -13
- data/lib/active_event_store/rspec/have_enqueued_async_subscriber_for.rb +7 -4
- data/lib/active_event_store/rspec/have_published_event.rb +1 -1
- data/lib/active_event_store/subscriber_job.rb +1 -1
- data/lib/active_event_store/test_helper/event_published_matcher.rb +150 -0
- data/lib/active_event_store/test_helper.rb +70 -0
- data/lib/active_event_store/version.rb +1 -1
- data/lib/active_event_store.rb +2 -2
- metadata +25 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e19560b568e8765b86d3925070dd1414b0de0545107e1dc1c7df4e3402148690
|
|
4
|
+
data.tar.gz: 87816ce897dc26614f8834993373bb271345c25934142f055547ad0f5023fe22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: acf6017590c69d26066617c86ab63cd658c2307e8da89c53ec0afe18ebb4b384430bde9f632806f4e92a26293ed37cffa1c7abaa55ca2cd5f312d70d98ba873a
|
|
7
|
+
data.tar.gz: 181e900564fb3640a5484045ebbe6170b3871e05ea10a8e443bcadf7a530bf58bc227f4101d998a92ff96b82b569d1d24adcc1f97d3195c57fa2f0023c7db7db
|
data/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## master
|
|
4
4
|
|
|
5
|
+
## 1.0.1 (2021-09-16)
|
|
6
|
+
|
|
7
|
+
- Add minitest assertions: `assert_event_published`, `refute_event_published`, `assert_async_event_subscriber_enqueued` ([@chriscz][])
|
|
8
|
+
|
|
9
|
+
## 1.0.0 (2021-09-14)
|
|
10
|
+
|
|
11
|
+
- Ruby 2.6+, Rails 6+ and RailsEventStore 2.1+ is required.
|
|
12
|
+
|
|
13
|
+
## 0.2.1 (2020-09-30)
|
|
14
|
+
|
|
15
|
+
- Fix Active Support load hook name. ([@palkan][])
|
|
16
|
+
|
|
17
|
+
Now `ActiveSupport.on_load(:active_event_store) { ... }` works.
|
|
18
|
+
|
|
19
|
+
## 0.2.0 (2020-05-11)
|
|
20
|
+
|
|
21
|
+
- Update Event API to support both RES 1.0 and 0.42+. ([@palkan][])
|
|
22
|
+
|
|
5
23
|
## 0.1.0 (2020-04-22)
|
|
6
24
|
|
|
7
25
|
- Open source Active Event Store. ([@palkan][])
|
|
8
26
|
|
|
9
27
|
[@palkan]: https://github.com/palkan
|
|
28
|
+
[@chriscz]: https://github.com/chriscz
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -17,11 +17,11 @@ Secondly, we wanted to have a store implementation independent API that would al
|
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
20
|
-
Add gem to your project:
|
|
20
|
+
Add the gem to your project:
|
|
21
21
|
|
|
22
22
|
```ruby
|
|
23
23
|
# Gemfile
|
|
24
|
-
gem "active_event_store"
|
|
24
|
+
gem "active_event_store", "~> 1.0"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Setup database according to the [Rails Event Store docs](https://railseventstore.org/docs/install/#setup-data-model):
|
|
@@ -33,8 +33,9 @@ rails db:migrate
|
|
|
33
33
|
|
|
34
34
|
### Requirements
|
|
35
35
|
|
|
36
|
-
- Ruby (MRI) >= 2.
|
|
37
|
-
- Rails >=
|
|
36
|
+
- Ruby (MRI) >= 2.6
|
|
37
|
+
- Rails >= 6.0
|
|
38
|
+
- RailsEventStore >= 2.1
|
|
38
39
|
|
|
39
40
|
## Usage
|
|
40
41
|
|
|
@@ -143,24 +144,40 @@ Subscribers could be any callable Ruby objects that accept a single argument (ev
|
|
|
143
144
|
|
|
144
145
|
We suggest putting subscribers to the `app/subscribers` folder using the following convention: `app/subscribers/on_<event_type>/<subscriber.rb>`, e.g. `app/subscribers/on_profile_created/create_chat_user.rb`.
|
|
145
146
|
|
|
147
|
+
**NOTE:** Active Job must be loaded to use async subscribers (i.e., `require "active_job/railtie"` or `require "rails/all"` in your `config/application.rb`).
|
|
148
|
+
|
|
146
149
|
### Testing
|
|
147
150
|
|
|
148
151
|
You can test subscribers as normal Ruby objects.
|
|
149
152
|
|
|
150
|
-
**NOTE
|
|
153
|
+
**NOTE** To test using minitest include the `ActiveEventStore::TestHelpers` module in your tests.
|
|
151
154
|
|
|
152
155
|
To test that a given subscriber exists, you can use the `have_enqueued_async_subscriber_for` matcher:
|
|
153
156
|
|
|
154
157
|
```ruby
|
|
155
|
-
# for asynchronous subscriptions
|
|
158
|
+
# for asynchronous subscriptions (rspec)
|
|
156
159
|
it "is subscribed to some event" do
|
|
157
160
|
event = MyEvent.new(some: "data")
|
|
158
161
|
expect { ActiveEventStore.publish event }
|
|
159
162
|
.to have_enqueued_async_subscriber_for(MySubscriberService)
|
|
160
163
|
.with(event)
|
|
161
164
|
end
|
|
165
|
+
|
|
166
|
+
# for asynchronous subscriptions (minitest)
|
|
167
|
+
def test_is_subscribed_to_some_event
|
|
168
|
+
event = MyEvent.new(some: "data")
|
|
169
|
+
|
|
170
|
+
assert_async_event_subscriber_enqueued(MySubscriberService, event: event) do
|
|
171
|
+
ActiveEventStore.publish event
|
|
172
|
+
end
|
|
173
|
+
end
|
|
162
174
|
```
|
|
163
175
|
|
|
176
|
+
**NOTE** Async event subscribers are queued only after the current transaction has committed so when using `assert_enqued_async_subcriber` in rails
|
|
177
|
+
make sure to have `self.use_transactional_fixtures = false` at the top of your test class.
|
|
178
|
+
|
|
179
|
+
**NOTE:** You must have `rspec-rails` gem in your bundle to use `have_enqueued_async_subscriber_for` matcher.
|
|
180
|
+
|
|
164
181
|
For synchronous subscribers using `have_received` is enough:
|
|
165
182
|
|
|
166
183
|
```ruby
|
|
@@ -178,10 +195,14 @@ end
|
|
|
178
195
|
To test event publishing, use `have_published_event` matcher:
|
|
179
196
|
|
|
180
197
|
```ruby
|
|
198
|
+
# rspec
|
|
181
199
|
expect { subject }.to have_published_event(ProfileCreated).with(user_id: user.id)
|
|
200
|
+
|
|
201
|
+
# minitest
|
|
202
|
+
assert_event_published(ProfileCreated, with: {user_id: user.id}) { subject }
|
|
182
203
|
```
|
|
183
204
|
|
|
184
|
-
**NOTE:** `have_published_event` only supports block expectations.
|
|
205
|
+
**NOTE:** `have_published_event` and `assert_event_published` only supports block expectations.
|
|
185
206
|
|
|
186
207
|
**NOTE 2** `with` modifier works like `have_attributes` matcher (not `contain_exactly`); you can only specify serializable attributes in `with` (i.e. sync attributes are not supported, 'cause they are not persistent).
|
|
187
208
|
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
3
5
|
module ActiveEventStore
|
|
4
6
|
class Config
|
|
5
|
-
attr_writer :repository, :job_queue_name, :store_options
|
|
7
|
+
attr_writer :repository, :serializer, :job_queue_name, :store_options
|
|
6
8
|
|
|
7
9
|
def repository
|
|
8
|
-
@repository ||= RailsEventStoreActiveRecord::EventRepository.new
|
|
10
|
+
@repository ||= RailsEventStoreActiveRecord::EventRepository.new(serializer: serializer)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def serializer
|
|
14
|
+
@serializer ||= JSON
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
def job_queue_name
|
|
@@ -18,7 +18,11 @@ module ActiveEventStore
|
|
|
18
18
|
# See https://railseventstore.org/docs/subscribe/#scheduling-async-handlers-after-commit
|
|
19
19
|
ActiveEventStore.event_store = RailsEventStore::Client.new(
|
|
20
20
|
dispatcher: RubyEventStore::ComposedDispatcher.new(
|
|
21
|
-
RailsEventStore::AfterCommitAsyncDispatcher.new(
|
|
21
|
+
RailsEventStore::AfterCommitAsyncDispatcher.new(
|
|
22
|
+
scheduler: RailsEventStore::ActiveJobScheduler.new(
|
|
23
|
+
serializer: ActiveEventStore.config.serializer
|
|
24
|
+
)
|
|
25
|
+
),
|
|
22
26
|
RubyEventStore::Dispatcher.new
|
|
23
27
|
),
|
|
24
28
|
repository: ActiveEventStore.config.repository,
|
|
@@ -26,7 +30,7 @@ module ActiveEventStore
|
|
|
26
30
|
**ActiveEventStore.config.store_options
|
|
27
31
|
)
|
|
28
32
|
|
|
29
|
-
ActiveSupport.run_load_hooks(
|
|
33
|
+
ActiveSupport.run_load_hooks(:active_event_store, ActiveEventStore)
|
|
30
34
|
end
|
|
31
35
|
end
|
|
32
36
|
end
|
|
@@ -72,8 +72,20 @@ module ActiveEventStore
|
|
|
72
72
|
self.class.identifier
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
alias_method :event_type, :type
|
|
76
|
+
|
|
75
77
|
def inspect
|
|
76
|
-
"#{self.class.name}<#{
|
|
78
|
+
"#{self.class.name}<#{event_type}##{message_id}>, data: #{data}, metadata: #{metadata}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Has been removed from RES: https://github.com/RailsEventStore/rails_event_store/pull/726
|
|
82
|
+
def to_h
|
|
83
|
+
{
|
|
84
|
+
event_id: event_id,
|
|
85
|
+
metadata: metadata.to_h,
|
|
86
|
+
data: data,
|
|
87
|
+
type: event_type
|
|
88
|
+
}
|
|
77
89
|
end
|
|
78
90
|
|
|
79
91
|
protected
|
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
3
|
module ActiveEventStore
|
|
6
|
-
using(Module.new
|
|
4
|
+
using(Module.new {
|
|
7
5
|
refine Hash do
|
|
8
6
|
def symbolize_keys
|
|
9
7
|
RubyEventStore::TransformKeys.symbolize(self)
|
|
10
8
|
end
|
|
11
9
|
end
|
|
12
|
-
|
|
10
|
+
})
|
|
13
11
|
|
|
14
12
|
# Custom mapper for RES events.
|
|
15
13
|
#
|
|
16
14
|
# See https://github.com/RailsEventStore/rails_event_store/blob/v0.35.0/ruby_event_store/lib/ruby_event_store/mappers/default.rb
|
|
17
15
|
class Mapper
|
|
18
|
-
def initialize(mapping:, serializer:
|
|
16
|
+
def initialize(mapping:, serializer: ActiveEventStore.config.serializer)
|
|
19
17
|
@serializer = serializer
|
|
20
18
|
@mapping = mapping
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
def
|
|
21
|
+
def event_to_record(domain_event)
|
|
24
22
|
# lazily add type to mapping
|
|
25
23
|
# NOTE: use class name instead of a class to handle code reload
|
|
26
24
|
# in development (to avoid accessing orphaned classes)
|
|
27
|
-
mapping.register(domain_event.
|
|
25
|
+
mapping.register(domain_event.event_type, domain_event.class.name) unless mapping.exist?(domain_event.event_type)
|
|
28
26
|
|
|
29
|
-
RubyEventStore::
|
|
27
|
+
RubyEventStore::Record.new(
|
|
30
28
|
event_id: domain_event.event_id,
|
|
31
29
|
metadata: serializer.dump(domain_event.metadata.to_h),
|
|
32
30
|
data: serializer.dump(domain_event.data),
|
|
33
|
-
event_type: domain_event.
|
|
31
|
+
event_type: domain_event.event_type,
|
|
32
|
+
timestamp: domain_event.timestamp,
|
|
33
|
+
valid_at: domain_event.valid_at
|
|
34
34
|
)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
event_class = mapping.fetch(record.event_type)
|
|
37
|
+
def record_to_event(record)
|
|
38
|
+
event_class = mapping.fetch(record.event_type) {
|
|
39
39
|
raise "Don't know how to deserialize event: \"#{record.event_type}\". " \
|
|
40
|
-
"Add explicit mapping: ActiveEventStore.
|
|
41
|
-
|
|
40
|
+
"Add explicit mapping: ActiveEventStore.mapping.register \"#{record.event_type}\", \"<Class Name>\""
|
|
41
|
+
}
|
|
42
42
|
|
|
43
43
|
Object.const_get(event_class).new(
|
|
44
44
|
**serializer.load(record.data).symbolize_keys,
|
|
@@ -21,9 +21,12 @@ module ActiveEventStore
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def matches?(actual_serialized)
|
|
24
|
-
actual = ActiveEventStore.event_store.deserialize(
|
|
24
|
+
actual = ActiveEventStore.event_store.deserialize(
|
|
25
|
+
**actual_serialized,
|
|
26
|
+
serializer: ActiveEventStore.config.serializer
|
|
27
|
+
)
|
|
25
28
|
|
|
26
|
-
actual.
|
|
29
|
+
actual.event_type == event.event_type && data_matches?(actual.data)
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
def description
|
|
@@ -62,9 +65,9 @@ module ActiveEventStore
|
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
RSpec.configure do |config|
|
|
65
|
-
config.include(Module.new
|
|
68
|
+
config.include(Module.new {
|
|
66
69
|
def have_enqueued_async_subscriber_for(*args)
|
|
67
70
|
ActiveEventStore::HaveEnqueuedAsyncSubscriberFor.new(*args)
|
|
68
71
|
end
|
|
69
|
-
|
|
72
|
+
})
|
|
70
73
|
end
|
|
@@ -66,7 +66,7 @@ module ActiveEventStore
|
|
|
66
66
|
|
|
67
67
|
@matching_events, @unmatching_events =
|
|
68
68
|
in_block_events.partition do |actual_event|
|
|
69
|
-
(event_class.identifier == actual_event.
|
|
69
|
+
(event_class.identifier == actual_event.event_type) &&
|
|
70
70
|
(attributes.nil? || attributes_match?(actual_event))
|
|
71
71
|
end
|
|
72
72
|
|
|
@@ -38,7 +38,7 @@ module ActiveEventStore
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def perform(payload)
|
|
41
|
-
event = event_store.deserialize(payload)
|
|
41
|
+
event = event_store.deserialize(**payload, serializer: ActiveEventStore.config.serializer)
|
|
42
42
|
|
|
43
43
|
event_store.with_metadata(**event.metadata.to_h) do
|
|
44
44
|
subscriber.call(event)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveEventStore
|
|
4
|
+
module TestHelper
|
|
5
|
+
class EventPublishedMatcher
|
|
6
|
+
attr_reader :attributes,
|
|
7
|
+
:matching_events
|
|
8
|
+
|
|
9
|
+
def initialize(expected_event_class, store: nil, with: nil, exactly: nil, at_least: nil, at_most: nil, refute: false)
|
|
10
|
+
@event_class = expected_event_class
|
|
11
|
+
@store = store || ActiveEventStore.event_store
|
|
12
|
+
@attributes = with
|
|
13
|
+
@refute = refute
|
|
14
|
+
|
|
15
|
+
count_expectations = {
|
|
16
|
+
exactly: exactly,
|
|
17
|
+
at_most: at_most,
|
|
18
|
+
at_least: at_least
|
|
19
|
+
}.reject { |_, v| v.nil? }
|
|
20
|
+
|
|
21
|
+
if count_expectations.length > 1
|
|
22
|
+
raise ArgumentError("Only one of :exactly, :at_least or :at_most can be specified")
|
|
23
|
+
elsif count_expectations.length == 0
|
|
24
|
+
@count_expectation_kind = :at_least
|
|
25
|
+
@expected_count = 1
|
|
26
|
+
else
|
|
27
|
+
@count_expectation_kind = count_expectations.keys.first
|
|
28
|
+
@expected_count = count_expectations.values.first
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def with_published_events(&block)
|
|
33
|
+
original_count = @store.read.count
|
|
34
|
+
block.call
|
|
35
|
+
in_block_events(original_count, @store.read.count)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def matches?(block)
|
|
39
|
+
raise ArgumentError, "#{assertion_name} only support block assertions" if block.nil?
|
|
40
|
+
|
|
41
|
+
events = with_published_events do
|
|
42
|
+
block.call
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@matching_events, @unmatching_events = partition_events(events)
|
|
46
|
+
|
|
47
|
+
mismatch_message = count_mismatch_message(@matching_events.size)
|
|
48
|
+
|
|
49
|
+
if mismatch_message
|
|
50
|
+
expectations = [
|
|
51
|
+
"Expected #{mismatch_message} #{@event_class.identifier}"
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
expectations << if refute?
|
|
55
|
+
report_events = @matching_events
|
|
56
|
+
"not to have been published"
|
|
57
|
+
else
|
|
58
|
+
report_events = @unmatching_events
|
|
59
|
+
"to have been published"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
expectations << "with attributes #{attributes.inspect}" unless attributes.nil?
|
|
63
|
+
|
|
64
|
+
expectations << expectations.pop + ", but"
|
|
65
|
+
|
|
66
|
+
expectations << if report_events.any?
|
|
67
|
+
report_events.inject("published the following events instead:") do |msg, event|
|
|
68
|
+
msg + "\n #{event.inspect}"
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
"hasn't published anything"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return expectations.join(" ")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def refute?
|
|
83
|
+
@refute
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def assertion_name
|
|
87
|
+
if refute?
|
|
88
|
+
"refute_event_published"
|
|
89
|
+
else
|
|
90
|
+
"assert_event_published"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def negate_on_refute(cond)
|
|
95
|
+
if refute?
|
|
96
|
+
!cond
|
|
97
|
+
else
|
|
98
|
+
cond
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def in_block_events(before_block_count, after_block_count)
|
|
103
|
+
count_difference = after_block_count - before_block_count
|
|
104
|
+
if count_difference.positive?
|
|
105
|
+
@store.read.backward.limit(count_difference).to_a
|
|
106
|
+
else
|
|
107
|
+
[]
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Partitions events into matching and unmatching
|
|
112
|
+
def partition_events(events)
|
|
113
|
+
events.partition { |e| self.class.event_matches?(@event_class, @attributes, e) }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def count_mismatch_message(actual_count)
|
|
117
|
+
case @count_expectation_kind
|
|
118
|
+
when :exactly
|
|
119
|
+
if negate_on_refute(actual_count != @expected_count)
|
|
120
|
+
"exactly #{@expected_count}"
|
|
121
|
+
end
|
|
122
|
+
when :at_most
|
|
123
|
+
if negate_on_refute(actual_count > @expected_count)
|
|
124
|
+
"at most #{@expected_count}"
|
|
125
|
+
end
|
|
126
|
+
when :at_least
|
|
127
|
+
if negate_on_refute(actual_count < @expected_count)
|
|
128
|
+
"at least #{@expected_count}"
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
raise ArgumentError, "Unrecognized expectation kind: #{@count_expectation_kind}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class << self
|
|
136
|
+
def event_matches?(event_class, attributes, event)
|
|
137
|
+
event_type_matches?(event_class, event) && event_data_matches?(attributes, event)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def event_type_matches?(event_class, event)
|
|
141
|
+
event_class.identifier == event.event_type
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def event_data_matches?(attributes, event)
|
|
145
|
+
(attributes.nil? || attributes.all? { |k, v| v == event.public_send(k) })
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_event_store/test_helper/event_published_matcher"
|
|
4
|
+
|
|
5
|
+
module ActiveEventStore
|
|
6
|
+
module TestHelper
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
include ActiveJob::TestHelper
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Asserts that the given event was published `exactly`, `at_least` or `at_most` number of times
|
|
14
|
+
# to a specific `store` `with` a particular hash of attributes.
|
|
15
|
+
def assert_event_published(expected_event, store: nil, with: nil, exactly: nil, at_least: nil, at_most: nil, &block)
|
|
16
|
+
matcher = EventPublishedMatcher.new(
|
|
17
|
+
expected_event,
|
|
18
|
+
store: store,
|
|
19
|
+
with: with,
|
|
20
|
+
exactly: exactly,
|
|
21
|
+
at_least: at_least,
|
|
22
|
+
at_most: at_most
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if (msg = matcher.matches?(block))
|
|
26
|
+
fail(msg)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
matcher.matching_events
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Asserts that the given event was *not* published `exactly`, `at_least` or `at_most` number of times
|
|
33
|
+
# to a specific `store` `with` a particular hash of attributes.
|
|
34
|
+
def refute_event_published(expected_event, store: nil, with: nil, exactly: nil, at_least: nil, at_most: nil, &block)
|
|
35
|
+
matcher = EventPublishedMatcher.new(
|
|
36
|
+
expected_event,
|
|
37
|
+
store: store,
|
|
38
|
+
with: with,
|
|
39
|
+
exactly: exactly,
|
|
40
|
+
at_least: at_least,
|
|
41
|
+
at_most: at_most,
|
|
42
|
+
refute: true
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (msg = matcher.matches?(block))
|
|
46
|
+
fail(msg)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def assert_async_event_subscriber_enqueued(subscriber_class, event: nil, queue: "events_subscribers", &block)
|
|
51
|
+
subscriber_job = ActiveEventStore::SubscriberJob.for(subscriber_class)
|
|
52
|
+
if subscriber_job.nil?
|
|
53
|
+
fail("No such async subscriber: #{subscriber_class.name}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
expected_event = event
|
|
57
|
+
event_matcher = ->(actual_event) { EventPublishedMatcher.event_matches?(expected_event, expected_event.data, actual_event) }
|
|
58
|
+
|
|
59
|
+
expected_args = if expected_event
|
|
60
|
+
event_matcher
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
assert_enqueued_with(job: subscriber_job, queue: queue, args: expected_args) do
|
|
64
|
+
ActiveRecord::Base.transaction do
|
|
65
|
+
block.call
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/active_event_store.rb
CHANGED
|
@@ -24,8 +24,8 @@ module ActiveEventStore
|
|
|
24
24
|
@config ||= Config.new
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def subscribe(subscriber = nil, to: nil, sync: false)
|
|
28
|
-
subscriber ||=
|
|
27
|
+
def subscribe(subscriber = nil, to: nil, sync: false, &block)
|
|
28
|
+
subscriber ||= block
|
|
29
29
|
|
|
30
30
|
to ||= infer_event_from_subscriber(subscriber) if subscriber.is_a?(Module)
|
|
31
31
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_event_store
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vladimir Dementyev
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-09-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails_event_store
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 2.1.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 2.1.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '3.8'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: minitest
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '5.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '5.0'
|
|
83
97
|
description: Wrapper over Rails Event Store with conventions and transparent Rails
|
|
84
98
|
integration
|
|
85
99
|
email:
|
|
@@ -101,6 +115,8 @@ files:
|
|
|
101
115
|
- lib/active_event_store/rspec/have_enqueued_async_subscriber_for.rb
|
|
102
116
|
- lib/active_event_store/rspec/have_published_event.rb
|
|
103
117
|
- lib/active_event_store/subscriber_job.rb
|
|
118
|
+
- lib/active_event_store/test_helper.rb
|
|
119
|
+
- lib/active_event_store/test_helper/event_published_matcher.rb
|
|
104
120
|
- lib/active_event_store/version.rb
|
|
105
121
|
homepage: http://github.com/palkan/active_event_store
|
|
106
122
|
licenses:
|
|
@@ -111,7 +127,7 @@ metadata:
|
|
|
111
127
|
documentation_uri: http://github.com/palkan/active_event_store
|
|
112
128
|
homepage_uri: http://github.com/palkan/active_event_store
|
|
113
129
|
source_code_uri: http://github.com/palkan/active_event_store
|
|
114
|
-
post_install_message:
|
|
130
|
+
post_install_message:
|
|
115
131
|
rdoc_options: []
|
|
116
132
|
require_paths:
|
|
117
133
|
- lib
|
|
@@ -119,15 +135,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
119
135
|
requirements:
|
|
120
136
|
- - ">="
|
|
121
137
|
- !ruby/object:Gem::Version
|
|
122
|
-
version: '2.
|
|
138
|
+
version: '2.6'
|
|
123
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
140
|
requirements:
|
|
125
141
|
- - ">="
|
|
126
142
|
- !ruby/object:Gem::Version
|
|
127
143
|
version: '0'
|
|
128
144
|
requirements: []
|
|
129
|
-
rubygems_version: 3.
|
|
130
|
-
signing_key:
|
|
145
|
+
rubygems_version: 3.2.15
|
|
146
|
+
signing_key:
|
|
131
147
|
specification_version: 4
|
|
132
148
|
summary: Rails Event Store in a more Rails way
|
|
133
149
|
test_files: []
|