sandthorn 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|