nexia_event_store 0.3.3 → 0.4.0
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/lib/event_store/aggregate.rb +4 -0
- data/lib/event_store/client.rb +22 -16
- data/lib/event_store/snapshot.rb +15 -7
- data/lib/event_store/version.rb +1 -1
- data/spec/event_store/client_spec.rb +16 -1
- data/spec/unit/aggregate_unit_spec.rb +40 -0
- data/spec/unit/snapshot_unit_spec.rb +108 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a53b46ee6253d85453dc97abcaea3cf60d1b6cd6
|
4
|
+
data.tar.gz: 717b8827588f413f87b41513c2ef8539254140fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b96e05c1a31be2b889a473e86ea7254f506d946be4ef505d6fb2522a3b6c23a425cdc3a0debd3b3ba73d9e0eae386e06e71f4378735a09fc64dd6e452f516971
|
7
|
+
data.tar.gz: 8e2ecbf2ca66a91042dda7fd790f2b61f328a432232045c27b16d1d8fa5807a8a123033345fee46718c65a2a5e5ec7e4c0d6693e945f1dd43e1ee6233f8cdc1c
|
data/lib/event_store/client.rb
CHANGED
@@ -2,7 +2,7 @@ module EventStore
|
|
2
2
|
class Client
|
3
3
|
extend Forwardable
|
4
4
|
|
5
|
-
def_delegators
|
5
|
+
def_delegators :aggregate, :delete_snapshot!, :snapshot_version_table
|
6
6
|
|
7
7
|
def self.count
|
8
8
|
Aggregate.count
|
@@ -16,20 +16,24 @@ module EventStore
|
|
16
16
|
@aggregate = Aggregate.new(aggregate_id, aggregate_type)
|
17
17
|
end
|
18
18
|
|
19
|
+
def exists?
|
20
|
+
aggregate.snapshot_exists?
|
21
|
+
end
|
22
|
+
|
19
23
|
def id
|
20
|
-
|
24
|
+
aggregate.id
|
21
25
|
end
|
22
26
|
|
23
27
|
def type
|
24
|
-
|
28
|
+
aggregate.type
|
25
29
|
end
|
26
30
|
|
27
31
|
def event_table
|
28
|
-
|
32
|
+
aggregate.event_table
|
29
33
|
end
|
30
34
|
|
31
35
|
def append(event_data)
|
32
|
-
|
36
|
+
aggregate.append(event_data)
|
33
37
|
yield(event_data) if block_given?
|
34
38
|
nil
|
35
39
|
end
|
@@ -43,31 +47,31 @@ module EventStore
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def event_stream_from(version_number, max=nil)
|
46
|
-
translate_events(
|
50
|
+
translate_events(aggregate.events_from(version_number, max))
|
47
51
|
end
|
48
52
|
|
49
53
|
def event_stream_between(start_time, end_time, fully_qualified_names = [])
|
50
|
-
translate_events(
|
54
|
+
translate_events(aggregate.event_stream_between(start_time, end_time, fully_qualified_names))
|
51
55
|
end
|
52
56
|
|
53
57
|
def peek
|
54
|
-
|
58
|
+
aggregate.last_event
|
55
59
|
end
|
56
60
|
|
57
61
|
def raw_snapshot
|
58
|
-
|
62
|
+
aggregate.snapshot
|
59
63
|
end
|
60
64
|
|
61
65
|
def raw_event_stream
|
62
|
-
|
66
|
+
aggregate.event_stream
|
63
67
|
end
|
64
68
|
|
65
69
|
def raw_event_stream_from version_number, max=nil
|
66
|
-
|
70
|
+
aggregate.events_from(version_number, max)
|
67
71
|
end
|
68
72
|
|
69
73
|
def version
|
70
|
-
|
74
|
+
aggregate.version
|
71
75
|
end
|
72
76
|
|
73
77
|
def count
|
@@ -75,17 +79,19 @@ module EventStore
|
|
75
79
|
end
|
76
80
|
|
77
81
|
def destroy!
|
78
|
-
|
79
|
-
|
82
|
+
aggregate.delete_events!
|
83
|
+
aggregate.delete_snapshot!
|
80
84
|
end
|
81
85
|
|
82
86
|
def rebuild_snapshot!
|
83
|
-
|
84
|
-
|
87
|
+
aggregate.delete_snapshot!
|
88
|
+
aggregate.rebuild_snapshot!
|
85
89
|
end
|
86
90
|
|
87
91
|
private
|
88
92
|
|
93
|
+
attr_reader :aggregate
|
94
|
+
|
89
95
|
def translate_events(event_hashs)
|
90
96
|
event_hashs.map { |eh| translate_event(eh) }
|
91
97
|
end
|
data/lib/event_store/snapshot.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module EventStore
|
2
2
|
class Snapshot
|
3
3
|
|
4
|
-
attr_reader :snapshot_version_table
|
4
|
+
attr_reader :snapshot_version_table, :snapshot_table
|
5
5
|
|
6
6
|
def initialize aggregate
|
7
7
|
@aggregate = aggregate
|
@@ -10,12 +10,20 @@ module EventStore
|
|
10
10
|
@snapshot_version_table = "#{@aggregate.type}_snapshot_versions_for_#{@aggregate.id}"
|
11
11
|
end
|
12
12
|
|
13
|
+
def exists?
|
14
|
+
@redis.exists(snapshot_table)
|
15
|
+
end
|
16
|
+
|
13
17
|
def last_event
|
14
18
|
snapshot.last
|
15
19
|
end
|
16
20
|
|
17
21
|
def version
|
18
|
-
(@redis.hget(
|
22
|
+
(@redis.hget(snapshot_version_table, :current_version) || -1).to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
snapshot.size
|
19
27
|
end
|
20
28
|
|
21
29
|
def snapshot
|
@@ -39,7 +47,7 @@ module EventStore
|
|
39
47
|
end
|
40
48
|
|
41
49
|
def delete_snapshot!
|
42
|
-
EventStore.redis.del [
|
50
|
+
EventStore.redis.del [snapshot_table, snapshot_version_table]
|
43
51
|
end
|
44
52
|
|
45
53
|
def store_snapshot(prepared_events)
|
@@ -57,8 +65,8 @@ module EventStore
|
|
57
65
|
valid_snapshot_versions += [:current_version, valid_snapshot_versions.last.to_i]
|
58
66
|
|
59
67
|
@redis.multi do
|
60
|
-
@redis.hmset(
|
61
|
-
@redis.hmset(
|
68
|
+
@redis.hmset(snapshot_version_table, valid_snapshot_versions)
|
69
|
+
@redis.hmset(snapshot_table, valid_snapshot_events)
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|
@@ -87,13 +95,13 @@ module EventStore
|
|
87
95
|
end
|
88
96
|
|
89
97
|
def current_version_numbers
|
90
|
-
current_versions = @redis.hgetall(
|
98
|
+
current_versions = @redis.hgetall(snapshot_version_table)
|
91
99
|
current_versions.default = -1
|
92
100
|
current_versions
|
93
101
|
end
|
94
102
|
|
95
103
|
def read_raw_snapshot
|
96
|
-
@redis.hgetall(
|
104
|
+
@redis.hgetall(snapshot_table)
|
97
105
|
end
|
98
106
|
|
99
107
|
def auto_rebuild_snapshot(events_hash)
|
data/lib/event_store/version.rb
CHANGED
@@ -6,7 +6,7 @@ AGGREGATE_ID_TWO = SecureRandom.uuid
|
|
6
6
|
AGGREGATE_ID_THREE = SecureRandom.uuid
|
7
7
|
|
8
8
|
describe EventStore::Client do
|
9
|
-
|
9
|
+
subject(:es_client) { EventStore::Client }
|
10
10
|
|
11
11
|
before do
|
12
12
|
client_1 = es_client.new(AGGREGATE_ID_ONE, :device)
|
@@ -33,6 +33,19 @@ describe EventStore::Client do
|
|
33
33
|
expect(es_client.ids(offset, limit)).to eq([[AGGREGATE_ID_ONE, AGGREGATE_ID_TWO].sort.first])
|
34
34
|
end
|
35
35
|
|
36
|
+
describe "#exists?" do
|
37
|
+
let(:fake_aggregate) { double("Aggregate") }
|
38
|
+
|
39
|
+
subject(:client) { es_client.new(AGGREGATE_ID_ONE, :device) }
|
40
|
+
|
41
|
+
before(:each) { expect(client).to receive(:aggregate).and_return(fake_aggregate) }
|
42
|
+
|
43
|
+
it "checks if the snapshot exists" do
|
44
|
+
expect(fake_aggregate).to receive(:snapshot_exists?).and_return(true)
|
45
|
+
expect(client.exists?).to eq(true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
36
49
|
describe '#raw_event_stream' do
|
37
50
|
it "should be an array of hashes that represent database records, not EventStore::SerializedEvent objects" do
|
38
51
|
raw_stream = es_client.new(AGGREGATE_ID_ONE, :device).raw_event_stream
|
@@ -373,10 +386,12 @@ describe EventStore::Client do
|
|
373
386
|
end
|
374
387
|
|
375
388
|
end
|
389
|
+
|
376
390
|
def serialized_event_data_terminated_by_null
|
377
391
|
@term_data ||= File.open(File.expand_path("../binary_string_term_with_null_byte.txt", __FILE__), 'rb') {|f| f.read}
|
378
392
|
@term_data
|
379
393
|
end
|
394
|
+
|
380
395
|
def serialized_binary_event_data
|
381
396
|
@event_data ||= File.open(File.expand_path("../serialized_binary_event_data.txt", __FILE__), 'rb') {|f| f.read}
|
382
397
|
@event_data
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mock_redis'
|
3
|
+
|
4
|
+
module EventStore
|
5
|
+
describe Aggregate do
|
6
|
+
let(:aggregate_id) { "014001A8" }
|
7
|
+
let(:type) { "fake_events" }
|
8
|
+
let(:fake_snapshot) { double("Snapshot", exists?: true) }
|
9
|
+
let(:fake_stream) { double("EventStream") }
|
10
|
+
|
11
|
+
before(:each) { allow(Snapshot).to receive(:new).and_return(fake_snapshot) }
|
12
|
+
before(:each) { allow(EventStream).to receive(:new).and_return(fake_stream) }
|
13
|
+
|
14
|
+
subject(:aggregate) { EventStore::Aggregate.new(aggregate_id, type) }
|
15
|
+
|
16
|
+
describe "#count" do
|
17
|
+
it "has tests"
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#ids" do
|
21
|
+
it "has tests"
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#append" do
|
25
|
+
it "has tests"
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#snapshot_exists?" do
|
29
|
+
it "delegates to its snapshot" do
|
30
|
+
expect(fake_snapshot).to receive(:exists?).and_return(true)
|
31
|
+
expect(aggregate.snapshot_exists?).to eq(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "does not build a snapshot" do
|
35
|
+
expect(fake_snapshot).to_not receive(:snapshot) # :(
|
36
|
+
aggregate.snapshot_exists?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mock_redis'
|
3
|
+
|
4
|
+
module EventStore
|
5
|
+
describe Snapshot do
|
6
|
+
let(:redis) { EventStore.redis }
|
7
|
+
let(:aggregate_type) { "awesome" }
|
8
|
+
let(:aggregate_id) { "superman" }
|
9
|
+
let(:events) { [] }
|
10
|
+
let(:aggregate) { double("Aggregate", type: aggregate_type, id: aggregate_id, events: double(all: events)) }
|
11
|
+
|
12
|
+
subject(:snapshot) { EventStore::Snapshot.new(aggregate) }
|
13
|
+
|
14
|
+
it "has a version table for the snapshot" do
|
15
|
+
expect(snapshot.snapshot_version_table).to eq "#{aggregate_type}_snapshot_versions_for_#{aggregate_id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with events in the snapshot table" do
|
19
|
+
let(:first_event) {
|
20
|
+
{ version: 1,
|
21
|
+
fully_qualified_name: "fqn",
|
22
|
+
sub_key: "sub",
|
23
|
+
serialized_event: EventStore.escape_bytea("cheerios"),
|
24
|
+
occurred_at: Time.at( (Time.now - 3600).to_i )
|
25
|
+
}
|
26
|
+
}
|
27
|
+
let(:last_event) {
|
28
|
+
{ version: 2,
|
29
|
+
fully_qualified_name: "fqn2",
|
30
|
+
sub_key: "sub2",
|
31
|
+
serialized_event: EventStore.escape_bytea("cheerios2"),
|
32
|
+
occurred_at: Time.at( (Time.now - 1800).to_i )
|
33
|
+
}
|
34
|
+
}
|
35
|
+
let(:events) { [ first_event, last_event ] }
|
36
|
+
|
37
|
+
before(:each) { snapshot.store_snapshot(events) }
|
38
|
+
|
39
|
+
describe "#last_event" do
|
40
|
+
let(:events) { [ last_event, first_event ] }
|
41
|
+
|
42
|
+
it "returns the event with the highest version" do
|
43
|
+
expect(snapshot.last_event.fully_qualified_name).to eq(last_event[:fully_qualified_name])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#version" do
|
48
|
+
it "is the highest version of the last inserted event in the snapshot" do
|
49
|
+
expect(snapshot.version).to eq(last_event[:version])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#rebuild_snapshot!" do
|
54
|
+
it "deletes the existing snapshot" do
|
55
|
+
expect(redis).to receive(:del).with([snapshot.snapshot_table , snapshot.snapshot_version_table])
|
56
|
+
snapshot.rebuild_snapshot!
|
57
|
+
end
|
58
|
+
|
59
|
+
it "stores a a new snapshot from the aggregate's events" do
|
60
|
+
snapshot.rebuild_snapshot!
|
61
|
+
expect(snapshot.size).to eq(2)
|
62
|
+
# TODO: remove #snapshot in favor of Enumerable
|
63
|
+
names = snapshot.snapshot.map(&:fully_qualified_name)
|
64
|
+
expect(names).to eq(events.map { |e| e[:fully_qualified_name] })
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: remove this in favor of #each and include Enumerable
|
69
|
+
describe "#snapshot" do
|
70
|
+
let(:event_snapshot) { snapshot.snapshot }
|
71
|
+
let(:serialized_attrs) { [ :fully_qualified_name,
|
72
|
+
:serialized_event,
|
73
|
+
:version,
|
74
|
+
:occurred_at ] }
|
75
|
+
|
76
|
+
it "contains SerializedEvents" do
|
77
|
+
event_snapshot.each { |e| expect(e).to be_a(SerializedEvent) }
|
78
|
+
end
|
79
|
+
|
80
|
+
it "corresponds to the events used to build the snapshot" do
|
81
|
+
(serialized_attrs - [ :serialized_event ]).each { |attr|
|
82
|
+
expect(event_snapshot.first.send(attr)).to eq(first_event[attr])
|
83
|
+
expect(event_snapshot.last.send(attr)).to eq(last_event[attr])
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "unescapes the serialized events" do
|
88
|
+
expected_event = EventStore.unescape_bytea(last_event[:serialized_event])
|
89
|
+
expect(event_snapshot.last.serialized_event).to eq(expected_event)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#exists?" do
|
94
|
+
it "does exist" do
|
95
|
+
expect(snapshot.exists?).to eq(true)
|
96
|
+
end
|
97
|
+
|
98
|
+
context "without a snapshot" do
|
99
|
+
before(:each) { snapshot.delete_snapshot! }
|
100
|
+
|
101
|
+
it "does not exist" do
|
102
|
+
expect(snapshot.exists?).to eq(false)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexia_event_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Saieg, John Colvin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -251,6 +251,8 @@ files:
|
|
251
251
|
- spec/event_store/snapshot_spec.rb
|
252
252
|
- spec/event_store/vertica guy notes.txt
|
253
253
|
- spec/spec_helper.rb
|
254
|
+
- spec/unit/aggregate_unit_spec.rb
|
255
|
+
- spec/unit/snapshot_unit_spec.rb
|
254
256
|
homepage: https://github.com/nexiahome/event_store
|
255
257
|
licenses:
|
256
258
|
- MIT
|
@@ -271,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
271
273
|
version: '0'
|
272
274
|
requirements: []
|
273
275
|
rubyforge_project:
|
274
|
-
rubygems_version: 2.4.
|
276
|
+
rubygems_version: 2.4.5
|
275
277
|
signing_key:
|
276
278
|
specification_version: 4
|
277
279
|
summary: Ruby implementation of an EventSource (A+ES) for the Nexia Ecosystem
|
@@ -286,3 +288,5 @@ test_files:
|
|
286
288
|
- spec/event_store/snapshot_spec.rb
|
287
289
|
- spec/event_store/vertica guy notes.txt
|
288
290
|
- spec/spec_helper.rb
|
291
|
+
- spec/unit/aggregate_unit_spec.rb
|
292
|
+
- spec/unit/snapshot_unit_spec.rb
|