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 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