entity_store 0.2.13 → 0.2.14
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/lib/entity_store/mongo_entity_store.rb +29 -11
- data/lib/entity_store/version.rb +1 -1
- data/spec/entity_store/mongo_entity_store_spec.rb +160 -57
- data/spec/spec_helper.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da47c343082dee8f07abfc0c3dd8a0f57418b4cd
|
4
|
+
data.tar.gz: 549266a31901d09c77a34839d6f5d8ddf8708364
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b7a028a7f73a67ef28a09ccdf009e8ba8bcd02f34c8c31832c26d81a9ebbc8323dffe93854e06178b1851423c77f94e00654a489f8fa8af76883ce6cd90275d
|
7
|
+
data.tar.gz: 4317c2c625b7ffb233f1b6e1c6d7f04a9170c0a94d2245c6270e51afcd3887357dd0798679671662200d36ec223af92c63692d102d6022e478f0b0b6c6616307
|
@@ -52,23 +52,32 @@ module EntityStore
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# Public - create a snapshot of the entity and store in the entities collection
|
55
|
-
#
|
55
|
+
#
|
56
56
|
def snapshot_entity(entity)
|
57
57
|
query = {'_id' => BSON::ObjectId.from_string(entity.id)}
|
58
58
|
updates = { '$set' => { 'snapshot' => entity.attributes } }
|
59
|
+
|
60
|
+
if entity.class.respond_to? :entity_store_snapshot_key
|
61
|
+
# If there is a snapshot key, store it too
|
62
|
+
updates['$set']['snapshot_key'] = entity.class.entity_store_snapshot_key
|
63
|
+
else
|
64
|
+
# Otherwise, make sure there isn't one set
|
65
|
+
updates['$unset'] = { 'snapshot_key' => '' }
|
66
|
+
end
|
67
|
+
|
59
68
|
entities.update(query, updates, { :upsert => true} )
|
60
69
|
end
|
61
70
|
|
62
71
|
# Public - remove the snapshot for an entity
|
63
|
-
#
|
72
|
+
#
|
64
73
|
def remove_entity_snapshot(id)
|
65
74
|
entities.update({'_id' => BSON::ObjectId.from_string(id)}, { '$unset' => { 'snapshot' => 1}})
|
66
75
|
end
|
67
76
|
|
68
77
|
# Public - remove all snapshots
|
69
|
-
#
|
78
|
+
#
|
70
79
|
# type - String optional class name for the entity
|
71
|
-
#
|
80
|
+
#
|
72
81
|
def remove_snapshots type=nil
|
73
82
|
query = {}
|
74
83
|
query['_type'] = type if type
|
@@ -85,21 +94,29 @@ module EntityStore
|
|
85
94
|
|
86
95
|
# Public - loads the entity from the store, including any available snapshots
|
87
96
|
# then loads the events to complete the state
|
88
|
-
#
|
97
|
+
#
|
89
98
|
# id - String representation of BSON::ObjectId
|
90
99
|
# raise_exception - Boolean indicating whether to raise an exception if not found (default=false)
|
91
|
-
#
|
100
|
+
#
|
92
101
|
# Returns an object of the entity type
|
93
102
|
def get_entity(id, raise_exception=false)
|
94
103
|
if attrs = entities.find_one('_id' => BSON::ObjectId.from_string(id))
|
95
104
|
begin
|
96
105
|
entity_type = EntityStore::Config.load_type(attrs['_type'])
|
106
|
+
|
107
|
+
# Check if there is a snapshot key in use
|
108
|
+
if entity_type.respond_to? :entity_store_snapshot_key
|
109
|
+
active_key = entity_type.entity_store_snapshot_key
|
110
|
+
# Discard the snapshot if the keys don't match
|
111
|
+
attrs.delete('snapshot') unless active_key == attrs['snapshot_key']
|
112
|
+
end
|
113
|
+
|
97
114
|
entity = entity_type.new(attrs['snapshot'] || {'id' => id })
|
98
115
|
rescue => e
|
99
116
|
log_error "Error loading type #{attrs['_type']}", e
|
100
117
|
raise
|
101
118
|
end
|
102
|
-
|
119
|
+
|
103
120
|
entity
|
104
121
|
else
|
105
122
|
raise NotFound.new(id) if raise_exception
|
@@ -111,23 +128,24 @@ module EntityStore
|
|
111
128
|
end
|
112
129
|
|
113
130
|
def get_events(id, since_version=nil)
|
114
|
-
|
115
131
|
query = { '_entity_id' => BSON::ObjectId.from_string(id) }
|
132
|
+
|
116
133
|
query['entity_version'] = { '$gt' => since_version } if since_version
|
117
134
|
|
118
135
|
options = {
|
119
136
|
:sort => [['entity_version', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]]
|
120
137
|
}
|
121
138
|
|
122
|
-
events.find(query, options).collect do |attrs|
|
139
|
+
loaded_events = events.find(query, options).collect do |attrs|
|
123
140
|
begin
|
124
141
|
EntityStore::Config.load_type(attrs['_type']).new(attrs)
|
125
142
|
rescue => e
|
126
143
|
log_error "Error loading type #{attrs['_type']}", e
|
127
144
|
nil
|
128
145
|
end
|
129
|
-
end
|
130
|
-
end
|
146
|
+
end
|
131
147
|
|
148
|
+
loaded_events.compact
|
149
|
+
end
|
132
150
|
end
|
133
151
|
end
|
data/lib/entity_store/version.rb
CHANGED
@@ -1,99 +1,202 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
describe MongoEntityStore do
|
4
4
|
class DummyEntity
|
5
5
|
include EntityStore::Entity
|
6
6
|
|
7
7
|
attr_accessor :name, :description
|
8
8
|
|
9
|
+
def set_name(new_name)
|
10
|
+
record_event DummyEntityNameSet.new(name: new_name)
|
11
|
+
end
|
9
12
|
end
|
10
|
-
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
class DummyEntityWithSnapshotKey < DummyEntity
|
15
|
+
def self.entity_store_snapshot_key
|
16
|
+
@entity_store_snapshot_key ||= 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.increment_entity_store_snapshot_key!
|
20
|
+
@entity_store_snapshot_key = entity_store_snapshot_key + 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class DummyEntityNameSet
|
25
|
+
include EntityStore::Event
|
26
|
+
|
27
|
+
attr_accessor :name
|
28
|
+
|
29
|
+
def apply(entity)
|
30
|
+
entity.name = self.name
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
# Crude check relying on inspect, ok for tests
|
35
|
+
self.inspect == other.inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:store) do
|
14
40
|
MongoEntityStore.connection_profile = "mongodb://localhost/entity_store_default"
|
15
|
-
|
41
|
+
MongoEntityStore.new
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "event storage" do
|
45
|
+
let(:entity_id) { random_object_id }
|
46
|
+
|
47
|
+
let(:first_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 1, :name => random_string) }
|
48
|
+
let(:second_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 2, :name => random_string) }
|
49
|
+
let(:third_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 3, :name => random_string) }
|
50
|
+
let(:unrelated_event) { DummyEntityNameSet.new(:entity_id => random_object_id, :entity_version => 4, :name => random_string) }
|
51
|
+
|
52
|
+
before do
|
53
|
+
store.add_event(second_event)
|
54
|
+
store.add_event(unrelated_event)
|
55
|
+
store.add_event(first_event)
|
56
|
+
store.add_event(third_event)
|
57
|
+
end
|
58
|
+
|
59
|
+
subject { store.get_events(event_entity_id, since_version) }
|
60
|
+
|
61
|
+
context "all events" do
|
62
|
+
let(:event_entity_id) { entity_id }
|
63
|
+
let(:since_version) { 0 }
|
64
|
+
|
65
|
+
it "should return the three events in order" do
|
66
|
+
subject.should == [first_event, second_event, third_event]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "subset of events" do
|
71
|
+
let(:event_entity_id) { entity_id }
|
72
|
+
let(:since_version) { second_event.entity_version }
|
73
|
+
|
74
|
+
it "should only include events greater than the given version" do
|
75
|
+
subject.should == [ third_event ]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "no events" do
|
80
|
+
let(:event_entity_id) { random_object_id }
|
81
|
+
let(:since_version) { 0 }
|
82
|
+
|
83
|
+
it "should return an empty array" do
|
84
|
+
subject.should be_empty
|
85
|
+
end
|
86
|
+
end
|
16
87
|
end
|
17
88
|
|
18
89
|
describe "#get_entity" do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@entity = MongoEntityStoreSpec::DummyEntity.new
|
26
|
-
MongoEntityStoreSpec::DummyEntity.stub(:new) { @entity }
|
27
|
-
@entities_collection = double('MongoCollection', :find_one => @attrs)
|
28
|
-
@store.stub(:entities) { @entities_collection }
|
29
|
-
@events = [
|
30
|
-
double('Event', :apply => true, :entity_version => random_integer), double('Event', :apply => true, :entity_version => random_integer)
|
31
|
-
]
|
32
|
-
@store.stub(:get_events) { @events }
|
33
|
-
end
|
34
|
-
|
35
|
-
subject { @store.get_entity(@id) }
|
36
|
-
|
37
|
-
it "should attempt to retrieve the entity record from the store" do
|
38
|
-
@entities_collection.should_receive(:find_one).with({'_id' => BSON::ObjectId.from_string(@id)})
|
39
|
-
subject
|
90
|
+
let(:entity_class) { DummyEntity }
|
91
|
+
|
92
|
+
let(:saved_entity) do
|
93
|
+
entity = entity_class.new(:name => random_string, :description => random_string)
|
94
|
+
entity.id = store.add_entity(entity)
|
95
|
+
entity
|
40
96
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
97
|
+
|
98
|
+
subject { store.get_entity(saved_entity.id) }
|
99
|
+
|
100
|
+
it "should retrieve an entity from the store with the same ID" do
|
101
|
+
subject.id.should == saved_entity.id
|
44
102
|
end
|
45
|
-
|
46
|
-
|
103
|
+
|
104
|
+
it "should retrieve an entity from the store with the same class" do
|
105
|
+
subject.class.should == saved_entity.class
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should have the same version" do
|
109
|
+
subject.version.should == saved_entity.version
|
47
110
|
end
|
111
|
+
|
112
|
+
context "when a snapshot does not exist" do
|
113
|
+
it "should not have set the name" do
|
114
|
+
subject.name.should be_nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
48
118
|
context "when a snapshot exists" do
|
49
|
-
before
|
50
|
-
|
51
|
-
|
52
|
-
'name' => @name = random_string
|
53
|
-
}
|
119
|
+
before do
|
120
|
+
saved_entity.version = 10
|
121
|
+
store.snapshot_entity(saved_entity)
|
54
122
|
end
|
55
|
-
|
56
|
-
|
57
|
-
|
123
|
+
|
124
|
+
context "when a snapshot key not in use" do
|
125
|
+
it "should have set the name" do
|
126
|
+
subject.name.should == saved_entity.name
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when a snapshot key is in use" do
|
131
|
+
let(:entity_class) { DummyEntityWithSnapshotKey }
|
132
|
+
|
133
|
+
context "when the key matches the class's key" do
|
134
|
+
it "should have set the name" do
|
135
|
+
subject.name.should == saved_entity.name
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when the key does not match the class's key" do
|
140
|
+
before do
|
141
|
+
entity_class.increment_entity_store_snapshot_key!
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should ignore the invalidated snapshot" do
|
145
|
+
subject.name.should be_nil
|
146
|
+
end
|
147
|
+
end
|
58
148
|
end
|
59
149
|
end
|
60
150
|
end
|
61
151
|
|
62
152
|
describe "#get_entity!" do
|
63
|
-
|
64
153
|
context "when invalid id format passed" do
|
65
|
-
|
66
|
-
|
67
|
-
|
154
|
+
subject { store.get_entity!(random_string) }
|
155
|
+
|
68
156
|
it "should raise not found" do
|
69
157
|
expect { subject }.to raise_error(NotFound)
|
70
158
|
end
|
71
159
|
end
|
160
|
+
|
72
161
|
context "when valid id format passed but no object exists" do
|
73
|
-
|
74
|
-
|
75
|
-
|
162
|
+
subject { store.get_entity!(random_object_id) }
|
163
|
+
|
76
164
|
it "should raise not found" do
|
77
165
|
expect { subject }.to raise_error(NotFound)
|
78
166
|
end
|
79
167
|
end
|
80
|
-
|
81
168
|
end
|
82
|
-
|
169
|
+
|
83
170
|
describe "#snapshot_entity" do
|
84
|
-
|
85
|
-
|
171
|
+
let(:entity_class) { DummyEntity }
|
172
|
+
|
173
|
+
let(:entity) do
|
174
|
+
entity_class.new(:id => random_object_id, :version => random_integer, :name => random_string)
|
86
175
|
end
|
87
176
|
|
88
|
-
|
177
|
+
let(:saved_entity) do
|
178
|
+
store.entities.find_one({'_id' => BSON::ObjectId.from_string(entity.id)})
|
179
|
+
end
|
180
|
+
|
181
|
+
subject { store.snapshot_entity(entity) }
|
89
182
|
|
90
183
|
it "should add a snaphot to the entity record" do
|
91
|
-
subject
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
184
|
+
subject
|
185
|
+
snapshot = saved_entity['snapshot']
|
186
|
+
|
187
|
+
snapshot['id'].should eq(entity.id)
|
188
|
+
snapshot['version'].should eq(entity.version)
|
189
|
+
snapshot['name'].should eq(entity.name)
|
190
|
+
snapshot['description'].should eq(entity.description)
|
191
|
+
end
|
192
|
+
|
193
|
+
context "entity with snapshot key" do
|
194
|
+
let(:entity_class) { DummyEntityWithSnapshotKey }
|
195
|
+
|
196
|
+
it "should store the snapshot key" do
|
197
|
+
subject
|
198
|
+
saved_entity['snapshot_key'].should == entity.class.entity_store_snapshot_key
|
199
|
+
end
|
97
200
|
end
|
98
201
|
end
|
99
202
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: entity_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Bird
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03
|
11
|
+
date: 2014-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongo
|