entity_store 0.2.13 → 0.2.14
Sign up to get free protection for your applications and to get access to all the features.
- 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
|