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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 264411828ceee4ea1915efe3f722133d514b4bf2
4
- data.tar.gz: ec65529f45578093403dd4fc53042ea98b527594
3
+ metadata.gz: 29159ff3ce6f7b8393363e2bf740c3a37d57a2b2
4
+ data.tar.gz: 9b1053c87fb402b1887acb6bc8a0fd6faee71ebf
5
5
  SHA512:
6
- metadata.gz: 0d6ddfdf0147881f3d2346f7eee7d25a2d8c63e497d93bae583a94b49f78d34790ce16aa611aece9fdfcb179f70026cd095708c974b40828678ff7c3b87354cd
7
- data.tar.gz: 40747fe40ae47821bef14ff855876861a2d2c76d7a69749d363282cc9f3c2eb291d07b188d1793f8c04fde83f2175a5473b3d56884e2b45166f73ea37a1b7ad9
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]
@@ -1,3 +1,3 @@
1
1
  module Sandthorn
2
- VERSION = "0.12.0"
2
+ VERSION = "0.13.0"
3
3
  end
@@ -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
- include Sandthorn::AggregateRoot
5
- def initialize
6
- end
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 rebuilt " do
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 have an set the array attribute to [] on new" do
57
+ it "should set the array attribute to ['banana'] on rebuilt" do
34
58
  add_default_attributes
35
59
  aggregate = DefaultAttributes.new
36
- expect(aggregate.array).to eql []
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.12.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-03-29 00:00:00.000000000 Z
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: