ruby_event_store 1.3.1 → 2.0.3
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/Gemfile +2 -4
- data/Gemfile.lock +121 -0
- data/Makefile +13 -3
- data/lib/ruby_event_store/broker.rb +3 -3
- data/lib/ruby_event_store/client.rb +47 -23
- data/lib/ruby_event_store/composed_dispatcher.rb +2 -2
- data/lib/ruby_event_store/constants.rb +1 -0
- data/lib/ruby_event_store/errors.rb +0 -1
- data/lib/ruby_event_store/event.rb +8 -1
- data/lib/ruby_event_store/immediate_async_dispatcher.rb +2 -2
- data/lib/ruby_event_store/in_memory_repository.rb +98 -59
- data/lib/ruby_event_store/instrumented_dispatcher.rb +2 -2
- data/lib/ruby_event_store/mappers/default.rb +28 -6
- data/lib/ruby_event_store/mappers/deprecated_wrapper.rb +33 -0
- data/lib/ruby_event_store/mappers/encryption_mapper.rb +1 -4
- data/lib/ruby_event_store/mappers/instrumented_mapper.rb +8 -4
- data/lib/ruby_event_store/mappers/json_mapper.rb +2 -4
- data/lib/ruby_event_store/mappers/pipeline.rb +26 -5
- data/lib/ruby_event_store/mappers/pipeline_mapper.rb +6 -2
- data/lib/ruby_event_store/mappers/transformation/domain_event.rb +16 -8
- data/lib/ruby_event_store/mappers/transformation/encryption.rb +20 -12
- data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +11 -4
- data/lib/ruby_event_store/mappers/transformation/serialization.rb +16 -14
- data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +12 -7
- data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +12 -7
- data/lib/ruby_event_store/null.rb +13 -0
- data/lib/ruby_event_store/projection.rb +2 -13
- data/lib/ruby_event_store/record.rb +68 -0
- data/lib/ruby_event_store/serialized_record.rb +23 -4
- data/lib/ruby_event_store/spec/broker_lint.rb +9 -9
- data/lib/ruby_event_store/spec/event_repository_lint.rb +200 -36
- data/lib/ruby_event_store/spec/mapper_lint.rb +6 -6
- data/lib/ruby_event_store/spec/subscriptions_lint.rb +6 -0
- data/lib/ruby_event_store/specification.rb +100 -7
- data/lib/ruby_event_store/specification_reader.rb +2 -2
- data/lib/ruby_event_store/specification_result.rb +86 -2
- data/lib/ruby_event_store/version.rb +1 -1
- data/lib/ruby_event_store.rb +4 -7
- data/ruby_event_store.gemspec +1 -3
- metadata +7 -9
- data/lib/ruby_event_store/mappers/protobuf.rb +0 -24
- data/lib/ruby_event_store/mappers/transformation/item.rb +0 -56
- data/lib/ruby_event_store/mappers/transformation/proto_event.rb +0 -17
- data/lib/ruby_event_store/mappers/transformation/protobuf_encoder.rb +0 -30
- data/lib/ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata.rb +0 -30
- data/lib/ruby_event_store/mappers/transformation/serialized_record.rb +0 -27
@@ -3,15 +3,19 @@ module RubyEventStore
|
|
3
3
|
class SRecord
|
4
4
|
def self.new(
|
5
5
|
event_id: SecureRandom.uuid,
|
6
|
-
data:
|
7
|
-
metadata:
|
8
|
-
event_type: 'SRecordTestEvent'
|
6
|
+
data: {},
|
7
|
+
metadata: {},
|
8
|
+
event_type: 'SRecordTestEvent',
|
9
|
+
timestamp: Time.new.utc,
|
10
|
+
valid_at: nil
|
9
11
|
)
|
10
|
-
|
12
|
+
Record.new(
|
11
13
|
event_id: event_id,
|
12
14
|
data: data,
|
13
15
|
metadata: metadata,
|
14
16
|
event_type: event_type,
|
17
|
+
timestamp: timestamp.round(TIMESTAMP_PRECISION),
|
18
|
+
valid_at: (valid_at || timestamp).round(TIMESTAMP_PRECISION),
|
15
19
|
)
|
16
20
|
end
|
17
21
|
end
|
@@ -22,13 +26,45 @@ module RubyEventStore
|
|
22
26
|
Type2 = Class.new(RubyEventStore::Event)
|
23
27
|
# @private
|
24
28
|
Type3 = Class.new(RubyEventStore::Event)
|
29
|
+
|
30
|
+
# @private
|
31
|
+
class EventRepositoryHelper
|
32
|
+
def supports_concurrent_auto?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def supports_concurrent_any?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def supports_binary?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def supports_upsert?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_connection_pooling?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def connection_pool_size
|
53
|
+
end
|
54
|
+
|
55
|
+
def cleanup_concurrency_test
|
56
|
+
end
|
57
|
+
|
58
|
+
def rescuable_concurrency_test_errors
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
end
|
25
62
|
end
|
26
63
|
|
27
64
|
module RubyEventStore
|
28
|
-
RSpec.shared_examples :event_repository do
|
29
|
-
let(:
|
30
|
-
let(:
|
31
|
-
let(:specification) { Specification.new(SpecificationReader.new(repository, mapper)) }
|
65
|
+
::RSpec.shared_examples :event_repository do
|
66
|
+
let(:helper) { EventRepositoryHelper.new }
|
67
|
+
let(:specification) { Specification.new(SpecificationReader.new(repository, Mappers::NullMapper.new)) }
|
32
68
|
let(:global_stream) { Stream.new(GLOBAL_STREAM) }
|
33
69
|
let(:stream) { Stream.new(SecureRandom.uuid) }
|
34
70
|
let(:stream_flow) { Stream.new('flow') }
|
@@ -42,6 +78,11 @@ module RubyEventStore
|
|
42
78
|
let(:version_2) { ExpectedVersion.new(2) }
|
43
79
|
let(:version_3) { ExpectedVersion.new(3) }
|
44
80
|
|
81
|
+
def verify_conncurency_assumptions
|
82
|
+
return unless helper.has_connection_pooling?
|
83
|
+
expect(helper.connection_pool_size).to eq(5)
|
84
|
+
end
|
85
|
+
|
45
86
|
def read_events(scope, stream = nil, from: nil, to: nil, count: nil)
|
46
87
|
scope = scope.stream(stream.name) if stream
|
47
88
|
scope = scope.from(from) if from
|
@@ -393,7 +434,7 @@ module RubyEventStore
|
|
393
434
|
end
|
394
435
|
|
395
436
|
specify 'unlimited concurrency for :any - everything should succeed', timeout: 10, mutant: false do
|
396
|
-
skip unless
|
437
|
+
skip unless helper.supports_concurrent_any?
|
397
438
|
verify_conncurency_assumptions
|
398
439
|
begin
|
399
440
|
concurrency_level = 4
|
@@ -426,12 +467,12 @@ module RubyEventStore
|
|
426
467
|
end
|
427
468
|
expect(events0).to eq(events0.sort_by{|ev| ev.event_id })
|
428
469
|
ensure
|
429
|
-
cleanup_concurrency_test
|
470
|
+
helper.cleanup_concurrency_test
|
430
471
|
end
|
431
472
|
end
|
432
473
|
|
433
474
|
specify 'unlimited concurrency for :any - everything should succeed when linking', timeout: 10, mutant: false do
|
434
|
-
skip unless
|
475
|
+
skip unless helper.supports_concurrent_any?
|
435
476
|
verify_conncurency_assumptions
|
436
477
|
begin
|
437
478
|
concurrency_level = 4
|
@@ -471,12 +512,12 @@ module RubyEventStore
|
|
471
512
|
end
|
472
513
|
expect(events0).to eq(events0.sort_by{|ev| ev.event_id })
|
473
514
|
ensure
|
474
|
-
cleanup_concurrency_test
|
515
|
+
helper.cleanup_concurrency_test
|
475
516
|
end
|
476
517
|
end
|
477
518
|
|
478
519
|
specify 'limited concurrency for :auto - some operations will fail without outside lock, stream is ordered', mutant: false do
|
479
|
-
skip unless
|
520
|
+
skip unless helper.supports_concurrent_auto?
|
480
521
|
verify_conncurency_assumptions
|
481
522
|
begin
|
482
523
|
concurrency_level = 4
|
@@ -494,7 +535,7 @@ module RubyEventStore
|
|
494
535
|
SRecord.new(event_id: eid),
|
495
536
|
], stream, version_auto)
|
496
537
|
sleep(rand(concurrency_level) / 1000.0)
|
497
|
-
rescue WrongExpectedEventVersion, *rescuable_concurrency_test_errors
|
538
|
+
rescue WrongExpectedEventVersion, *helper.rescuable_concurrency_test_errors
|
498
539
|
fail_occurred +=1
|
499
540
|
end
|
500
541
|
end
|
@@ -510,14 +551,14 @@ module RubyEventStore
|
|
510
551
|
ev.event_id.start_with?("0-")
|
511
552
|
end
|
512
553
|
expect(events0).to eq(events0.sort_by{|ev| ev.event_id })
|
513
|
-
additional_limited_concurrency_for_auto_check
|
554
|
+
additional_limited_concurrency_for_auto_check if defined? additional_limited_concurrency_for_auto_check
|
514
555
|
ensure
|
515
|
-
cleanup_concurrency_test
|
556
|
+
helper.cleanup_concurrency_test
|
516
557
|
end
|
517
558
|
end
|
518
559
|
|
519
560
|
specify 'limited concurrency for :auto - some operations will fail without outside lock, stream is ordered', mutant: false do
|
520
|
-
skip unless
|
561
|
+
skip unless helper.supports_concurrent_auto?
|
521
562
|
verify_conncurency_assumptions
|
522
563
|
begin
|
523
564
|
concurrency_level = 4
|
@@ -542,7 +583,7 @@ module RubyEventStore
|
|
542
583
|
eid = "0000000#{i}-#{sprintf("%04d", j)}-0000-0000-000000000000"
|
543
584
|
repository.link_to_stream(eid, stream, version_auto)
|
544
585
|
sleep(rand(concurrency_level) / 1000.0)
|
545
|
-
rescue WrongExpectedEventVersion, *rescuable_concurrency_test_errors
|
586
|
+
rescue WrongExpectedEventVersion, *helper.rescuable_concurrency_test_errors
|
546
587
|
fail_occurred +=1
|
547
588
|
end
|
548
589
|
end
|
@@ -558,9 +599,9 @@ module RubyEventStore
|
|
558
599
|
ev.event_id.start_with?("0-")
|
559
600
|
end
|
560
601
|
expect(events0).to eq(events0.sort_by{|ev| ev.event_id })
|
561
|
-
additional_limited_concurrency_for_auto_check
|
602
|
+
additional_limited_concurrency_for_auto_check if defined? additional_limited_concurrency_for_auto_check
|
562
603
|
ensure
|
563
|
-
cleanup_concurrency_test
|
604
|
+
helper.cleanup_concurrency_test
|
564
605
|
end
|
565
606
|
end
|
566
607
|
|
@@ -573,30 +614,30 @@ module RubyEventStore
|
|
573
614
|
end
|
574
615
|
|
575
616
|
it 'data attributes are retrieved' do
|
576
|
-
event = SRecord.new(data:
|
617
|
+
event = SRecord.new(data: { "order_id" => 3 })
|
577
618
|
repository.append_to_stream(event, stream, version_any)
|
578
619
|
retrieved_event = read_events_forward(repository, count: 1).first
|
579
|
-
expect(retrieved_event.data).to eq(
|
620
|
+
expect(retrieved_event.data).to eq({ "order_id" => 3 })
|
580
621
|
end
|
581
622
|
|
582
623
|
it 'metadata attributes are retrieved' do
|
583
|
-
event = SRecord.new(metadata:
|
624
|
+
event = SRecord.new(metadata: { "request_id" => 3 })
|
584
625
|
repository.append_to_stream(event, stream, version_any)
|
585
626
|
retrieved_event = read_events_forward(repository, count: 1).first
|
586
|
-
expect(retrieved_event.metadata).to eq(
|
627
|
+
expect(retrieved_event.metadata).to eq({ "request_id" => 3 })
|
587
628
|
end
|
588
629
|
|
589
630
|
it 'data and metadata attributes are retrieved when linking' do
|
590
631
|
event = SRecord.new(
|
591
|
-
data:
|
592
|
-
metadata:
|
632
|
+
data: { "order_id" => 3 },
|
633
|
+
metadata: { "request_id" => 4},
|
593
634
|
)
|
594
635
|
repository
|
595
636
|
.append_to_stream(event, stream, version_any)
|
596
637
|
.link_to_stream(event.event_id, stream_flow, version_any)
|
597
638
|
retrieved_event = read_events_forward(repository, stream_flow).first
|
598
|
-
expect(retrieved_event.metadata).to eq(
|
599
|
-
expect(retrieved_event.data).to eq(
|
639
|
+
expect(retrieved_event.metadata).to eq({ "request_id" => 4 })
|
640
|
+
expect(retrieved_event.data).to eq({ "order_id" => 3 })
|
600
641
|
expect(event).to eq(retrieved_event)
|
601
642
|
end
|
602
643
|
|
@@ -918,7 +959,7 @@ module RubyEventStore
|
|
918
959
|
end
|
919
960
|
|
920
961
|
specify 'can store arbitrary binary data' do
|
921
|
-
skip unless
|
962
|
+
skip unless helper.supports_binary?
|
922
963
|
binary = "\xB0"
|
923
964
|
expect(binary.valid_encoding?).to eq(false)
|
924
965
|
binary.force_encoding("binary")
|
@@ -1091,7 +1132,7 @@ module RubyEventStore
|
|
1091
1132
|
|
1092
1133
|
context "#update_messages" do
|
1093
1134
|
specify "changes events" do
|
1094
|
-
skip unless
|
1135
|
+
skip unless helper.supports_upsert?
|
1095
1136
|
events = Array.new(5) { SRecord.new }
|
1096
1137
|
repository.append_to_stream(
|
1097
1138
|
events[0..2],
|
@@ -1104,19 +1145,20 @@ module RubyEventStore
|
|
1104
1145
|
ExpectedVersion.any
|
1105
1146
|
)
|
1106
1147
|
repository.update_messages([
|
1107
|
-
a = SRecord.new(event_id: events[0].event_id.clone, data: events[0].data,
|
1108
|
-
b = SRecord.new(event_id: events[1].event_id.dup,
|
1109
|
-
c = SRecord.new(event_id: events[2].event_id,
|
1110
|
-
d = SRecord.new(event_id: events[3].event_id.clone, data: events[3].data,
|
1111
|
-
e = SRecord.new(event_id: events[4].event_id.dup,
|
1148
|
+
a = SRecord.new(event_id: events[0].event_id.clone, data: events[0].data, metadata: events[0].metadata, event_type: events[0].event_type, timestamp: events[0].timestamp),
|
1149
|
+
b = SRecord.new(event_id: events[1].event_id.dup, data: { "test" => 1 }, metadata: events[1].metadata, event_type: events[1].event_type, timestamp: events[1].timestamp),
|
1150
|
+
c = SRecord.new(event_id: events[2].event_id, data: events[2].data, metadata: { "test" => 2 }, event_type: events[2].event_type, timestamp: events[2].timestamp),
|
1151
|
+
d = SRecord.new(event_id: events[3].event_id.clone, data: events[3].data, metadata: events[3].metadata, event_type: "event_type3", timestamp: events[3].timestamp),
|
1152
|
+
e = SRecord.new(event_id: events[4].event_id.dup, data: { "test" => 4 }, metadata: { "test" => 42 }, event_type: "event_type4", timestamp: events[4].timestamp),
|
1112
1153
|
])
|
1154
|
+
|
1113
1155
|
expect(repository.read(specification.result).to_a).to eq([a,b,c,d,e])
|
1114
1156
|
expect(repository.read(specification.stream("whatever").result).to_a).to eq([a,b,c])
|
1115
1157
|
expect(repository.read(specification.stream("elo").result).to_a).to eq([d,e])
|
1116
1158
|
end
|
1117
1159
|
|
1118
1160
|
specify "cannot change unexisting event" do
|
1119
|
-
skip unless
|
1161
|
+
skip unless helper.supports_upsert?
|
1120
1162
|
e = SRecord.new
|
1121
1163
|
expect{ repository.update_messages([e]) }.to raise_error do |err|
|
1122
1164
|
expect(err).to be_a(EventNotFound)
|
@@ -1124,6 +1166,14 @@ module RubyEventStore
|
|
1124
1166
|
expect(err.message).to eq("Event not found: #{e.event_id}")
|
1125
1167
|
end
|
1126
1168
|
end
|
1169
|
+
|
1170
|
+
specify "does not change timestamp" do
|
1171
|
+
r = SRecord.new(timestamp: Time.utc(2020, 1, 1))
|
1172
|
+
repository.append_to_stream([r], Stream.new("whatever"), ExpectedVersion.any)
|
1173
|
+
repository.update_messages([SRecord.new(event_id: r.event_id, timestamp: Time.utc(2020, 1, 20))])
|
1174
|
+
|
1175
|
+
expect(repository.read(specification.result).first.timestamp).to eq(Time.utc(2020, 1, 1))
|
1176
|
+
end
|
1127
1177
|
end
|
1128
1178
|
|
1129
1179
|
specify do
|
@@ -1228,5 +1278,119 @@ module RubyEventStore
|
|
1228
1278
|
expect(repository.count(specification.stream("Dummy").of_type([Type3]).result)).to eq(2)
|
1229
1279
|
expect(repository.count(specification.stream(stream.name).of_type([Type3]).result)).to eq(0)
|
1230
1280
|
end
|
1281
|
+
|
1282
|
+
specify 'timestamp precision' do
|
1283
|
+
time = Time.utc(2020, 9, 11, 12, 26, 0, 123456)
|
1284
|
+
repository.append_to_stream(SRecord.new(timestamp: time), stream, version_none)
|
1285
|
+
event = read_events_forward(repository, count: 1).first
|
1286
|
+
|
1287
|
+
expect(event.timestamp).to eq(time)
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
specify 'fetching records older than specified date in stream' do
|
1291
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1292
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1293
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1294
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1295
|
+
|
1296
|
+
expect(repository.read(specification.stream('whatever').older_than(Time.utc(2020, 1, 2)).result).to_a).to eq([event_1])
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
specify 'fetching records older than or equal to specified date in stream' do
|
1300
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1301
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1302
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1303
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1304
|
+
|
1305
|
+
expect(repository.read(specification.stream('whatever').older_than_or_equal(Time.utc(2020, 1, 2)).result).to_a).to eq([event_1, event_2])
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
specify 'fetching records newer than specified date in stream' do
|
1309
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1310
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1311
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1312
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1313
|
+
|
1314
|
+
expect(repository.read(specification.stream('whatever').newer_than(Time.utc(2020, 1, 2)).result).to_a).to eq([event_3])
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
specify 'fetching records newer than or equal to specified date in stream' do
|
1318
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1319
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1320
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1321
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1322
|
+
|
1323
|
+
expect(repository.read(specification.stream('whatever').newer_than_or_equal(Time.utc(2020, 1, 2)).result).to_a).to eq([event_2, event_3])
|
1324
|
+
end
|
1325
|
+
|
1326
|
+
specify 'fetching records older than specified date' do
|
1327
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1328
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1329
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1330
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1331
|
+
|
1332
|
+
expect(repository.read(specification.older_than(Time.utc(2020, 1, 2)).result).to_a).to eq([event_1])
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
specify 'fetching records older than or equal to specified date' do
|
1336
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1337
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1338
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1339
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1340
|
+
|
1341
|
+
expect(repository.read(specification.older_than_or_equal(Time.utc(2020, 1, 2)).result).to_a).to eq([event_1, event_2])
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
specify 'fetching records newer than specified date' do
|
1345
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1346
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1347
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1348
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1349
|
+
|
1350
|
+
expect(repository.read(specification.newer_than(Time.utc(2020, 1, 2)).result).to_a).to eq([event_3])
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
specify 'fetching records newer than or equal to specified date' do
|
1354
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1355
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1356
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1357
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1358
|
+
|
1359
|
+
expect(repository.read(specification.newer_than_or_equal(Time.utc(2020, 1, 2)).result).to_a).to eq([event_2, event_3])
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
specify 'fetching records from disjoint periods' do
|
1363
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1364
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1365
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1366
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1367
|
+
|
1368
|
+
expect(repository.read(specification.older_than(Time.utc(2020, 1, 2)).newer_than(Time.utc(2020, 1, 2)).result).to_a).to eq([])
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
specify 'fetching records within time range' do
|
1372
|
+
event_1 = SRecord.new(event_id: '8a6f053e-3ce2-4c82-a55b-4d02c66ae6ea', timestamp: Time.utc(2020, 1, 1))
|
1373
|
+
event_2 = SRecord.new(event_id: '8cee1139-4f96-483a-a175-2b947283c3c7', timestamp: Time.utc(2020, 1, 2))
|
1374
|
+
event_3 = SRecord.new(event_id: 'd345f86d-b903-4d78-803f-38990c078d9e', timestamp: Time.utc(2020, 1, 3))
|
1375
|
+
repository.append_to_stream([event_1, event_2, event_3], Stream.new('whatever'), version_any)
|
1376
|
+
|
1377
|
+
expect(repository.read(specification.between(Time.utc(2020, 1, 1)...Time.utc(2020, 1, 3)).result).to_a).to eq([event_1, event_2])
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
specify "time order is respected" do
|
1381
|
+
repository.append_to_stream([
|
1382
|
+
SRecord.new(event_id: e1 = SecureRandom.uuid, timestamp: Time.new(2020,1,1), valid_at: Time.new(2020,1,9)),
|
1383
|
+
SRecord.new(event_id: e2 = SecureRandom.uuid, timestamp: Time.new(2020,1,3), valid_at: Time.new(2020,1,6)),
|
1384
|
+
SRecord.new(event_id: e3 = SecureRandom.uuid, timestamp: Time.new(2020,1,2), valid_at: Time.new(2020,1,3)),
|
1385
|
+
],
|
1386
|
+
Stream.new("Dummy"),
|
1387
|
+
ExpectedVersion.any
|
1388
|
+
)
|
1389
|
+
expect(repository.read(specification.result).map(&:event_id)).to eq [e1, e2, e3]
|
1390
|
+
expect(repository.read(specification.as_at.result).map(&:event_id)).to eq [e1, e3, e2]
|
1391
|
+
expect(repository.read(specification.as_at.backward.result).map(&:event_id)).to eq [e2, e3, e1]
|
1392
|
+
expect(repository.read(specification.as_of.result).map(&:event_id)).to eq [e3, e2, e1]
|
1393
|
+
expect(repository.read(specification.as_of.backward.result).map(&:event_id)).to eq [e1, e2, e3]
|
1394
|
+
end
|
1231
1395
|
end
|
1232
1396
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module RubyEventStore
|
2
|
-
RSpec.shared_examples :mapper do |mapper, domain_event|
|
3
|
-
specify "
|
4
|
-
record = mapper.
|
2
|
+
::RSpec.shared_examples :mapper do |mapper, domain_event|
|
3
|
+
specify "event_to_record returns instance of Record" do
|
4
|
+
record = mapper.event_to_record(domain_event)
|
5
5
|
|
6
|
-
expect(record).to be_kind_of(
|
6
|
+
expect(record).to be_kind_of(Record)
|
7
7
|
expect(record.event_id).to eq(domain_event.event_id)
|
8
8
|
expect(record.event_type).to eq(domain_event.event_type)
|
9
9
|
end
|
10
10
|
|
11
11
|
specify "serialize and deserialize gives equal event" do
|
12
|
-
record = mapper.
|
12
|
+
record = mapper.event_to_record(domain_event)
|
13
13
|
|
14
|
-
expect(mapper.
|
14
|
+
expect(mapper.record_to_event(record)).to eq(domain_event)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -39,10 +39,16 @@ RSpec.shared_examples :subscriptions do |subscriptions_class|
|
|
39
39
|
subscriptions.add_thread_subscription(handler, [Test1DomainEvent, Test3DomainEvent])
|
40
40
|
subscriptions.add_thread_subscription(another_handler, [Test2DomainEvent])
|
41
41
|
subscriptions.add_thread_global_subscription(global_handler)
|
42
|
+
t = Thread.new do
|
43
|
+
subscriptions.add_thread_subscription(handler, [Test2DomainEvent])
|
44
|
+
subscriptions.add_thread_global_subscription(another_handler)
|
45
|
+
expect(subscriptions.all_for('Test2DomainEvent')).to eq([another_handler, handler])
|
46
|
+
end
|
42
47
|
|
43
48
|
expect(subscriptions.all_for('Test1DomainEvent')).to eq([global_handler, handler])
|
44
49
|
expect(subscriptions.all_for('Test2DomainEvent')).to eq([global_handler, another_handler])
|
45
50
|
expect(subscriptions.all_for('Test3DomainEvent')).to eq([global_handler, handler])
|
51
|
+
t.join
|
46
52
|
end
|
47
53
|
|
48
54
|
it 'returns lambda as an output of global subscribe methods' do
|
@@ -35,7 +35,7 @@ module RubyEventStore
|
|
35
35
|
# Limits the query to events before or after another event.
|
36
36
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
37
37
|
#
|
38
|
-
# @param
|
38
|
+
# @param stop [String] id of event to start reading from.
|
39
39
|
# @return [Specification]
|
40
40
|
def to(stop)
|
41
41
|
raise InvalidPageStop if stop.nil? || stop.empty?
|
@@ -43,6 +43,99 @@ module RubyEventStore
|
|
43
43
|
Specification.new(reader, result.dup { |r| r.stop = stop })
|
44
44
|
end
|
45
45
|
|
46
|
+
# Limits the query to events that later than given time.
|
47
|
+
# {http://railseventstore.org/docs/read/ Find out more}.
|
48
|
+
#
|
49
|
+
# @param time [Time]
|
50
|
+
# @return [Specification]
|
51
|
+
def older_than(time)
|
52
|
+
raise ArgumentError unless time.respond_to?(:to_time)
|
53
|
+
Specification.new(
|
54
|
+
reader,
|
55
|
+
result.dup do |r|
|
56
|
+
r.older_than = time
|
57
|
+
r.older_than_or_equal = nil
|
58
|
+
end
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Limits the query to events that occurred on given time or later.
|
63
|
+
# {http://railseventstore.org/docs/read/ Find out more}.
|
64
|
+
#
|
65
|
+
# @param time [Time]
|
66
|
+
# @return [Specification]
|
67
|
+
def older_than_or_equal(time)
|
68
|
+
raise ArgumentError unless time.respond_to?(:to_time)
|
69
|
+
Specification.new(
|
70
|
+
reader,
|
71
|
+
result.dup do |r|
|
72
|
+
r.older_than = nil
|
73
|
+
r.older_than_or_equal = time
|
74
|
+
end
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Limits the query to events that occurred earlier than given time.
|
79
|
+
# {http://railseventstore.org/docs/read/ Find out more}.
|
80
|
+
#
|
81
|
+
# @param time [Time]
|
82
|
+
# @return [Specification]
|
83
|
+
def newer_than(time)
|
84
|
+
raise ArgumentError unless time.respond_to?(:to_time)
|
85
|
+
Specification.new(
|
86
|
+
reader,
|
87
|
+
result.dup do |r|
|
88
|
+
r.newer_than_or_equal = nil
|
89
|
+
r.newer_than = time
|
90
|
+
end
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Limits the query to events that occurred on given time or earlier.
|
95
|
+
# {http://railseventstore.org/docs/read/ Find out more}.
|
96
|
+
#
|
97
|
+
# @param time [Time]
|
98
|
+
# @return [Specification]
|
99
|
+
def newer_than_or_equal(time)
|
100
|
+
raise ArgumentError unless time.respond_to?(:to_time)
|
101
|
+
Specification.new(
|
102
|
+
reader,
|
103
|
+
result.dup do |r|
|
104
|
+
r.newer_than_or_equal = time
|
105
|
+
r.newer_than = nil
|
106
|
+
end
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Limits the query to events within given time range.
|
111
|
+
# {http://railseventstore.org/docs/read/ Find out more}.
|
112
|
+
#
|
113
|
+
# @param time_range [Range]
|
114
|
+
# @return [Specification]
|
115
|
+
def between(time_range)
|
116
|
+
if time_range.exclude_end?
|
117
|
+
newer_than_or_equal(time_range.first).older_than(time_range.last)
|
118
|
+
else
|
119
|
+
newer_than_or_equal(time_range.first).older_than_or_equal(time_range.last)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Sets the order of time sorting using transaction time
|
124
|
+
# {http://railseventstore.org/docs/read/ Find out more}
|
125
|
+
#
|
126
|
+
# @return [Specification]
|
127
|
+
def as_at
|
128
|
+
Specification.new(reader, result.dup { |r| r.time_sort_by = :as_at})
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets the order of time sorting using validity time
|
132
|
+
# {http://railseventstore.org/docs/read/ Find out more}
|
133
|
+
#
|
134
|
+
# @return [Specification]
|
135
|
+
def as_of
|
136
|
+
Specification.new(reader, result.dup { |r| r.time_sort_by = :as_of })
|
137
|
+
end
|
138
|
+
|
46
139
|
# Sets the order of reading events to ascending (forward from the start).
|
47
140
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
48
141
|
#
|
@@ -73,7 +166,7 @@ module RubyEventStore
|
|
73
166
|
# Yields each batch of records that was retrieved from the store.
|
74
167
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
75
168
|
#
|
76
|
-
# @yield [Array<Event
|
169
|
+
# @yield [Array<Event>] batch of events
|
77
170
|
# @return [Enumerator, nil] Enumerator is returned when block not given
|
78
171
|
def each_batch
|
79
172
|
return to_enum(:each_batch) unless block_given?
|
@@ -87,7 +180,7 @@ module RubyEventStore
|
|
87
180
|
# Yields events read from the store if block given. Otherwise, returns enumerable collection.
|
88
181
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
89
182
|
#
|
90
|
-
# @yield [Event
|
183
|
+
# @yield [Event] event
|
91
184
|
# @return [Enumerator, nil] Enumerator is returned when block not given
|
92
185
|
def each
|
93
186
|
return to_enum unless block_given?
|
@@ -111,7 +204,7 @@ module RubyEventStore
|
|
111
204
|
# built up to this point result using provided block.
|
112
205
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
113
206
|
#
|
114
|
-
# @accumulator starting state for reduce operation
|
207
|
+
# @param accumulator starting state for reduce operation
|
115
208
|
# @return reduce result as defined by block given
|
116
209
|
def reduce(accumulator = nil, &block)
|
117
210
|
raise ArgumentError.new("Block must be given") unless block_given?
|
@@ -130,7 +223,7 @@ module RubyEventStore
|
|
130
223
|
# Returns array of domain events.
|
131
224
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
132
225
|
#
|
133
|
-
# @return [Array<Event
|
226
|
+
# @return [Array<Event>]
|
134
227
|
def to_a
|
135
228
|
each.to_a
|
136
229
|
end
|
@@ -200,7 +293,7 @@ module RubyEventStore
|
|
200
293
|
# Limits the query to certain events by given even ids.
|
201
294
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
202
295
|
#
|
203
|
-
# @param
|
296
|
+
# @param event_ids [Array(String)] ids of event to look for.
|
204
297
|
# @return [Specification]
|
205
298
|
def with_id(event_ids)
|
206
299
|
Specification.new(reader, result.dup{ |r| r.with_ids = event_ids })
|
@@ -231,7 +324,7 @@ module RubyEventStore
|
|
231
324
|
# read from the store if block given. Otherwise, returns enumerable collection.
|
232
325
|
# {http://railseventstore.org/docs/read/ Find out more}.
|
233
326
|
#
|
234
|
-
# @yield [Event
|
327
|
+
# @yield [Event] event
|
235
328
|
# @return [Enumerator] Enumerator is returned when block not given
|
236
329
|
def events(event_ids)
|
237
330
|
with_id(event_ids).each
|
@@ -14,14 +14,14 @@ module RubyEventStore
|
|
14
14
|
# @private
|
15
15
|
def one(specification_result)
|
16
16
|
record = repository.read(specification_result)
|
17
|
-
mapper.
|
17
|
+
mapper.record_to_event(record) if record
|
18
18
|
end
|
19
19
|
|
20
20
|
# @api private
|
21
21
|
# @private
|
22
22
|
def each(specification_result)
|
23
23
|
repository.read(specification_result).each do |batch|
|
24
|
-
yield batch.map { |
|
24
|
+
yield batch.map { |record| mapper.record_to_event(record) }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|