active_event_store 0.2.1 → 1.0.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/CHANGELOG.md +14 -0
- data/LICENSE.txt +1 -1
- data/README.md +26 -7
- data/lib/active_event_store/config.rb +8 -2
- data/lib/active_event_store/engine.rb +5 -1
- data/lib/active_event_store/event.rb +4 -12
- data/lib/active_event_store/mapper.rb +12 -12
- data/lib/active_event_store/rspec/have_enqueued_async_subscriber_for.rb +6 -3
- data/lib/active_event_store/subscriber_job.rb +10 -6
- 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: f4ccb82314fce7aa4504e6834af793ad82e4613ef970a0277664de839e72a543
|
4
|
+
data.tar.gz: 38ea7210977f150226cf63f3cdec4793210e054e207cf71bcfcf5643cab21b28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26ff53ad69fe94b9643031511072ef1d3b89e17eebae6b492570ad56829afc03ccb39c1ae16b26e3da038a549e4ae63c95eab117e1f4f0781e1202b32d6662b1
|
7
|
+
data.tar.gz: bb66fa72aed216e1328dc7b52737a273de13da41dcdc723c6250e015c7aee5574523c9ed6de876ae0525599076326f6bb847c5115eb146801f8e177dc04b13b4
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.0.2 (2021-03-15)
|
6
|
+
|
7
|
+
- Support using classes with `#call` as async subscribers. ([@caws][])
|
8
|
+
|
9
|
+
## 1.0.1 (2021-09-16)
|
10
|
+
|
11
|
+
- Add minitest assertions: `assert_event_published`, `refute_event_published`, `assert_async_event_subscriber_enqueued` ([@chriscz][])
|
12
|
+
|
13
|
+
## 1.0.0 (2021-09-14)
|
14
|
+
|
15
|
+
- Ruby 2.6+, Rails 6+ and RailsEventStore 2.1+ is required.
|
16
|
+
|
5
17
|
## 0.2.1 (2020-09-30)
|
6
18
|
|
7
19
|
- Fix Active Support load hook name. ([@palkan][])
|
@@ -17,3 +29,5 @@ Now `ActiveSupport.on_load(:active_event_store) { ... }` works.
|
|
17
29
|
- Open source Active Event Store. ([@palkan][])
|
18
30
|
|
19
31
|
[@palkan]: https://github.com/palkan
|
32
|
+
[@chriscz]: https://github.com/chriscz
|
33
|
+
[@caws]: https://github.com/caws
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -21,7 +21,7 @@ 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
|
|
@@ -139,30 +140,44 @@ ActiveSupport.on_load :active_event_store do |store|
|
|
139
140
|
end
|
140
141
|
```
|
141
142
|
|
142
|
-
Subscribers could be any callable Ruby objects that accept a single argument (event) as its input.
|
143
|
+
Subscribers could be any callable Ruby objects that accept a single argument (event) as its input or classes that inherit from `Class` and have `#call` as an instance method.
|
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
|
|
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`).
|
147
148
|
|
149
|
+
**NOTE:** Subscribers that inherit from `Class` and implement `call` as a class method will not be instantiated.
|
150
|
+
|
148
151
|
### Testing
|
149
152
|
|
150
153
|
You can test subscribers as normal Ruby objects.
|
151
154
|
|
152
|
-
**NOTE
|
155
|
+
**NOTE** To test using minitest include the `ActiveEventStore::TestHelpers` module in your tests.
|
153
156
|
|
154
157
|
To test that a given subscriber exists, you can use the `have_enqueued_async_subscriber_for` matcher:
|
155
158
|
|
156
159
|
```ruby
|
157
|
-
# for asynchronous subscriptions
|
160
|
+
# for asynchronous subscriptions (rspec)
|
158
161
|
it "is subscribed to some event" do
|
159
162
|
event = MyEvent.new(some: "data")
|
160
163
|
expect { ActiveEventStore.publish event }
|
161
164
|
.to have_enqueued_async_subscriber_for(MySubscriberService)
|
162
165
|
.with(event)
|
163
166
|
end
|
167
|
+
|
168
|
+
# for asynchronous subscriptions (minitest)
|
169
|
+
def test_is_subscribed_to_some_event
|
170
|
+
event = MyEvent.new(some: "data")
|
171
|
+
|
172
|
+
assert_async_event_subscriber_enqueued(MySubscriberService, event: event) do
|
173
|
+
ActiveEventStore.publish event
|
174
|
+
end
|
175
|
+
end
|
164
176
|
```
|
165
177
|
|
178
|
+
**NOTE** Async event subscribers are queued only after the current transaction has committed so when using `assert_enqued_async_subcriber` in rails
|
179
|
+
make sure to have `self.use_transactional_fixtures = false` at the top of your test class.
|
180
|
+
|
166
181
|
**NOTE:** You must have `rspec-rails` gem in your bundle to use `have_enqueued_async_subscriber_for` matcher.
|
167
182
|
|
168
183
|
For synchronous subscribers using `have_received` is enough:
|
@@ -182,10 +197,14 @@ end
|
|
182
197
|
To test event publishing, use `have_published_event` matcher:
|
183
198
|
|
184
199
|
```ruby
|
200
|
+
# rspec
|
185
201
|
expect { subject }.to have_published_event(ProfileCreated).with(user_id: user.id)
|
202
|
+
|
203
|
+
# minitest
|
204
|
+
assert_event_published(ProfileCreated, with: {user_id: user.id}) { subject }
|
186
205
|
```
|
187
206
|
|
188
|
-
**NOTE:** `have_published_event` only supports block expectations.
|
207
|
+
**NOTE:** `have_published_event` and `assert_event_published` only supports block expectations.
|
189
208
|
|
190
209
|
**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).
|
191
210
|
|
@@ -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,
|
@@ -68,20 +68,12 @@ module ActiveEventStore
|
|
68
68
|
super(**{event_id: event_id, metadata: metadata, data: params}.compact)
|
69
69
|
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
if method_defined?(:event_type)
|
74
|
-
def event_type
|
75
|
-
self.class.identifier
|
76
|
-
end
|
77
|
-
else
|
78
|
-
def type
|
79
|
-
self.class.identifier
|
80
|
-
end
|
81
|
-
|
82
|
-
alias event_type type
|
71
|
+
def type
|
72
|
+
self.class.identifier
|
83
73
|
end
|
84
74
|
|
75
|
+
alias_method :event_type, :type
|
76
|
+
|
85
77
|
def inspect
|
86
78
|
"#{self.class.name}<#{event_type}##{message_id}>, data: #{data}, metadata: #{metadata}"
|
87
79
|
end
|
@@ -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
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.event_type
|
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,7 +21,10 @@ 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
29
|
actual.event_type == event.event_type && data_matches?(actual.data)
|
27
30
|
end
|
@@ -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
|
@@ -14,11 +14,11 @@ module ActiveEventStore
|
|
14
14
|
|
15
15
|
raise ArgumentError, "Async subscriber must be a module/class, not instance" unless callable.is_a?(Module)
|
16
16
|
|
17
|
-
if callable.const_defined?(
|
18
|
-
callable.const_get(
|
17
|
+
if callable.const_defined?(:SubscriberJob, false)
|
18
|
+
callable.const_get(:SubscriberJob, false)
|
19
19
|
else
|
20
20
|
callable.const_set(
|
21
|
-
|
21
|
+
:SubscriberJob,
|
22
22
|
Class.new(self).tap do |job|
|
23
23
|
queue_as ActiveEventStore.config.job_queue_name
|
24
24
|
|
@@ -31,14 +31,14 @@ module ActiveEventStore
|
|
31
31
|
def for(callable)
|
32
32
|
raise ArgumentError, "Async subscriber must be a module/class" unless callable.is_a?(Module)
|
33
33
|
|
34
|
-
callable.const_defined?(
|
35
|
-
callable.const_get(
|
34
|
+
callable.const_defined?(:SubscriberJob, false) ?
|
35
|
+
callable.const_get(:SubscriberJob, false) :
|
36
36
|
nil
|
37
37
|
end
|
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)
|
@@ -48,6 +48,10 @@ module ActiveEventStore
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def subscriber
|
51
|
+
if self.class.subscriber.is_a?(Class) && !self.class.subscriber.respond_to?(:call)
|
52
|
+
return self.class.subscriber.new
|
53
|
+
end
|
54
|
+
|
51
55
|
self.class.subscriber
|
52
56
|
end
|
53
57
|
|
@@ -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.2
|
4
|
+
version: 1.0.2
|
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: 2022-03-15 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.22
|
146
|
+
signing_key:
|
131
147
|
specification_version: 4
|
132
148
|
summary: Rails Event Store in a more Rails way
|
133
149
|
test_files: []
|