sandthorn 0.12.0 → 0.13.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/README.md +41 -4
- data/lib/sandthorn/aggregate_root_base.rb +54 -4
- data/lib/sandthorn/version.rb +1 -1
- data/spec/constructor_events_spec.rb +64 -0
- data/spec/default_attributes_spec.rb +35 -8
- data/spec/stateless_events_spec.rb +92 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29159ff3ce6f7b8393363e2bf740c3a37d57a2b2
|
4
|
+
data.tar.gz: 9b1053c87fb402b1887acb6bc8a0fd6faee71ebf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5cf43faad3a61d3fe307c1598701e99644c7f2d7ea57aa336cd6f22fe9b4c6d707406657ee824619baf0b32ff5418500b46649ace7e1cefa25092529a739b7c
|
7
|
+
data.tar.gz: 2802ac2bf37494ff1ddd4cfd050057e070ddad9422ccee0f9078be37c12c2772bf3402ca7cee6ce92839c042df04df996b658cf3b50bbd1376e60231e590440f
|
data/README.md
CHANGED
@@ -222,9 +222,9 @@ In this exampel the `events` method will generate a method called `marked`, this
|
|
222
222
|
```ruby
|
223
223
|
class Board
|
224
224
|
include Sandthorn::AggregateRoot
|
225
|
-
|
225
|
+
|
226
226
|
events :marked
|
227
|
-
|
227
|
+
|
228
228
|
def mark player, pos_x, pos_y
|
229
229
|
# change some state
|
230
230
|
marked(player) do
|
@@ -235,6 +235,43 @@ class Board
|
|
235
235
|
end
|
236
236
|
```
|
237
237
|
|
238
|
+
### `Sandthorn::AggregateRoot::constructor_events`
|
239
|
+
|
240
|
+
Before version 0.13.0 the only initial event on an aggregate were `new`. With the `constructor_events` its possible to be more specific on how an aggregate came to be.
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
class Board
|
244
|
+
include Sandthorn::AggregateRoot
|
245
|
+
|
246
|
+
# creates a private class method `board_created`
|
247
|
+
contructor_events :board_created
|
248
|
+
|
249
|
+
def self.create name
|
250
|
+
|
251
|
+
board_created(name) do
|
252
|
+
@name = name
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
```
|
257
|
+
|
258
|
+
### `Sandthorn::AggregateRoot::stateless_events`
|
259
|
+
|
260
|
+
Calling `stateless_events` creates public class methods. The first argument is an `aggregate_id`. The resulting event is attached to the referenced aggregate. The rest of the arguments are optional and are stored in the method_args of the event.
|
261
|
+
|
262
|
+
When creating a stateless event, the corresponding aggregate is never loaded. The event is appendend to the aggregate's event stream.
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
class Board
|
266
|
+
include Sandthorn::AggregateRoot
|
267
|
+
|
268
|
+
stateless_events :player_went_to_toilet
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
Board.player_went_to_toilet "board_aggregate_id", player_id, time
|
273
|
+
```
|
274
|
+
|
238
275
|
### `Sandthorn::AggregateRoot::default_attributes`
|
239
276
|
|
240
277
|
Its possible to add a default_attributes method on an aggregate and set default values to new and already created aggregates.
|
@@ -264,7 +301,7 @@ end
|
|
264
301
|
|
265
302
|
`commit` determines the state changes by monitoring the object's readable fields.
|
266
303
|
|
267
|
-
Since version 0.10.0 of Sandthorn the concept `events` have been introduced to abstract away the usage of `commit`. Commit still works as before but we think that the `events` abstraction makes the aggregate more readable.
|
304
|
+
Since version 0.10.0 of Sandthorn the concept `events` have been introduced to abstract away the usage of `commit`. Commit still works as before but we think that the `events` abstraction makes the aggregate more readable.
|
268
305
|
|
269
306
|
### `Sandthorn::AggregateRoot.save`
|
270
307
|
|
@@ -328,7 +365,7 @@ In this case, the resulting events from the commands `new` and `mark` will have
|
|
328
365
|
|
329
366
|
## Bounded Context
|
330
367
|
|
331
|
-
A bounded context is a system divider that split large systems into smaller parts. [Bounded Context by Martin Fowler](http://martinfowler.com/bliki/BoundedContext.html)
|
368
|
+
A bounded context is a system divider that split large systems into smaller parts. [Bounded Context by Martin Fowler](http://martinfowler.com/bliki/BoundedContext.html)
|
332
369
|
|
333
370
|
A module can include `Sandthorn::BoundedContext` and all aggregates within the module can be retreived via the ::aggregate_types method on the module. A use case is to use it when Sandthorn is configured and setup all aggregates in a bounded context to a driver.
|
334
371
|
|
@@ -6,7 +6,6 @@ module Sandthorn
|
|
6
6
|
attr_reader :aggregate_events
|
7
7
|
attr_reader :aggregate_current_event_version
|
8
8
|
attr_reader :aggregate_originating_version
|
9
|
-
attr_reader :aggregate_stored_serialized_object
|
10
9
|
attr_reader :aggregate_trace_information
|
11
10
|
|
12
11
|
alias :id :aggregate_id
|
@@ -72,8 +71,8 @@ module Sandthorn
|
|
72
71
|
end
|
73
72
|
|
74
73
|
def all
|
75
|
-
Sandthorn.all(self).map { |events|
|
76
|
-
aggregate_build events
|
74
|
+
Sandthorn.all(self).map { |events|
|
75
|
+
aggregate_build events
|
77
76
|
}
|
78
77
|
end
|
79
78
|
|
@@ -116,7 +115,7 @@ module Sandthorn
|
|
116
115
|
end
|
117
116
|
|
118
117
|
if events.any?
|
119
|
-
current_aggregate_version = events.last[:aggregate_version]
|
118
|
+
current_aggregate_version = events.last[:aggregate_version]
|
120
119
|
aggregate.send :set_orginating_aggregate_version!, current_aggregate_version
|
121
120
|
aggregate.send :set_current_aggregate_version!, current_aggregate_version
|
122
121
|
end
|
@@ -131,6 +130,37 @@ module Sandthorn
|
|
131
130
|
aggregate
|
132
131
|
end
|
133
132
|
|
133
|
+
|
134
|
+
|
135
|
+
def stateless_events(*event_names)
|
136
|
+
event_names.each do |name|
|
137
|
+
define_singleton_method name do |aggregate_id, *args|
|
138
|
+
event = build_event(name.to_s, args, [], nil)
|
139
|
+
Sandthorn.save_events([event], aggregate_id, self)
|
140
|
+
return aggregate_id
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def constructor_events(*event_names)
|
147
|
+
event_names.each do |name|
|
148
|
+
define_singleton_method name do |*args, &block|
|
149
|
+
|
150
|
+
create_new_empty_aggregate.tap do |aggregate|
|
151
|
+
aggregate.aggregate_base_initialize
|
152
|
+
aggregate.aggregate_initialize
|
153
|
+
aggregate.send :set_aggregate_id, Sandthorn.generate_aggregate_id
|
154
|
+
aggregate.instance_eval(&block) if block
|
155
|
+
aggregate.send :commit_with_event_name, name.to_s, args
|
156
|
+
return aggregate
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
self.singleton_class.class_eval { private name.to_s }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
134
164
|
def events(*event_names)
|
135
165
|
event_names.each do |name|
|
136
166
|
define_method(name) do |*args, &block|
|
@@ -143,6 +173,26 @@ module Sandthorn
|
|
143
173
|
|
144
174
|
private
|
145
175
|
|
176
|
+
def build_event name, args, attribute_deltas, aggregate_version, trace_information = nil
|
177
|
+
|
178
|
+
data = {
|
179
|
+
method_name: name,
|
180
|
+
method_args: args,
|
181
|
+
attribute_deltas: attribute_deltas
|
182
|
+
}
|
183
|
+
|
184
|
+
unless trace_information.nil? || trace_information.empty?
|
185
|
+
data.merge!({ trace: trace_information })
|
186
|
+
end
|
187
|
+
|
188
|
+
return {
|
189
|
+
aggregate_version: aggregate_version,
|
190
|
+
event_name: name,
|
191
|
+
event_args: data
|
192
|
+
}
|
193
|
+
|
194
|
+
end
|
195
|
+
|
146
196
|
def build_instance_vars_from_events events
|
147
197
|
events.each_with_object({}) do |event, instance_vars|
|
148
198
|
event_args = event[:event_args]
|
data/lib/sandthorn/version.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Sandthorn
|
4
|
+
class ConstructorEventsSpec
|
5
|
+
include AggregateRoot
|
6
|
+
|
7
|
+
constructor_events :created_event
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
|
11
|
+
def self.create name
|
12
|
+
created_event(name) { @name = name }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "::constructor_events" do
|
18
|
+
|
19
|
+
let(:subject) do
|
20
|
+
ConstructorEventsSpec.create("name").save
|
21
|
+
end
|
22
|
+
|
23
|
+
context "interface" do
|
24
|
+
|
25
|
+
it "should not expose constructor_events methods" do
|
26
|
+
expect(subject).not_to respond_to(:created_event)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create the constructor event on the class" do
|
30
|
+
expect(ConstructorEventsSpec.private_methods).to include(:created_event)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "::create" do
|
37
|
+
let(:aggregate_id) do
|
38
|
+
a = ConstructorEventsSpec.create("create_name")
|
39
|
+
a.save
|
40
|
+
a.aggregate_id
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should create an ConstructorEventsSpec aggregate" do
|
44
|
+
expect(ConstructorEventsSpec.find(aggregate_id)).to be_a ConstructorEventsSpec
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should set instance variable in aggregate" do
|
48
|
+
expect(ConstructorEventsSpec.find(aggregate_id).name).to eql "create_name"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should have created an created_event" do
|
52
|
+
expect(Sandthorn.get_aggregate_events(ConstructorEventsSpec, aggregate_id).first[:event_name]).to eql "created_event"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should have set the method_args to create_name" do
|
56
|
+
expect(Sandthorn.get_aggregate_events(ConstructorEventsSpec, aggregate_id).first[:event_args][:method_args].first).to eql "create_name"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should have set the attribute_delta name" do
|
60
|
+
expect(Sandthorn.get_aggregate_events(ConstructorEventsSpec, aggregate_id).first[:event_args][:attribute_deltas].last[:attribute_name]).to eql "name"
|
61
|
+
expect(Sandthorn.get_aggregate_events(ConstructorEventsSpec, aggregate_id).first[:event_args][:attribute_deltas].last[:new_value]).to eql "create_name"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,14 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
class DefaultAttributes
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
# class DefaultAttributes
|
4
|
+
# include Sandthorn::AggregateRoot
|
5
|
+
# def initialize
|
6
|
+
# end
|
7
|
+
# end
|
8
8
|
|
9
9
|
|
10
10
|
describe "when the initialize-method changes" do
|
11
11
|
|
12
|
+
before do
|
13
|
+
class DefaultAttributes
|
14
|
+
include Sandthorn::AggregateRoot
|
15
|
+
def initialize
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
#Make sure the DefaultAttributes class are reset on every test
|
22
|
+
after do
|
23
|
+
Object.send(:remove_const, :DefaultAttributes)
|
24
|
+
end
|
25
|
+
|
12
26
|
it "should not have an array attribute on first version of the DefaultAttributes class" do
|
13
27
|
aggregate = DefaultAttributes.new
|
14
28
|
expect(aggregate.respond_to?(:array)).to be_falsy
|
@@ -20,20 +34,33 @@ describe "when the initialize-method changes" do
|
|
20
34
|
DefaultAttributes.class_eval do
|
21
35
|
attr_reader :array
|
22
36
|
define_method :default_attributes, lambda { @array = [] }
|
37
|
+
define_method :add_item, lambda { |item|
|
38
|
+
@array << item
|
39
|
+
commit
|
40
|
+
}
|
23
41
|
end
|
24
42
|
end
|
25
43
|
|
26
|
-
it "should have set the array attribute to [] on
|
44
|
+
it "should have an set the array attribute to [] on new" do
|
45
|
+
add_default_attributes
|
46
|
+
aggregate = DefaultAttributes.new
|
47
|
+
expect(aggregate.array).to eql []
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should have set the array attribute to [] on rebuilt when attribute is intruduced after `new`" do
|
27
51
|
aggregate = DefaultAttributes.new
|
28
52
|
add_default_attributes
|
29
53
|
rebuilt_aggregate = DefaultAttributes.aggregate_build(aggregate.aggregate_events)
|
30
54
|
expect(rebuilt_aggregate.array).to eql []
|
31
55
|
end
|
32
56
|
|
33
|
-
it "should
|
57
|
+
it "should set the array attribute to ['banana'] on rebuilt" do
|
34
58
|
add_default_attributes
|
35
59
|
aggregate = DefaultAttributes.new
|
36
|
-
|
60
|
+
aggregate.add_item 'banana'
|
61
|
+
rebuilt_aggregate = DefaultAttributes.aggregate_build(aggregate.aggregate_events)
|
62
|
+
expect(rebuilt_aggregate.array).to eql ['banana']
|
37
63
|
end
|
64
|
+
|
38
65
|
end
|
39
66
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Sandthorn
|
4
|
+
class StatelessEventsSpec
|
5
|
+
include AggregateRoot
|
6
|
+
|
7
|
+
stateless_events :one_event, :some_other_event
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize name
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "::stateless_events" do
|
17
|
+
|
18
|
+
context "interface" do
|
19
|
+
|
20
|
+
it "should expose stateless_events methods" do
|
21
|
+
expect(StatelessEventsSpec).to respond_to(:one_event)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when adding a stateless event to an existing aggregate" do
|
27
|
+
|
28
|
+
let(:subject) do
|
29
|
+
StatelessEventsSpec.new("name").save
|
30
|
+
end
|
31
|
+
|
32
|
+
before do
|
33
|
+
StatelessEventsSpec.one_event(subject.aggregate_id, args, 1)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:args) do
|
37
|
+
{first: "first", other: [1,2,3]}
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:last_event) do
|
41
|
+
Sandthorn.get_aggregate_events(StatelessEventsSpec, subject.aggregate_id).last
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:reloaded_subject) do
|
45
|
+
StatelessEventsSpec.find subject.aggregate_id
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should add one_event last on the aggregate" do
|
49
|
+
expect(last_event[:event_name]).to eql "one_event"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not have any deltas in event" do
|
53
|
+
expect(last_event[:event_args][:attribute_deltas]).to eql []
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should store event arguments" do
|
57
|
+
expect(last_event[:event_args][:method_args].first).to eql(args)
|
58
|
+
expect(last_event[:event_args][:method_args].last).to eql(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should have same name attribute after reload" do
|
62
|
+
expect(subject.name).to eql(reloaded_subject.name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when adding stateless_events to none existing aggregate" do
|
67
|
+
|
68
|
+
before do
|
69
|
+
StatelessEventsSpec.one_event(aggregate_id, "argument_data")
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:aggregate_id) {"none_existing_aggregate_id"}
|
73
|
+
|
74
|
+
let(:events) do
|
75
|
+
Sandthorn.get_aggregate_events(StatelessEventsSpec, aggregate_id)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should store the stateless event as the first event" do
|
79
|
+
expect(events.length).to be 1
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should have correct aggregate_id in event" do
|
83
|
+
expect(events.first[:aggregate_id]).to eql aggregate_id
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should have event name one_event" do
|
87
|
+
expect(events.first[:event_name]).to eql "one_event"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandthorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Krantz
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-04-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -206,6 +206,7 @@ files:
|
|
206
206
|
- spec/benchmark_spec.rb
|
207
207
|
- spec/bounded_context_spec.rb
|
208
208
|
- spec/complex_aggregate_spec.rb
|
209
|
+
- spec/constructor_events_spec.rb
|
209
210
|
- spec/db/sequel_driver.sqlite3_old
|
210
211
|
- spec/default_attributes_spec.rb
|
211
212
|
- spec/different_driver_spec.rb
|
@@ -216,6 +217,7 @@ files:
|
|
216
217
|
- spec/initialize_signature_change_spec.rb
|
217
218
|
- spec/sandthorn_spec.rb
|
218
219
|
- spec/spec_helper.rb
|
220
|
+
- spec/stateless_events_spec.rb
|
219
221
|
- spec/support/custom_matchers.rb
|
220
222
|
- spec/tracing_spec.rb
|
221
223
|
homepage: ''
|
@@ -250,6 +252,7 @@ test_files:
|
|
250
252
|
- spec/benchmark_spec.rb
|
251
253
|
- spec/bounded_context_spec.rb
|
252
254
|
- spec/complex_aggregate_spec.rb
|
255
|
+
- spec/constructor_events_spec.rb
|
253
256
|
- spec/db/sequel_driver.sqlite3_old
|
254
257
|
- spec/default_attributes_spec.rb
|
255
258
|
- spec/different_driver_spec.rb
|
@@ -260,6 +263,7 @@ test_files:
|
|
260
263
|
- spec/initialize_signature_change_spec.rb
|
261
264
|
- spec/sandthorn_spec.rb
|
262
265
|
- spec/spec_helper.rb
|
266
|
+
- spec/stateless_events_spec.rb
|
263
267
|
- spec/support/custom_matchers.rb
|
264
268
|
- spec/tracing_spec.rb
|
265
269
|
has_rdoc:
|