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 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: