sandthorn 0.5.1 → 0.6.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/.gitignore +1 -0
- data/Gemfile.lock +21 -21
- data/README.md +23 -3
- data/lib/sandthorn.rb +62 -53
- data/lib/sandthorn/aggregate_root_base.rb +19 -21
- data/lib/sandthorn/aggregate_root_snapshot.rb +14 -0
- data/lib/sandthorn/event_stores.rb +64 -0
- data/lib/sandthorn/version.rb +1 -1
- data/sandthorn.gemspec +1 -1
- data/spec/aggregate_root_spec.rb +11 -0
- data/spec/complex_aggregate_spec.rb +1 -1
- data/spec/event_stores_spec.rb +81 -0
- data/spec/get_events_spec.rb +65 -7
- data/spec/initialize_signature_change_spec.rb +25 -0
- data/spec/sandthorn_spec.rb +26 -11
- data/spec/spec_helper.rb +6 -3
- data/spec/support/custom_matchers.rb +7 -0
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f39fd41916e13c8d9af482497da9774295644984
|
4
|
+
data.tar.gz: b3ae469f87f318a99d27f34049513cf35cae754d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c3d769b134a6421cfe4164190726c9ce1448f06962ee24aee7aec32f14fc1cf7c4c6e5c2a6160c62a0c316f482e8d72709463c2ac3f0298e7584ab02f604011
|
7
|
+
data.tar.gz: d54a6d161c31dd38ff4bd03217035d35d2f445419d83c59caca649828b32543eaf9714ed8832b56f4262315fad95992936b778bdda52f602254103611067b6c2
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sandthorn (0.
|
4
|
+
sandthorn (0.6.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -9,7 +9,7 @@ GEM
|
|
9
9
|
autotest-standalone (4.5.11)
|
10
10
|
awesome_print (1.2.0)
|
11
11
|
coderay (1.1.0)
|
12
|
-
coveralls (0.7.
|
12
|
+
coveralls (0.7.1)
|
13
13
|
multi_json (~> 1.3)
|
14
14
|
rest-client
|
15
15
|
simplecov (>= 0.7)
|
@@ -19,11 +19,11 @@ GEM
|
|
19
19
|
docile (1.1.5)
|
20
20
|
gem-release (0.7.3)
|
21
21
|
method_source (0.8.2)
|
22
|
-
mime-types (2.3)
|
22
|
+
mime-types (2.4.3)
|
23
23
|
multi_json (1.10.1)
|
24
|
-
netrc (0.
|
24
|
+
netrc (0.8.0)
|
25
25
|
pg (0.17.1)
|
26
|
-
pry (0.10.
|
26
|
+
pry (0.10.1)
|
27
27
|
coderay (~> 1.1.0)
|
28
28
|
method_source (~> 0.8.1)
|
29
29
|
slop (~> 3.4)
|
@@ -34,25 +34,25 @@ GEM
|
|
34
34
|
rest-client (1.7.2)
|
35
35
|
mime-types (>= 1.16, < 3.0)
|
36
36
|
netrc (~> 0.7)
|
37
|
-
rspec (3.
|
38
|
-
rspec-core (~> 3.
|
39
|
-
rspec-expectations (~> 3.
|
40
|
-
rspec-mocks (~> 3.
|
41
|
-
rspec-core (3.
|
42
|
-
rspec-support (~> 3.
|
43
|
-
rspec-expectations (3.
|
37
|
+
rspec (3.1.0)
|
38
|
+
rspec-core (~> 3.1.0)
|
39
|
+
rspec-expectations (~> 3.1.0)
|
40
|
+
rspec-mocks (~> 3.1.0)
|
41
|
+
rspec-core (3.1.7)
|
42
|
+
rspec-support (~> 3.1.0)
|
43
|
+
rspec-expectations (3.1.2)
|
44
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
-
rspec-support (~> 3.
|
46
|
-
rspec-mocks (3.
|
47
|
-
rspec-support (~> 3.
|
48
|
-
rspec-support (3.
|
45
|
+
rspec-support (~> 3.1.0)
|
46
|
+
rspec-mocks (3.1.3)
|
47
|
+
rspec-support (~> 3.1.0)
|
48
|
+
rspec-support (3.1.2)
|
49
49
|
sandthorn_driver_sequel (1.1.0)
|
50
50
|
pg
|
51
51
|
sequel
|
52
|
-
sequel (4.
|
53
|
-
simplecov (0.9.
|
52
|
+
sequel (4.15.0)
|
53
|
+
simplecov (0.9.1)
|
54
54
|
docile (~> 1.1.0)
|
55
|
-
multi_json
|
55
|
+
multi_json (~> 1.0)
|
56
56
|
simplecov-html (~> 0.8.0)
|
57
57
|
simplecov-html (0.8.0)
|
58
58
|
slop (3.6.0)
|
@@ -60,7 +60,7 @@ GEM
|
|
60
60
|
term-ansicolor (1.3.0)
|
61
61
|
tins (~> 1.0)
|
62
62
|
thor (0.19.1)
|
63
|
-
tins (1.3.
|
63
|
+
tins (1.3.3)
|
64
64
|
yard (0.8.7.4)
|
65
65
|
|
66
66
|
PLATFORMS
|
@@ -75,7 +75,7 @@ DEPENDENCIES
|
|
75
75
|
pry
|
76
76
|
pry-doc
|
77
77
|
rake
|
78
|
-
rspec
|
78
|
+
rspec (~> 3.0)
|
79
79
|
sandthorn!
|
80
80
|
sandthorn_driver_sequel (~> 1.1)
|
81
81
|
sqlite3
|
data/README.md
CHANGED
@@ -64,10 +64,30 @@ class Ship
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
#
|
67
|
+
# Configure a driver
|
68
68
|
url = "sqlite://spec/db/sequel_driver.sqlite3"
|
69
|
-
|
70
|
-
Sandthorn.
|
69
|
+
sql_event_store = SandthornDriverSequel.driver_from_url(url: url)
|
70
|
+
Sandthorn.configure do |c|
|
71
|
+
c.event_store = sql_event_store
|
72
|
+
end
|
73
|
+
|
74
|
+
# Or configure many drivers
|
75
|
+
|
76
|
+
Sandthorn.configure do |c|
|
77
|
+
c.event_stores = {
|
78
|
+
default: sql_event_store,
|
79
|
+
other_event_store: other_store
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Assign your aggregates to a named event store
|
84
|
+
|
85
|
+
class Boat
|
86
|
+
include Sandthorn::AggregateRoot
|
87
|
+
event_store :other_event_store
|
88
|
+
end
|
89
|
+
|
90
|
+
# Aggregates with no explicit event store will use the default event store
|
71
91
|
|
72
92
|
# Migrate db schema for the sequel driver
|
73
93
|
migrator = SandthornDriverSequel::Migration.new url: url
|
data/lib/sandthorn.rb
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
require "sandthorn/version"
|
2
2
|
require "sandthorn/errors"
|
3
3
|
require "sandthorn/aggregate_root"
|
4
|
+
require "sandthorn/event_stores"
|
4
5
|
require 'yaml'
|
5
6
|
require 'securerandom'
|
6
7
|
|
7
8
|
module Sandthorn
|
8
9
|
class << self
|
9
|
-
|
10
|
-
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegators :configuration, :event_stores
|
13
|
+
|
14
|
+
def default_event_store
|
15
|
+
event_stores.default_store
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_event_store=(store)
|
19
|
+
event_stores.default_store = store
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure
|
23
|
+
yield(configuration) if block_given?
|
11
24
|
end
|
25
|
+
|
12
26
|
def configuration
|
13
|
-
@configuration ||=
|
27
|
+
@configuration ||= Configuration.new
|
14
28
|
end
|
15
29
|
|
16
30
|
def serialize data
|
@@ -31,35 +45,29 @@ module Sandthorn
|
|
31
45
|
SecureRandom.uuid
|
32
46
|
end
|
33
47
|
|
34
|
-
def get_aggregate_events aggregate_id,
|
35
|
-
|
48
|
+
def get_aggregate_events aggregate_id, aggregate_type
|
49
|
+
event_store_for(aggregate_type).get_aggregate_events aggregate_id, aggregate_type
|
36
50
|
end
|
37
51
|
|
38
|
-
def save_events aggregate_events, originating_aggregate_version, aggregate_id,
|
39
|
-
|
40
|
-
driver_for(class_name).save_events aggregate_events, originating_aggregate_version, aggregate_id, *class_name
|
41
|
-
#rescue UpptecEventSequelDriver::Errors::WrongAggregateVersionError => sequel_error
|
42
|
-
# raise UpptecEventFramework::Errors::ConcurrencyError.new sequel_error.message
|
43
|
-
#end
|
52
|
+
def save_events aggregate_events, originating_aggregate_version, aggregate_id, aggregate_type
|
53
|
+
event_store_for(aggregate_type).save_events aggregate_events, originating_aggregate_version, aggregate_id, *aggregate_type
|
44
54
|
end
|
45
55
|
|
46
|
-
def get_aggregate aggregate_id,
|
47
|
-
|
56
|
+
def get_aggregate aggregate_id, aggregate_type
|
57
|
+
event_store_for(aggregate_type).get_aggregate aggregate_id, aggregate_type
|
48
58
|
end
|
49
59
|
|
50
|
-
def save_snapshot aggregate_snapshot, aggregate_id,
|
51
|
-
|
60
|
+
def save_snapshot aggregate_snapshot, aggregate_id, aggregate_type
|
61
|
+
event_store_for(aggregate_type).save_snapshot aggregate_snapshot, aggregate_id, aggregate_type
|
52
62
|
end
|
53
63
|
|
54
|
-
def
|
55
|
-
|
64
|
+
def get_aggregate_list_by_type aggregate_type
|
65
|
+
event_store_for(aggregate_type).get_aggregate_list_by_typename aggregate_type
|
56
66
|
end
|
57
67
|
|
58
|
-
def get_events aggregate_types: [], take: 0, after_sequence_number: 0
|
59
|
-
|
60
|
-
|
61
|
-
driver = drivers.first
|
62
|
-
events = driver.get_events aggregate_types: aggregate_types, take: take, after_sequence_number: after_sequence_number
|
68
|
+
def get_events event_store: :default, aggregate_types: [], take: 0, after_sequence_number: 0
|
69
|
+
event_store = find_event_store(event_store)
|
70
|
+
events = event_store.get_events aggregate_types: aggregate_types, take: take, after_sequence_number: after_sequence_number
|
63
71
|
events.each do |event|
|
64
72
|
event[:event_args] = deserialize event[:event_data]
|
65
73
|
event.delete(:event_data)
|
@@ -68,43 +76,44 @@ module Sandthorn
|
|
68
76
|
end
|
69
77
|
|
70
78
|
def obsolete_snapshots type_names: [], min_event_distance: 0
|
71
|
-
|
72
|
-
obsolete
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
if block_given?
|
78
|
-
yield aggregate
|
79
|
-
else
|
80
|
-
yielder << aggregate
|
81
|
-
end
|
79
|
+
obsolete = event_stores.flat_map { |event_store| event_store.obsolete_snapshots(class_names: type_names, max_event_distance: min_event_distance) }
|
80
|
+
obsolete.map do |single_obsolete|
|
81
|
+
type = Kernel.const_get single_obsolete[:aggregate_type]
|
82
|
+
aggregate = type.aggregate_find(single_obsolete[:aggregate_id]).tap do |agg|
|
83
|
+
yield agg if block_given?
|
84
|
+
end
|
82
85
|
end
|
83
|
-
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_event_store(name)
|
89
|
+
event_stores.by_name(name)
|
84
90
|
end
|
85
91
|
|
86
92
|
private
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
def identify_driver_from_class class_name
|
93
|
-
matches = configuration.select do |conf|
|
94
|
-
r = Regexp.new "^#{conf[:aggregate_pattern]}"
|
95
|
-
pattern = class_name.to_s
|
96
|
-
conf[:aggregate_pattern].nil? || r.match(pattern)
|
93
|
+
|
94
|
+
def event_store_for(aggregate_type)
|
95
|
+
event_store = event_stores.by_name(aggregate_type.event_store).tap do |store|
|
96
|
+
yield(store) if block_given?
|
97
97
|
end
|
98
|
-
raise Sandthorn::Errors::ConfigurationError.new "Aggregate class #{class_name} is not configured for Sandthorn" if matches.empty?
|
99
|
-
first_match = matches.first
|
100
|
-
first_match[:driver]
|
101
98
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
99
|
+
|
100
|
+
class Configuration
|
101
|
+
extend Forwardable
|
102
|
+
|
103
|
+
def_delegators :default_store, :event_stores, :default_store=
|
104
|
+
|
105
|
+
def initialize
|
106
|
+
yield(self) if block_given?
|
107
|
+
end
|
108
|
+
|
109
|
+
def event_stores
|
110
|
+
@event_stores ||= EventStores.new
|
111
|
+
end
|
112
|
+
|
113
|
+
def event_store=(store)
|
114
|
+
@event_stores = EventStores.new(store)
|
115
|
+
end
|
116
|
+
alias_method :event_stores=, :event_store=
|
108
117
|
end
|
109
118
|
end
|
110
119
|
end
|
@@ -29,9 +29,8 @@ module Sandthorn
|
|
29
29
|
aggregate_events,
|
30
30
|
aggregate_originating_version,
|
31
31
|
aggregate_id,
|
32
|
-
self.class
|
32
|
+
self.class
|
33
33
|
)
|
34
|
-
|
35
34
|
@aggregate_events = []
|
36
35
|
@aggregate_originating_version = @aggregate_current_event_version
|
37
36
|
end
|
@@ -39,6 +38,10 @@ module Sandthorn
|
|
39
38
|
self
|
40
39
|
end
|
41
40
|
|
41
|
+
def ==(other)
|
42
|
+
other.respond_to?(:aggregate_id) && aggregate_id == other.aggregate_id
|
43
|
+
end
|
44
|
+
|
42
45
|
def aggregate_trace args
|
43
46
|
@aggregate_trace_information = args
|
44
47
|
yield self if block_given?
|
@@ -83,8 +86,16 @@ module Sandthorn
|
|
83
86
|
@@aggregate_trace_information = nil
|
84
87
|
end
|
85
88
|
|
89
|
+
def event_store(event_store = nil)
|
90
|
+
if event_store
|
91
|
+
@event_store = event_store
|
92
|
+
else
|
93
|
+
@event_store
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
86
97
|
def all
|
87
|
-
aggregate_id_list = Sandthorn.
|
98
|
+
aggregate_id_list = Sandthorn.get_aggregate_list_by_type(self)
|
88
99
|
find aggregate_id_list
|
89
100
|
end
|
90
101
|
|
@@ -94,17 +105,13 @@ module Sandthorn
|
|
94
105
|
end
|
95
106
|
|
96
107
|
def aggregate_find aggregate_id
|
97
|
-
|
98
|
-
events
|
99
|
-
|
100
|
-
unless events and !events.empty?
|
108
|
+
events = Sandthorn.get_aggregate(aggregate_id, self)
|
109
|
+
unless events && !events.empty?
|
101
110
|
raise Sandthorn::Errors::AggregateNotFound
|
102
111
|
end
|
103
|
-
|
104
112
|
transformed_events = events.map do |e|
|
105
113
|
e.merge(event_args: Sandthorn.deserialize(e[:event_data]))
|
106
114
|
end
|
107
|
-
|
108
115
|
aggregate_build transformed_events
|
109
116
|
end
|
110
117
|
|
@@ -128,7 +135,7 @@ module Sandthorn
|
|
128
135
|
current_aggregate_version = aggregate.aggregate_originating_version
|
129
136
|
events.shift
|
130
137
|
else
|
131
|
-
aggregate =
|
138
|
+
aggregate = create_new_empty_aggregate
|
132
139
|
end
|
133
140
|
|
134
141
|
attributes = build_instance_vars_from_events events
|
@@ -165,17 +172,8 @@ module Sandthorn
|
|
165
172
|
snapshot = events.first[:event_args][0]
|
166
173
|
end
|
167
174
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
if new_args.nil?
|
172
|
-
aggregate = new
|
173
|
-
else
|
174
|
-
aggregate = new(*new_args)
|
175
|
-
end
|
176
|
-
|
177
|
-
aggregate.send :aggregate_clear_current_event_version!
|
178
|
-
aggregate
|
175
|
+
def create_new_empty_aggregate
|
176
|
+
allocate
|
179
177
|
end
|
180
178
|
end
|
181
179
|
|
@@ -25,6 +25,20 @@ module Sandthorn
|
|
25
25
|
unless aggregate_snapshot
|
26
26
|
raise Errors::SnapshotError, "No snapshot has been created!"
|
27
27
|
end
|
28
|
+
@aggregate_snapshot[:event_data] = Sandthorn.serialize @aggregate_snapshot[:event_args]
|
29
|
+
@aggregate_snapshot[:event_args] = nil
|
30
|
+
Sandthorn.save_snapshot @aggregate_snapshot, @aggregate_id, self.class
|
31
|
+
@aggregate_snapshot = nil
|
32
|
+
end
|
33
|
+
private
|
34
|
+
def aggregate_create_event_when_extended
|
35
|
+
self.aggregate_snapshot!
|
36
|
+
vars = extract_relevant_aggregate_instance_variables
|
37
|
+
vars.each do |var_name|
|
38
|
+
value = instance_variable_get var_name
|
39
|
+
dump = Marshal.dump(value)
|
40
|
+
store_aggregate_instance_variable var_name, dump
|
41
|
+
end
|
28
42
|
|
29
43
|
@aggregate_snapshot[:event_data] = Sandthorn
|
30
44
|
.serialize aggregate_snapshot[:event_args]
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Sandthorn
|
4
|
+
class EventStores
|
5
|
+
extend Forwardable
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def_delegators :stores, :each
|
9
|
+
|
10
|
+
def initialize(stores = nil)
|
11
|
+
@store_map = Hash.new
|
12
|
+
add_initial(stores)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(name, event_store)
|
16
|
+
store_map[name] = event_store
|
17
|
+
end
|
18
|
+
alias_method :[]=, :add
|
19
|
+
|
20
|
+
def add_many(stores)
|
21
|
+
stores.each_pair do |name, store|
|
22
|
+
add(name, store)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def by_name(name)
|
27
|
+
store_map[name] || default_store
|
28
|
+
end
|
29
|
+
alias_method :[], :by_name
|
30
|
+
|
31
|
+
def default_store
|
32
|
+
store_map.fetch(:default)
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_store=(store)
|
36
|
+
store_map[:default] = store
|
37
|
+
end
|
38
|
+
|
39
|
+
def stores
|
40
|
+
store_map.values
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :store_map
|
46
|
+
|
47
|
+
def add_initial(store)
|
48
|
+
if is_event_store?(store)
|
49
|
+
self.default_store = store
|
50
|
+
elsif is_many_event_stores?(store)
|
51
|
+
add_many(store)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def is_many_event_stores?(store)
|
56
|
+
store.respond_to?(:each_pair)
|
57
|
+
end
|
58
|
+
|
59
|
+
def is_event_store?(store)
|
60
|
+
store.respond_to?(:get_events)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
data/lib/sandthorn/version.rb
CHANGED
data/sandthorn.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
23
23
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
25
|
spec.add_development_dependency "gem-release"
|
26
26
|
spec.add_development_dependency "pry"
|
27
27
|
spec.add_development_dependency "pry-doc"
|
data/spec/aggregate_root_spec.rb
CHANGED
@@ -38,6 +38,17 @@ module Sandthorn
|
|
38
38
|
|
39
39
|
end
|
40
40
|
|
41
|
+
describe "::event_store" do
|
42
|
+
let(:klass) { Class.new { include Sandthorn::AggregateRoot } }
|
43
|
+
it "is available as a class method" do
|
44
|
+
expect(klass).to respond_to(:event_store)
|
45
|
+
end
|
46
|
+
it "sets the event store as a class level variable and returns it" do
|
47
|
+
klass.event_store(:other)
|
48
|
+
expect(klass.event_store).to eq(:other)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
41
52
|
describe "when get all aggregates from DirtyClass" do
|
42
53
|
|
43
54
|
before(:each) do
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Sandthorn
|
4
|
+
describe EventStores do
|
5
|
+
let(:stores) { EventStores.new }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
context "when given a single event_store" do
|
9
|
+
it "sets it as the default event store" do
|
10
|
+
store = double(get_events: true)
|
11
|
+
allow(store).to receive(:get_events)
|
12
|
+
stores = EventStores.new(store)
|
13
|
+
expect(stores.default_store).to eq(store)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when given number of stores" do
|
18
|
+
it "adds them all" do
|
19
|
+
stores = {
|
20
|
+
default: double,
|
21
|
+
other: double
|
22
|
+
}
|
23
|
+
repo = EventStores.new(stores)
|
24
|
+
expect(repo.by_name(:default)).to eq(stores[:default])
|
25
|
+
expect(repo.by_name(:other)).to eq(stores[:other])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "enumerable" do
|
31
|
+
let(:store) { double }
|
32
|
+
let(:other_store) { double }
|
33
|
+
it "should respond to each" do
|
34
|
+
expect(stores).to respond_to(:each)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should yield each store" do
|
38
|
+
stores.add_many(
|
39
|
+
foo: store,
|
40
|
+
bar: other_store
|
41
|
+
)
|
42
|
+
expect { |block| stores.each(&block) }.to yield_successive_args(store, other_store)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#default_store=" do
|
47
|
+
it "sets the default" do
|
48
|
+
store = double
|
49
|
+
stores = EventStores.new
|
50
|
+
stores.default_store = store
|
51
|
+
expect(stores.default_store).to eq(store)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#by_name" do
|
56
|
+
context "when the store exists" do
|
57
|
+
it "returns the store" do
|
58
|
+
store = double
|
59
|
+
stores.add(:foo, store)
|
60
|
+
expect(stores.by_name(:foo)).to eq(store)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the store does not exist" do
|
65
|
+
it "returns the default store" do
|
66
|
+
store = double
|
67
|
+
stores.default_store = store
|
68
|
+
expect(stores.by_name(:unknown)).to eq(store)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#add" do
|
74
|
+
it "adds the store under the given name" do
|
75
|
+
store = double
|
76
|
+
stores.add(:foo, store)
|
77
|
+
expect(stores[:foo]).to eq(store)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/get_events_spec.rb
CHANGED
@@ -4,12 +4,70 @@ class AnAggregate
|
|
4
4
|
include Sandthorn::AggregateRoot
|
5
5
|
end
|
6
6
|
|
7
|
+
class AnotherAggregate
|
8
|
+
include Sandthorn::AggregateRoot
|
9
|
+
event_store :other
|
10
|
+
end
|
11
|
+
|
7
12
|
describe Sandthorn do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
describe "::get_events" do
|
15
|
+
context "when getting events using Sandthorn.get_events for an aggregate type" do
|
16
|
+
before do
|
17
|
+
AnAggregate.new.save
|
18
|
+
end
|
19
|
+
let(:events) { Sandthorn.get_events aggregate_types: [AnAggregate] }
|
20
|
+
it "should return events" do
|
21
|
+
expect(events).to_not be_empty
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when there are many event stores configured" do
|
26
|
+
before do
|
27
|
+
setup_secondary_db
|
28
|
+
end
|
29
|
+
|
30
|
+
let!(:agg) do
|
31
|
+
AnAggregate.new.save
|
32
|
+
end
|
33
|
+
|
34
|
+
let!(:other_agg) do
|
35
|
+
AnotherAggregate.new.save
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples(:default_event_store) do
|
39
|
+
it "returns events from the default event store" do
|
40
|
+
events = Sandthorn.get_events
|
41
|
+
expect(events).to all(have_aggregate_type("AnAggregate"))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when no explicit event store is used" do
|
46
|
+
it_behaves_like :default_event_store
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when given an explicit event store" do
|
50
|
+
context "and that event store exists" do
|
51
|
+
it "returns events from the chosen event store" do
|
52
|
+
events = Sandthorn.get_events(event_store: :other)
|
53
|
+
expect(events).to all(have_aggregate_type("AnotherAggregate"))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "and that event store does not exist" do
|
58
|
+
it_behaves_like :default_event_store
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_secondary_db
|
66
|
+
url = "sqlite://spec/db/other_db.sqlite3"
|
67
|
+
driver = SandthornDriverSequel.driver_from_url(url: url)
|
68
|
+
Sandthorn.event_stores.add(:other, driver)
|
69
|
+
migrator = SandthornDriverSequel::Migration.new url: url
|
70
|
+
SandthornDriverSequel.migrate_db url: url
|
71
|
+
migrator.send(:clear_for_test)
|
72
|
+
end
|
15
73
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class InitChange
|
4
|
+
include Sandthorn::AggregateRoot
|
5
|
+
attr_reader :foo
|
6
|
+
def initialize foo: nil
|
7
|
+
@foo = foo
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def change_init
|
11
|
+
InitChange.class_eval do
|
12
|
+
define_method :initialize, lambda { @foo = :foo }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
describe "when the initialize-method changes" do
|
16
|
+
it "should be possible to replay anyway" do
|
17
|
+
aggregate = InitChange.new foo: :bar
|
18
|
+
events = aggregate.aggregate_events
|
19
|
+
change_init
|
20
|
+
with_change = InitChange.new
|
21
|
+
expect(with_change.foo).to eql :foo
|
22
|
+
replayed = InitChange.aggregate_build(events)
|
23
|
+
expect(replayed.foo).to eql :bar
|
24
|
+
end
|
25
|
+
end
|
data/spec/sandthorn_spec.rb
CHANGED
@@ -2,14 +2,16 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
class AnAggregate
|
4
4
|
include Sandthorn::AggregateRoot
|
5
|
-
attr_accessor :test_block_count
|
6
|
-
def initialize
|
7
|
-
@test_block_count = false
|
8
|
-
end
|
9
5
|
def touch; touched; end
|
10
6
|
def touched; commit; end
|
11
7
|
end
|
12
8
|
|
9
|
+
module Outer
|
10
|
+
module Inner
|
11
|
+
class OtherAggregate < AnAggregate; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
13
15
|
describe Sandthorn do
|
14
16
|
before(:each) {
|
15
17
|
@aggregate = AnAggregate.new
|
@@ -17,24 +19,37 @@ describe Sandthorn do
|
|
17
19
|
@aggregate.save
|
18
20
|
}
|
19
21
|
|
20
|
-
|
22
|
+
describe "::obsolete_snapshot" do
|
21
23
|
it "retrieves a list of obsolete snapshots" do
|
22
24
|
obsolete_aggregates = Sandthorn.obsolete_snapshots type_names: [AnAggregate], min_event_distance: 0
|
23
25
|
expect(obsolete_aggregates).to_not be_empty
|
24
26
|
end
|
25
27
|
|
26
28
|
it "accepts a block that is applied to each aggregate" do
|
27
|
-
obsolete_aggregates = []
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
expect(obsolete_aggregates.all? { |aggregate| aggregate.test_block_count == true }).to be_truthy
|
29
|
+
obsolete_aggregates = Sandthorn.obsolete_snapshots type_names: [AnAggregate], min_event_distance: 0
|
30
|
+
expect do |block|
|
31
|
+
Sandthorn.obsolete_snapshots type_names: [AnAggregate], min_event_distance: 0, &block
|
32
|
+
end.to yield_successive_args(*obsolete_aggregates)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "only retrieves aggregates older than min_event_distance" do
|
36
36
|
obsolete_aggregates = Sandthorn.obsolete_snapshots type_names: [AnAggregate], min_event_distance: 10
|
37
37
|
expect(obsolete_aggregates).to be_empty
|
38
38
|
end
|
39
|
+
|
40
|
+
context "when the aggregate has been declared in a module" do
|
41
|
+
|
42
|
+
before do
|
43
|
+
Outer::Inner::OtherAggregate.new.tap do |agg|
|
44
|
+
agg.touch
|
45
|
+
agg.save
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "doesn't crash" do
|
50
|
+
obsolete_aggregates = Sandthorn.obsolete_snapshots type_names: [Outer::Inner::OtherAggregate], min_event_distance: 0
|
51
|
+
expect(obsolete_aggregates).to all(be_a_kind_of(Outer::Inner::OtherAggregate))
|
52
|
+
end
|
53
|
+
end
|
39
54
|
end
|
40
55
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,12 +5,13 @@
|
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
7
|
require 'coveralls'
|
8
|
+
Coveralls.wear!
|
8
9
|
require "ap"
|
9
10
|
require "bundler"
|
10
11
|
require "sandthorn_driver_sequel"
|
12
|
+
require "support/custom_matchers"
|
11
13
|
|
12
14
|
Bundler.require
|
13
|
-
Coveralls.wear!
|
14
15
|
|
15
16
|
module Helpers
|
16
17
|
def class_including(mod)
|
@@ -36,8 +37,10 @@ def spec_db
|
|
36
37
|
end
|
37
38
|
def sqlite_store_setup
|
38
39
|
url = spec_db
|
39
|
-
|
40
|
-
Sandthorn.
|
40
|
+
driver = SandthornDriverSequel.driver_from_url(url: url)
|
41
|
+
Sandthorn.configure do |c|
|
42
|
+
c.event_store = driver
|
43
|
+
end
|
41
44
|
migrator = SandthornDriverSequel::Migration.new url: url
|
42
45
|
SandthornDriverSequel.migrate_db url: url
|
43
46
|
migrator.send(:clear_for_test)
|
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Krantz
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-03-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -43,16 +43,16 @@ dependencies:
|
|
43
43
|
name: rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0'
|
48
|
+
version: '3.0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - "
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
55
|
+
version: '3.0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: gem-release
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,6 +193,7 @@ files:
|
|
193
193
|
- lib/sandthorn/aggregate_root_snapshot.rb
|
194
194
|
- lib/sandthorn/errors.rb
|
195
195
|
- lib/sandthorn/event_inspector.rb
|
196
|
+
- lib/sandthorn/event_stores.rb
|
196
197
|
- lib/sandthorn/version.rb
|
197
198
|
- sandthorn.gemspec
|
198
199
|
- spec/aggregate_delta_spec.rb
|
@@ -203,9 +204,12 @@ files:
|
|
203
204
|
- spec/db/sequel_driver.sqlite3_old
|
204
205
|
- spec/different_driver_spec.rb
|
205
206
|
- spec/event_inspector_spec.rb
|
207
|
+
- spec/event_stores_spec.rb
|
206
208
|
- spec/get_events_spec.rb
|
209
|
+
- spec/initialize_signature_change_spec.rb
|
207
210
|
- spec/sandthorn_spec.rb
|
208
211
|
- spec/spec_helper.rb
|
212
|
+
- spec/support/custom_matchers.rb
|
209
213
|
- spec/tracing_spec.rb
|
210
214
|
homepage: ''
|
211
215
|
licenses:
|
@@ -227,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
231
|
version: '0'
|
228
232
|
requirements: []
|
229
233
|
rubyforge_project:
|
230
|
-
rubygems_version: 2.
|
234
|
+
rubygems_version: 2.4.3
|
231
235
|
signing_key:
|
232
236
|
specification_version: 4
|
233
237
|
summary: Event sourcing gem
|
@@ -240,8 +244,11 @@ test_files:
|
|
240
244
|
- spec/db/sequel_driver.sqlite3_old
|
241
245
|
- spec/different_driver_spec.rb
|
242
246
|
- spec/event_inspector_spec.rb
|
247
|
+
- spec/event_stores_spec.rb
|
243
248
|
- spec/get_events_spec.rb
|
249
|
+
- spec/initialize_signature_change_spec.rb
|
244
250
|
- spec/sandthorn_spec.rb
|
245
251
|
- spec/spec_helper.rb
|
252
|
+
- spec/support/custom_matchers.rb
|
246
253
|
- spec/tracing_spec.rb
|
247
254
|
has_rdoc:
|