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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56a515dc52707cf7d8ad0a5442a6bbd50288c0d7
4
- data.tar.gz: 5791edaf2c3fc2d42c5f3bffc014a67e372d41fb
3
+ metadata.gz: da47c343082dee8f07abfc0c3dd8a0f57418b4cd
4
+ data.tar.gz: 549266a31901d09c77a34839d6f5d8ddf8708364
5
5
  SHA512:
6
- metadata.gz: cac64af74658627325f5cb1aef22e457a96e8712d25dfcebd700ae4a9aca2c108abbf5c6f2ebc76c657370244b35af632b72c27c772854da8c9c8dde8c7fc822
7
- data.tar.gz: f8cd018700bc09fe40590d8b06f30bdbf80e8c16fc996106a9e9b2cd88a06877aff938a120f7aee1f25cf7c8d350de894a16f586da1045044f0e14ff6b8a3475
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.select { |e| !e.nil? }
130
- end
146
+ end
131
147
 
148
+ loaded_events.compact
149
+ end
132
150
  end
133
151
  end
@@ -1,3 +1,3 @@
1
1
  module EntityStore
2
- VERSION = "0.2.13".freeze
2
+ VERSION = "0.2.14".freeze
3
3
  end
@@ -1,99 +1,202 @@
1
1
  require 'spec_helper'
2
2
 
3
- module MongoEntityStoreSpec
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
- describe MongoEntityStore do
13
- before(:each) do
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
- @store = MongoEntityStore.new
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
- before(:each) do
20
- @id = random_object_id
21
- @attrs = {
22
- '_type' => MongoEntityStoreSpec::DummyEntity.name,
23
- 'version' => @version = random_integer
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
- it "should construct a new entity" do
42
- MongoEntityStoreSpec::DummyEntity.should_receive(:new).with({'id' => @id})
43
- subject
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
- it "should return the entity" do
46
- subject.should eq(@entity)
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(:each) do
50
- @attrs['snapshot'] = {
51
- 'version' => @snapshot_version = random_integer,
52
- 'name' => @name = random_string
53
- }
119
+ before do
120
+ saved_entity.version = 10
121
+ store.snapshot_entity(saved_entity)
54
122
  end
55
- it "should construct a new entity with from the snapshot" do
56
- MongoEntityStoreSpec::DummyEntity.should_receive(:new).with(@attrs['snapshot'])
57
- subject
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
- subject { @store.get_entity!(random_string) }
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
- subject { @store.get_entity!(random_object_id) }
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
- before(:each) do
85
- @entity = MongoEntityStoreSpec::DummyEntity.new(:id => random_object_id, :version => random_integer, :name => random_string)
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
- subject { @store.snapshot_entity(@entity) }
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
- saved_entity = @store.entities.find_one({'_id' => BSON::ObjectId.from_string(@entity.id)})['snapshot']
93
- saved_entity['id'].should eq(@entity.id)
94
- saved_entity['version'].should eq(@entity.version)
95
- saved_entity['name'].should eq(@entity.name)
96
- saved_entity['description'].should eq(@entity.description)
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
@@ -35,5 +35,5 @@ def random_time
35
35
  end
36
36
 
37
37
  def random_object_id
38
- BSON::ObjectId.from_time(random_time).to_s
39
- end
38
+ BSON::ObjectId.from_time(random_time, :unique => true).to_s
39
+ end
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.13
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-25 00:00:00.000000000 Z
11
+ date: 2014-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongo