sandthorn 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +1 -1
- data/README.md +5 -5
- data/lib/sandthorn.rb +0 -1
- data/lib/sandthorn/aggregate_root_base.rb +15 -27
- data/lib/sandthorn/aggregate_root_marshal.rb +4 -5
- data/lib/sandthorn/version.rb +1 -1
- data/sandthorn.gemspec +1 -1
- data/spec/aggregate_delta_spec.rb +6 -13
- data/spec/aggregate_events_spec.rb +3 -3
- data/spec/aggregate_root_spec.rb +37 -41
- data/spec/complex_aggregate_spec.rb +1 -1
- data/spec/constructor_events_spec.rb +5 -5
- data/spec/default_attributes_spec.rb +3 -3
- data/spec/initialize_signature_change_spec.rb +2 -2
- data/spec/stateless_events_spec.rb +11 -7
- data/spec/tracing_spec.rb +6 -6
- metadata +4 -13
- data/lib/sandthorn/event.rb +0 -61
- data/spec/aggregate_snapshot_spec.rb +0 -263
- data/spec/different_driver_spec.rb +0 -101
- data/spec/event_inspector_spec.rb +0 -152
- data/spec/event_spec.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eb2833dc0d8dfa96bc80089a176f2718040f1107f0305232840e3009232511bb
|
4
|
+
data.tar.gz: 1c57a94df6798dfa32a1050d105bec05ccf30e08876c0a50aa610e3f7be3ce11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f21f398f4bbfc6ed3e23be70e294f5fbabfec60d1ed69bb03b8efd0f047d2131d9d225f4dd9794a24d45661ad6200b85364b54c50590825f0fa4bf9eb1d6759d
|
7
|
+
data.tar.gz: 027501a879da6025ffd51511e88b8affb9eb21e2e493086e143cab1faaa50e462142771a423a1e93f0d7b31f81da381869f7cfbebe5caf8a82880863c9ac9b4d
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/Sandthorn/sandthorn.svg?branch=master)](https://travis-ci.org/Sandthorn/sandthorn)
|
2
|
-
[![Coverage Status](https://coveralls.io/repos/Sandthorn/sandthorn/badge.
|
3
|
-
[![Code Climate](https://codeclimate.com/github/Sandthorn/sandthorn.
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/sandthorn.
|
2
|
+
[![Coverage Status](https://coveralls.io/repos/Sandthorn/sandthorn/badge.svg?branch=master)](https://coveralls.io/r/Sandthorn/sandthorn?branch=master)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/Sandthorn/sandthorn.svg)](https://codeclimate.com/github/Sandthorn/sandthorn)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/sandthorn.svg)](http://badge.fury.io/rb/sandthorn)
|
5
5
|
|
6
6
|
# Sandthorn Event Sourcing
|
7
7
|
A ruby library for saving an object's state as a series of events.
|
@@ -124,7 +124,7 @@ class Board
|
|
124
124
|
include Sandthorn::AggregateRoot
|
125
125
|
|
126
126
|
# creates a private class method `board_created`
|
127
|
-
|
127
|
+
constructor_events :board_created
|
128
128
|
|
129
129
|
def self.create name
|
130
130
|
|
@@ -137,7 +137,7 @@ end
|
|
137
137
|
|
138
138
|
### `Sandthorn::AggregateRoot::stateless_events`
|
139
139
|
|
140
|
-
Calling `stateless_events` creates public class methods. The first argument is an `aggregate_id` and the second argument is optional but has to be a hash and is stored in the
|
140
|
+
Calling `stateless_events` creates public class methods. The first argument is an `aggregate_id` and the second argument is optional but has to be a hash and is stored in the event_data of the event.
|
141
141
|
|
142
142
|
When creating a stateless event, the corresponding aggregate is never loaded and the event is saved without calling the save method.
|
143
143
|
|
data/lib/sandthorn.rb
CHANGED
@@ -118,6 +118,7 @@ module Sandthorn
|
|
118
118
|
current_aggregate_version = events.last[:aggregate_version]
|
119
119
|
aggregate.send :set_orginating_aggregate_version!, current_aggregate_version
|
120
120
|
aggregate.send :set_current_aggregate_version!, current_aggregate_version
|
121
|
+
aggregate.send :set_aggregate_id, events.first.fetch(:aggregate_id)
|
121
122
|
end
|
122
123
|
attributes = build_instance_vars_from_events events
|
123
124
|
aggregate.send :clear_aggregate_events
|
@@ -132,7 +133,7 @@ module Sandthorn
|
|
132
133
|
def stateless_events(*event_names)
|
133
134
|
event_names.each do |name|
|
134
135
|
define_singleton_method name do |aggregate_id, *args|
|
135
|
-
event = build_stateless_event(name.to_s, args)
|
136
|
+
event = build_stateless_event(aggregate_id, name.to_s, args)
|
136
137
|
Sandthorn.save_events([event], aggregate_id, self)
|
137
138
|
return aggregate_id
|
138
139
|
end
|
@@ -169,24 +170,18 @@ module Sandthorn
|
|
169
170
|
|
170
171
|
private
|
171
172
|
|
172
|
-
def build_stateless_event name, args = []
|
173
|
+
def build_stateless_event aggregate_id, name, args = []
|
173
174
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
old_value: nil,
|
178
|
-
new_value: value
|
179
|
-
}
|
175
|
+
deltas = {}
|
176
|
+
args.first.each do |key, value|
|
177
|
+
deltas[key.to_sym] = { old_value: nil, new_value: value }
|
180
178
|
end unless args.empty?
|
181
179
|
|
182
|
-
data = {
|
183
|
-
attribute_deltas: formated_attribute_deltas
|
184
|
-
}
|
185
|
-
|
186
180
|
return {
|
187
181
|
aggregate_version: nil,
|
182
|
+
aggregate_id: aggregate_id,
|
188
183
|
event_name: name,
|
189
|
-
event_data:
|
184
|
+
event_data: deltas,
|
190
185
|
event_metadata: nil
|
191
186
|
}
|
192
187
|
|
@@ -194,11 +189,11 @@ module Sandthorn
|
|
194
189
|
|
195
190
|
def build_instance_vars_from_events events
|
196
191
|
events.each_with_object({}) do |event, instance_vars|
|
197
|
-
|
198
|
-
attribute_deltas = event_data[:attribute_deltas]
|
192
|
+
attribute_deltas = event[:event_data]
|
199
193
|
unless attribute_deltas.nil?
|
200
|
-
deltas =
|
201
|
-
|
194
|
+
deltas = {}
|
195
|
+
attribute_deltas.each do |key, value|
|
196
|
+
deltas[key] = value[:new_value]
|
202
197
|
end
|
203
198
|
instance_vars.merge! deltas
|
204
199
|
end
|
@@ -220,10 +215,7 @@ module Sandthorn
|
|
220
215
|
|
221
216
|
def extract_relevant_aggregate_instance_variables
|
222
217
|
instance_variables.select do |variable|
|
223
|
-
|
224
|
-
does_not_contain_aggregate = !variable.to_s.start_with?("@aggregate_")
|
225
|
-
|
226
|
-
equals_aggregate_id || does_not_contain_aggregate
|
218
|
+
!variable.to_s.start_with?("@aggregate_")
|
227
219
|
end
|
228
220
|
end
|
229
221
|
|
@@ -252,17 +244,13 @@ module Sandthorn
|
|
252
244
|
end
|
253
245
|
|
254
246
|
def commit_with_event_name(event_name)
|
255
|
-
aggregate_attribute_deltas = get_delta
|
256
|
-
|
257
247
|
increase_current_aggregate_version!
|
258
|
-
data = {
|
259
|
-
attribute_deltas: aggregate_attribute_deltas
|
260
|
-
}
|
261
248
|
|
262
249
|
@aggregate_events << ({
|
263
250
|
aggregate_version: @aggregate_current_event_version,
|
251
|
+
aggregate_id: @aggregate_id,
|
264
252
|
event_name: event_name,
|
265
|
-
event_data:
|
253
|
+
event_data: get_delta(),
|
266
254
|
event_metadata: @aggregate_trace_information
|
267
255
|
})
|
268
256
|
|
@@ -3,7 +3,7 @@ module Sandthorn
|
|
3
3
|
module Marshal
|
4
4
|
|
5
5
|
def aggregate_initialize *args
|
6
|
-
@aggregate_attribute_deltas =
|
6
|
+
@aggregate_attribute_deltas = {}
|
7
7
|
@aggregate_stored_instance_variables = {}
|
8
8
|
end
|
9
9
|
|
@@ -20,7 +20,7 @@ module Sandthorn
|
|
20
20
|
def get_delta
|
21
21
|
deltas = extract_relevant_aggregate_instance_variables
|
22
22
|
deltas.each { |d| delta_attribute(d) }
|
23
|
-
|
23
|
+
|
24
24
|
result = @aggregate_attribute_deltas
|
25
25
|
clear_aggregate_deltas
|
26
26
|
result
|
@@ -42,8 +42,7 @@ module Sandthorn
|
|
42
42
|
new_value_to_store = ::Marshal.load(new_dump)
|
43
43
|
old_value_to_store = old_dump ? ::Marshal.load(old_dump) : nil
|
44
44
|
|
45
|
-
@aggregate_attribute_deltas
|
46
|
-
attribute_name: attribute_name.to_s.delete("@"),
|
45
|
+
@aggregate_attribute_deltas[attribute_name.to_s.delete("@").to_sym] = {
|
47
46
|
old_value: old_value_to_store,
|
48
47
|
new_value: new_value_to_store
|
49
48
|
}
|
@@ -54,7 +53,7 @@ module Sandthorn
|
|
54
53
|
end
|
55
54
|
|
56
55
|
def clear_aggregate_deltas
|
57
|
-
@aggregate_attribute_deltas =
|
56
|
+
@aggregate_attribute_deltas = {}
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
data/lib/sandthorn/version.rb
CHANGED
data/sandthorn.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["lars.krantz@alaz.se", "morgan.hallgren@gmail.com", "jesper.josefsson@gmail.com"]
|
11
11
|
spec.description = %q{Event sourcing gem}
|
12
12
|
spec.summary = %q{Event sourcing gem}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/Sandthorn/sandthorn"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
3
|
class PersonTest
|
6
4
|
include Sandthorn::AggregateRoot
|
7
5
|
attr_reader :name
|
@@ -44,15 +42,14 @@ describe 'Property Delta Event Sourcing' do
|
|
44
42
|
|
45
43
|
it 'should be able to set name' do
|
46
44
|
person.change_name "Klabbarparen"
|
47
|
-
expect(person.name).to
|
48
|
-
#puts person.aggregate_events
|
45
|
+
expect(person.name).to eq("Klabbarparen")
|
49
46
|
end
|
50
47
|
|
51
48
|
it 'should be able to build from events' do
|
52
49
|
person.change_name "Klabbarparen"
|
53
50
|
builded = PersonTest.aggregate_build person.aggregate_events
|
54
|
-
expect(builded.name).to
|
55
|
-
expect(builded.aggregate_id).to
|
51
|
+
expect(builded.name).to eq(person.name)
|
52
|
+
expect(builded.aggregate_id).to eq(person.aggregate_id)
|
56
53
|
end
|
57
54
|
|
58
55
|
it 'should not have any events when built up' do
|
@@ -75,16 +72,12 @@ describe 'Property Delta Event Sourcing' do
|
|
75
72
|
person.add_to_hash :bar, "foo"
|
76
73
|
|
77
74
|
builded = PersonTest.aggregate_build person.aggregate_events
|
78
|
-
expect(builded.my_hash[:foo]).to
|
79
|
-
expect(builded.my_hash[:bar]).to
|
75
|
+
expect(builded.my_hash[:foo]).to eq("bar")
|
76
|
+
expect(builded.my_hash[:bar]).to eq("foo")
|
80
77
|
|
81
78
|
person.add_to_hash :foo, "BAR"
|
82
79
|
|
83
|
-
#events = person.aggregate_events
|
84
|
-
#events << builded.aggregate_events
|
85
|
-
#puts events
|
86
|
-
|
87
80
|
builded2 = PersonTest.aggregate_build person.aggregate_events
|
88
|
-
expect(builded2.my_hash[:foo]).to
|
81
|
+
expect(builded2.my_hash[:foo]).to eq("BAR")
|
89
82
|
end
|
90
83
|
end
|
@@ -44,7 +44,7 @@ module Sandthorn
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should set the name instance variable" do
|
47
|
-
expect(subject.name).to
|
47
|
+
expect(subject.name).to eq("new name")
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should store the event params as methods args" do
|
@@ -52,11 +52,11 @@ module Sandthorn
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should store the args to the event" do
|
55
|
-
expect(subject.aggregate_events[1][:event_data][:
|
55
|
+
expect(subject.aggregate_events[1][:event_data][:name][:new_value]).to eq("new name")
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should store the event_name" do
|
59
|
-
expect(subject.aggregate_events[1][:event_name]).to
|
59
|
+
expect(subject.aggregate_events[1][:event_name]).to eq("name_changed")
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
data/spec/aggregate_root_spec.rb
CHANGED
@@ -5,12 +5,12 @@ module Sandthorn
|
|
5
5
|
class DirtyClass
|
6
6
|
include Sandthorn::AggregateRoot
|
7
7
|
attr_reader :name, :age
|
8
|
-
attr :
|
8
|
+
attr :age
|
9
9
|
attr_writer :writer
|
10
10
|
|
11
11
|
def initialize args = {}
|
12
12
|
@name = args.fetch(:name, nil)
|
13
|
-
@
|
13
|
+
@age = args.fetch(:age, nil)
|
14
14
|
@writer = args.fetch(:writer, nil)
|
15
15
|
end
|
16
16
|
|
@@ -21,9 +21,9 @@ module Sandthorn
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
unless
|
26
|
-
@
|
24
|
+
def change_age value
|
25
|
+
unless age == value
|
26
|
+
@age = value
|
27
27
|
commit
|
28
28
|
end
|
29
29
|
end
|
@@ -64,7 +64,7 @@ module Sandthorn
|
|
64
64
|
|
65
65
|
context "all" do
|
66
66
|
it "should all the aggregates" do
|
67
|
-
expect(subject.length).to
|
67
|
+
expect(subject.length).to eq(3)
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should include correct aggregates" do
|
@@ -83,10 +83,10 @@ module Sandthorn
|
|
83
83
|
|
84
84
|
context "new with args" do
|
85
85
|
|
86
|
-
let(:subject) { DirtyClass.new(name: "Mogge",
|
86
|
+
let(:subject) { DirtyClass.new(name: "Mogge", age: 35, writer: true) }
|
87
87
|
it "should set the values" do
|
88
|
-
expect(subject.name).to
|
89
|
-
expect(subject.
|
88
|
+
expect(subject.name).to eq("Mogge")
|
89
|
+
expect(subject.age).to eq(35)
|
90
90
|
expect{subject.writer}.to raise_error NoMethodError
|
91
91
|
end
|
92
92
|
end
|
@@ -95,23 +95,23 @@ module Sandthorn
|
|
95
95
|
|
96
96
|
it "should get new_name" do
|
97
97
|
dirty_object.change_name "new_name"
|
98
|
-
expect(dirty_object.name).to
|
98
|
+
expect(dirty_object.name).to eq("new_name")
|
99
99
|
end
|
100
100
|
|
101
101
|
it "should generate one event on new" do
|
102
|
-
expect(dirty_object.aggregate_events.length).to
|
102
|
+
expect(dirty_object.aggregate_events.length).to eq(1)
|
103
103
|
end
|
104
104
|
|
105
105
|
it "should generate 2 events new and change_name" do
|
106
106
|
dirty_object.change_name "new_name"
|
107
|
-
expect(dirty_object.aggregate_events.length).to
|
107
|
+
expect(dirty_object.aggregate_events.length).to eq(2)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
context "when changing
|
112
|
-
it "should get
|
113
|
-
dirty_object.
|
114
|
-
expect(dirty_object.
|
111
|
+
context "when changing age (attr)" do
|
112
|
+
it "should get new_age" do
|
113
|
+
dirty_object.change_age "new_age"
|
114
|
+
expect(dirty_object.age).to eq("new_age")
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -123,27 +123,27 @@ module Sandthorn
|
|
123
123
|
|
124
124
|
context "save" do
|
125
125
|
it "should not have events on aggregate after save" do
|
126
|
-
expect(dirty_object.save.aggregate_events.length).to
|
126
|
+
expect(dirty_object.save.aggregate_events.length).to eq(0)
|
127
127
|
end
|
128
128
|
|
129
129
|
it "should have aggregate_originating_version == 0 pre save" do
|
130
|
-
expect(dirty_object.aggregate_originating_version).to
|
130
|
+
expect(dirty_object.aggregate_originating_version).to eq(0)
|
131
131
|
end
|
132
132
|
|
133
133
|
it "should have aggregate_originating_version == 1 post save" do
|
134
|
-
expect(dirty_object.save.aggregate_originating_version).to
|
134
|
+
expect(dirty_object.save.aggregate_originating_version).to eq(1)
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
138
|
context "find" do
|
139
139
|
before(:each) { dirty_object.save }
|
140
140
|
it "should find by id" do
|
141
|
-
expect(DirtyClass.find(dirty_object.id).id).to
|
141
|
+
expect(DirtyClass.find(dirty_object.id).id).to eq(dirty_object.id)
|
142
142
|
end
|
143
143
|
|
144
144
|
it "should hold changed name" do
|
145
145
|
dirty_object.change_name("morgan").save
|
146
|
-
expect(DirtyClass.find(dirty_object.id).name).to
|
146
|
+
expect(DirtyClass.find(dirty_object.id).name).to eq("morgan")
|
147
147
|
end
|
148
148
|
|
149
149
|
it "should raise error if trying to find id that not exist" do
|
@@ -157,7 +157,7 @@ module Sandthorn
|
|
157
157
|
describe "event data" do
|
158
158
|
|
159
159
|
let(:dirty_object) {
|
160
|
-
o = DirtyClass.new :name => "old_value", :
|
160
|
+
o = DirtyClass.new :name => "old_value", :age => 35
|
161
161
|
o.save
|
162
162
|
}
|
163
163
|
|
@@ -167,7 +167,7 @@ module Sandthorn
|
|
167
167
|
|
168
168
|
it "should set the old_value on the event" do
|
169
169
|
dirty_object_after_find.change_name "new_name"
|
170
|
-
expect(dirty_object_after_find.aggregate_events.last[:event_data][:
|
170
|
+
expect(dirty_object_after_find.aggregate_events.last[:event_data][:name][:old_value]).to eq("old_value")
|
171
171
|
end
|
172
172
|
|
173
173
|
end
|
@@ -176,36 +176,32 @@ module Sandthorn
|
|
176
176
|
|
177
177
|
it "should set the old_value on the event" do
|
178
178
|
dirty_object.change_name "new_name"
|
179
|
-
expect(dirty_object.aggregate_events.last[:event_data][:
|
179
|
+
expect(dirty_object.aggregate_events.last[:event_data][:name][:old_value]).to eq("old_value")
|
180
180
|
end
|
181
181
|
|
182
182
|
it "should not change aggregate_id" do
|
183
183
|
dirty_object.change_name "new_name"
|
184
|
-
expect(dirty_object.aggregate_events.last[:event_data][
|
184
|
+
expect(dirty_object.aggregate_events.last[:event_data]["attribute_name"]).not_to eq("aggregate_id")
|
185
185
|
end
|
186
186
|
|
187
|
-
it "should not change
|
187
|
+
it "should not change age attribute if age method is not runned" do
|
188
188
|
dirty_object.change_name "new_name"
|
189
189
|
dirty_object.aggregate_events.each do |event|
|
190
|
-
event[:event_data][
|
191
|
-
expect(attribute_delta[:attribute_name]).not_to eql "sex"
|
192
|
-
end
|
190
|
+
expect(event[:event_data]["age"].nil?).to be_truthy
|
193
191
|
end
|
194
192
|
end
|
195
193
|
|
196
|
-
it "should not change
|
197
|
-
dirty_object.
|
194
|
+
it "should not change age attribute if age attribute is the same" do
|
195
|
+
dirty_object.change_age 35
|
198
196
|
dirty_object.aggregate_events.each do |event|
|
199
|
-
event[:event_data][
|
200
|
-
expect(attribute_delta[:attribute_name]).not_to eql "sex"
|
201
|
-
end
|
197
|
+
expect(event[:event_data]["age"].nil?).to be_truthy
|
202
198
|
end
|
203
199
|
end
|
204
200
|
|
205
|
-
it "should set old_value and new_value on
|
206
|
-
dirty_object.
|
207
|
-
expect(dirty_object.aggregate_events.last[:event_data][:
|
208
|
-
expect(dirty_object.aggregate_events.last[:event_data][:
|
201
|
+
it "should set old_value and new_value on age change" do
|
202
|
+
dirty_object.change_age 36
|
203
|
+
expect(dirty_object.aggregate_events.last[:event_data][:age][:old_value]).to eq(35)
|
204
|
+
expect(dirty_object.aggregate_events.last[:event_data][:age][:new_value]).to eq(36)
|
209
205
|
end
|
210
206
|
end
|
211
207
|
end
|
@@ -218,11 +214,11 @@ module Sandthorn
|
|
218
214
|
end
|
219
215
|
|
220
216
|
it "should have the event no_state_change_only_empty_event" do
|
221
|
-
expect(dirty_object.aggregate_events.first[:event_name]).to
|
217
|
+
expect(dirty_object.aggregate_events.first[:event_name]).to eq("no_state_change_only_empty_event")
|
222
218
|
end
|
223
219
|
|
224
|
-
it "should have
|
225
|
-
expect(dirty_object.aggregate_events.first[:event_data]
|
220
|
+
it "should have event_data set to empty hash" do
|
221
|
+
expect(dirty_object.aggregate_events.first[:event_data]).to eq({})
|
226
222
|
end
|
227
223
|
|
228
224
|
end
|