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