entity_store 0.6.0 → 1.0.0

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: 2fd6deacb63e070d4f7052598ccc5e79679d1052
4
- data.tar.gz: 4c3fcfb0cae2e37aaa699c39a3730dda39d9f217
3
+ metadata.gz: c1bf9f57ff36a9e94b582cec4dd38d5364536455
4
+ data.tar.gz: 27b4160e5a19ee2e315a8a4f526e7bfd46f0c39b
5
5
  SHA512:
6
- metadata.gz: 676503735ad66f25dd174831c840ac9ec2d42616783aeaed0c8ba37fe50c33c00cf1e227284d84d4e08e76de9280c0f7b094e990dbe445f7937e1975545de288
7
- data.tar.gz: 00516945f8fd853b07c982c823da8e911d8fee23630ba2f0ab68d751f36c2f277225722ed1055b8e849eea3969b32d1bfe3d1642e90f9cf4b4d891571760e238
6
+ metadata.gz: 763f6f18705899dcf36b63b526a7dd97dd40896409401a2405a1609c3ea4f026c344c7f0e8d66af5d8e1f77c5558dc98da044346b598064c02233e3c01e5fbae
7
+ data.tar.gz: cb1c725e9c7befe55fe237510bc20a37ab4752c375e7395373489ddfd8064f28b31de00c9752c8c73f58a2bcea2e68b5d6071dc6a6dc6b453dd362da4929aaaf
@@ -57,6 +57,10 @@ module EntityStore
57
57
  storage_client.remove_snapshots type
58
58
  end
59
59
 
60
+ def clear_entity_events(id, excluded_types = [])
61
+ storage_client.clear_entity_events(id, excluded_types)
62
+ end
63
+
60
64
  def add_events(entity)
61
65
  items = entity.pending_events.map do |event|
62
66
  event.entity_id = entity.id.to_s
@@ -1,3 +1,3 @@
1
1
  module EntityStore
2
- VERSION = "0.6.0".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
data/lib/entity_store.rb CHANGED
@@ -14,9 +14,6 @@ module EntityStore
14
14
  require_relative 'entity_store/event_bus'
15
15
  require_relative 'entity_store/not_found'
16
16
 
17
- require_relative 'entity_store/mongo_entity_store'
18
- require_relative 'entity_store/external_store'
19
-
20
17
  class << self
21
18
  def setup
22
19
  yield EntityStore::Config.setup
@@ -23,6 +23,9 @@ class DummyAllSubscriber
23
23
  end
24
24
  end
25
25
 
26
+ class DummyExternalStore
27
+ end
28
+
26
29
  describe EventBus do
27
30
  before(:each) do
28
31
  @entity_type = random_string
@@ -68,7 +71,7 @@ describe EventBus do
68
71
 
69
72
  describe ".publish_to_feed" do
70
73
  before(:each) do
71
- @feed_store = double(ExternalStore)
74
+ @feed_store = double(DummyExternalStore)
72
75
  @event_bus.stub(:feed_store) { @feed_store }
73
76
  end
74
77
 
@@ -87,7 +90,7 @@ describe EventBus do
87
90
  @subscriber = double("Subscriber", :dummy_event => true)
88
91
  DummySubscriber.stub(:new) { @subscriber }
89
92
 
90
- @feed_store = double(ExternalStore)
93
+ @feed_store = double(DummyExternalStore)
91
94
  @id = random_object_id
92
95
  @feed_store.stub(:get_events) { |since| since == @id ? [] : [
93
96
  EventDataObject.new('_id' => @id, '_type' => DummyEvent.name, 'name' => random_string)
@@ -30,12 +30,60 @@ class DummyEntitySubscriber
30
30
  end
31
31
  end
32
32
 
33
+ class DummyStore
34
+ def open
35
+ end
36
+
37
+ def entities
38
+ @entities ||= {}
39
+ end
40
+
41
+ def events
42
+ @events ||= {}
43
+ end
44
+
45
+ def add_entity(entity, id = BSON::ObjectId.new)
46
+ entities[id] = entity
47
+ id.to_s
48
+ end
49
+
50
+ def add_events(items)
51
+ items.each do |item|
52
+ events[item.entity_id] ||= []
53
+ events[item.entity_id] << item
54
+ end
55
+ end
56
+
57
+ def get_entities(ids, options={})
58
+ result = []
59
+ ids.each do |id|
60
+ if entity = entities[BSON::ObjectId.from_string(id)]
61
+ result << entity
62
+ end
63
+ end
64
+
65
+ result
66
+ end
67
+
68
+ def get_events(attrs)
69
+ result = {}
70
+
71
+ attrs.each do |attr|
72
+ result[attr[:id]] = events[attr[:id]]
73
+ end
74
+
75
+ result
76
+ end
77
+
78
+ def save_entity(entity)
79
+ entities[entity.id] = entity
80
+ end
81
+ end
82
+
33
83
  describe "end to end" do
34
84
  before(:each) do
35
- MongoEntityStore.connection_profile = "mongodb://localhost/entity_store_test"
36
-
37
85
  EntityStore::Config.setup do |config|
38
- config.store = MongoEntityStore.new
86
+ config.store = DummyStore.new
39
87
  config.event_subscribers << DummyEntitySubscriber
40
88
  end
41
89
  end
@@ -55,4 +103,4 @@ describe "end to end" do
55
103
  EntityStore::Store.new.get(@entity.id).name.should eq(name)
56
104
  end
57
105
  end
58
- end
106
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rspec'
3
- require 'mongo'
4
3
  require 'hatchet'
4
+ require 'bson'
5
5
 
6
6
  require "#{Rake.application.original_dir}/lib/entity_store"
7
7
 
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: entity_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Bird
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-09 00:00:00.000000000 Z
11
+ date: 2018-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: mongo
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.8'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.8'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bson
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -66,10 +52,8 @@ files:
66
52
  - lib/entity_store/event.rb
67
53
  - lib/entity_store/event_bus.rb
68
54
  - lib/entity_store/event_data_object.rb
69
- - lib/entity_store/external_store.rb
70
55
  - lib/entity_store/hash_serialization.rb
71
56
  - lib/entity_store/logging.rb
72
- - lib/entity_store/mongo_entity_store.rb
73
57
  - lib/entity_store/not_found.rb
74
58
  - lib/entity_store/store.rb
75
59
  - lib/entity_store/time_factory.rb
@@ -81,8 +65,6 @@ files:
81
65
  - spec/entity_store/entity_value_spec.rb
82
66
  - spec/entity_store/event_bus_spec.rb
83
67
  - spec/entity_store/event_spec.rb
84
- - spec/entity_store/external_store_spec.rb
85
- - spec/entity_store/mongo_entity_store_spec.rb
86
68
  - spec/entity_store/store_spec.rb
87
69
  - spec/entity_store_spec.rb
88
70
  - spec/spec_helper.rb
@@ -106,18 +88,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
88
  version: '0'
107
89
  requirements: []
108
90
  rubyforge_project:
109
- rubygems_version: 2.6.6
91
+ rubygems_version: 2.4.5.1
110
92
  signing_key:
111
93
  specification_version: 4
112
94
  summary: Event sourced entity store with a replaceable body
113
95
  test_files:
96
+ - spec/spec_helper.rb
97
+ - spec/entity_store_spec.rb
98
+ - spec/entity_store/entity_value_spec.rb
114
99
  - spec/entity_store/config_spec.rb
115
100
  - spec/entity_store/entity_spec.rb
116
- - spec/entity_store/entity_value_spec.rb
117
- - spec/entity_store/event_bus_spec.rb
118
101
  - spec/entity_store/event_spec.rb
119
- - spec/entity_store/external_store_spec.rb
120
- - spec/entity_store/mongo_entity_store_spec.rb
102
+ - spec/entity_store/event_bus_spec.rb
121
103
  - spec/entity_store/store_spec.rb
122
- - spec/entity_store_spec.rb
123
- - spec/spec_helper.rb
@@ -1,62 +0,0 @@
1
- require 'mongo'
2
- require 'uri'
3
-
4
- module EntityStore
5
- class ExternalStore
6
- include Mongo
7
-
8
- class << self
9
- attr_accessor :connection_profile
10
- attr_writer :connect_timeout
11
-
12
- def connection
13
- @_connection ||= Mongo::MongoClient.from_uri(ExternalStore.connection_profile, :connect_timeout => EntityStore::Config.connect_timeout)
14
- end
15
-
16
- def database
17
- @_database ||= ExternalStore.connection_profile.split('/').last
18
- end
19
- end
20
-
21
- def open
22
- ExternalStore.connection.db(ExternalStore.database)
23
- end
24
-
25
- def collection
26
- @_collection ||= open['events']
27
- end
28
-
29
- def ensure_indexes
30
- collection.ensure_index([['_type', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]])
31
- end
32
-
33
- def add_event(entity_type, event)
34
- collection.insert({
35
- '_entity_type' => entity_type, '_type' => event.class.name
36
- }.merge(event.attributes)
37
- )
38
- end
39
-
40
- # Public - get events since a Time or ID
41
- #
42
- # since - Time or String id to filter events from
43
- # type - String optionally filter the event type to return (default=nil)
44
- # max_items - Fixnum max items to return (default=100)
45
- #
46
- # Returns Enumerable EventDataObject
47
- def get_events(since, type=nil, max_items=100)
48
- since_id = since.is_a?(Time) ? BSON::ObjectId.from_time(since) : BSON::ObjectId.from_string(since)
49
-
50
- query = { '_id' => { '$gt' => since_id } }
51
- query['_type'] = type if type
52
-
53
- options = {
54
- :sort => [['_id', Mongo::ASCENDING]],
55
- :limit => max_items
56
- }
57
-
58
- collection.find(query, options).collect { |e| EventDataObject.new(e)}
59
- end
60
-
61
- end
62
- end
@@ -1,196 +0,0 @@
1
- require 'mongo'
2
- require 'uri'
3
-
4
- module EntityStore
5
- class MongoEntityStore
6
- include Mongo
7
- include Logging
8
-
9
- class << self
10
- attr_accessor :connection_profile
11
- attr_writer :connect_timeout
12
-
13
- def connection
14
- @_connection ||= Mongo::MongoClient.from_uri(MongoEntityStore.connection_profile, :connect_timeout => EntityStore::Config.connect_timeout, :refresh_mode => :sync)
15
- end
16
-
17
- def database
18
- @_database ||= MongoEntityStore.connection_profile.split('/').last.split('?').first
19
- end
20
- end
21
-
22
- def open
23
- MongoEntityStore.connection.db(MongoEntityStore.database)
24
- end
25
-
26
- def entities
27
- @entities_collection ||= open['entities']
28
- end
29
-
30
- def events
31
- @events_collection ||= open['entity_events']
32
- end
33
-
34
- def clear
35
- entities.drop
36
- @entities_collection = nil
37
- events.drop
38
- @events_collection = nil
39
- end
40
-
41
- def ensure_indexes
42
- events.ensure_index([['_entity_id', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]])
43
- events.ensure_index([['_entity_id', Mongo::ASCENDING], ['entity_version', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]])
44
- end
45
-
46
- def add_entity(entity, id = BSON::ObjectId.new)
47
- entities.insert('_id' => id, '_type' => entity.class.name, 'version' => entity.version).to_s
48
- end
49
-
50
- def save_entity(entity)
51
- entities.update({'_id' => BSON::ObjectId.from_string(entity.id)}, { '$set' => { 'version' => entity.version } })
52
- end
53
-
54
- # Public: create a snapshot of the entity and store in the entities collection
55
- #
56
- def snapshot_entity(entity)
57
- query = {'_id' => BSON::ObjectId.from_string(entity.id)}
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
-
68
- entities.update(query, updates, { :upsert => true} )
69
- end
70
-
71
- # Public - remove the snapshot for an entity
72
- #
73
- def remove_entity_snapshot(id)
74
- entities.update({'_id' => BSON::ObjectId.from_string(id)}, { '$unset' => { 'snapshot' => 1}})
75
- end
76
-
77
- # Public: remove all snapshots
78
- #
79
- # type - String optional class name for the entity
80
- #
81
- def remove_snapshots type=nil
82
- query = {}
83
- query['_type'] = type if type
84
- entities.update(query, { '$unset' => { 'snapshot' => 1 } }, { multi: true })
85
- end
86
-
87
- def add_events(items)
88
- events_with_id = items.map { |e| [ BSON::ObjectId.new, e ] }
89
- add_events_with_ids(events_with_id)
90
- end
91
-
92
- def add_events_with_ids(event_id_map)
93
- docs = event_id_map.map do |id, event|
94
- {
95
- '_id' => id,
96
- '_type' => event.class.name,
97
- '_entity_id' => BSON::ObjectId.from_string(event.entity_id)
98
- }.merge(event.attributes)
99
- end
100
-
101
- events.insert(docs)
102
- end
103
-
104
- # Public: loads the entity from the store, including any available snapshots
105
- # then loads the events to complete the state
106
- #
107
- # ids - Array of Strings representation of BSON::ObjectId
108
- # options - Hash of options (default: {})
109
- # :raise_exception - Boolean (default: true)
110
- #
111
- # Returns an array of entities
112
- def get_entities(ids, options={})
113
-
114
- object_ids = ids.map do |id|
115
- begin
116
- BSON::ObjectId.from_string(id)
117
- rescue BSON::InvalidObjectId
118
- raise NotFound.new(id) if options.fetch(:raise_exception, true)
119
- nil
120
- end
121
- end
122
-
123
- entities.find('_id' => { '$in' => object_ids }).map do |attrs|
124
- begin
125
- entity_type = EntityStore::Config.load_type(attrs['_type'])
126
-
127
- # Check if there is a snapshot key in use
128
- if entity_type.respond_to? :entity_store_snapshot_key
129
- active_key = entity_type.entity_store_snapshot_key
130
- # Discard the snapshot if the keys don't match
131
- attrs.delete('snapshot') unless active_key == attrs['snapshot_key']
132
- end
133
-
134
- entity = entity_type.new(attrs['snapshot'] || {'id' => attrs['_id'].to_s })
135
- rescue => e
136
- log_error "Error loading type #{attrs['_type']}", e
137
- raise
138
- end
139
-
140
- entity
141
- end
142
-
143
- end
144
-
145
- # Public: get events for an array of criteria objects
146
- # because each entity could have a different reference
147
- # version this allows optional criteria to be specifed
148
- #
149
- #
150
- # criteria - Hash :id mandatory, :since_version optional
151
- #
152
- # Examples
153
- #
154
- # get_events_for_criteria([ { id: "23232323"}, { id: "2398429834", since_version: 4 } ] )
155
- #
156
- # Returns Hash with id as key and Array of Event instances as value
157
- def get_events(criteria)
158
- return {} if criteria.empty?
159
-
160
- query_items = criteria.map do |item|
161
- raise ArgumentError.new(":id missing from criteria") unless item[:id]
162
- item_query = { '_entity_id' => BSON::ObjectId.from_string(item[:id]) }
163
- item_query['entity_version'] = { '$gt' => item[:since_version] } if item[:since_version]
164
- item_query
165
- end
166
-
167
- query = { '$or' => query_items }
168
-
169
- result = Hash[ criteria.map { |item| [ item[:id], [] ] } ]
170
-
171
- events.find(query).each do |attrs|
172
- result[attrs['_entity_id'].to_s] << attrs
173
- end
174
-
175
- result.each do |id, events|
176
- # Have to do the sort client side as otherwise the query will not use
177
- # indexes (http://docs.mongodb.org/manual/reference/operator/query/or/#or-and-sort-operations)
178
- events.sort_by! { |attrs| [attrs['entity_version'], attrs['_id'].to_s] }
179
-
180
- # Convert the attributes into event objects
181
- events.map! do |attrs|
182
- begin
183
- EntityStore::Config.load_type(attrs['_type']).new(attrs)
184
- rescue => e
185
- log_error "Error loading type #{attrs['_type']}", e
186
- nil
187
- end
188
- end
189
-
190
- events.compact!
191
- end
192
-
193
- result
194
- end
195
- end
196
- end
@@ -1,124 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class DummyEvent
4
- include Event
5
- attr_accessor :name
6
- end
7
-
8
- class DummyEventTwo
9
- include Event
10
- attr_accessor :name
11
- end
12
-
13
- describe ExternalStore do
14
- before(:each) do
15
- ExternalStore.connection_profile = "mongodb://localhost/external_store_default"
16
- ExternalStore.new.collection.drop
17
- @store = ExternalStore.new
18
- end
19
- describe "#add_event" do
20
- before(:each) do
21
- @entity_type = random_string
22
- @event = DummyEvent.new(:name => random_string, :entity_id => random_object_id)
23
- end
24
-
25
- subject { @store.add_event(@entity_type, @event) }
26
-
27
- it "creates a record in the collection" do
28
- subject
29
- item = @store.collection.find_one
30
- item['_entity_type'].should eq(@entity_type)
31
- item['_type'].should eq(@event.class.name)
32
- item['name'].should eq(@event.name)
33
- item['entity_id'].should eq(@event.entity_id)
34
- end
35
- end
36
-
37
- describe "#get_events" do
38
- before(:each) do
39
- @reference_time = Time.now
40
- @ids = (-2..2).collect { |i| BSON::ObjectId.from_time(@reference_time + i) }
41
-
42
- @store.collection.insert({'_id' => @ids[0], '_type' => 'DummyEvent'})
43
- @store.collection.insert({'_id' => @ids[1], '_type' => 'DummyEventTwo'})
44
- @store.collection.insert({'_id' => @ids[2], '_type' => 'DummyEvent'})
45
- @store.collection.insert({'_id' => @ids[3], '_type' => 'DummyEventTwo'})
46
- @store.collection.insert({'_id' => @ids[4], '_type' => 'DummyEvent'})
47
- end
48
-
49
- subject { @store.get_events @since, @type }
50
-
51
- context "when time passed as since" do
52
- before(:each) do
53
- @since = @reference_time
54
- end
55
- context "when no type filter" do
56
- before(:each) do
57
- @type = nil
58
- @results = subject
59
- end
60
- it "returns two records" do
61
- @results.count.should eq(2)
62
- end
63
- it "it returns the 4th item first" do
64
- @results.first.id.should eq(@ids[3].to_s)
65
- end
66
- it "it returns the 5th item second" do
67
- @results[1].id.should eq(@ids[4].to_s)
68
- end
69
- end
70
- context "when type filter 'DummyEventTwo' passed" do
71
- before(:each) do
72
- @type = "DummyEventTwo"
73
- @results = subject
74
- end
75
- it "returns 1 record" do
76
- @results.count.should eq(1)
77
- end
78
- it "returns the 4th item" do
79
- @results.first.id.should eq(@ids[3].to_s)
80
- end
81
- end
82
- end
83
-
84
- context "when id passed as since" do
85
- before(:each) do
86
- @since = @ids[1].to_s
87
- end
88
- context "when no type filter passed" do
89
- before(:each) do
90
- @type = nil
91
- @results = subject
92
- end
93
- it "returns 3 records" do
94
- @results.count.should eq(3)
95
- end
96
- it "it returns the 3rd item first" do
97
- @results.first.id.should eq(@ids[2].to_s)
98
- end
99
- it "it returns the 4th item second" do
100
- @results[1].id.should eq(@ids[3].to_s)
101
- end
102
- it "it returns the 5th item second" do
103
- @results[2].id.should eq(@ids[4].to_s)
104
- end
105
- end
106
- context "when type filter 'DummyEvent' passed" do
107
- before(:each) do
108
- @type = "DummyEvent"
109
- @results = subject
110
- end
111
- it "returns 2 records" do
112
- @results.count.should eq(2)
113
- end
114
- it "returns the 3rd item" do
115
- @results.first.id.should eq(@ids[2].to_s)
116
- end
117
- it "returns the 5th item" do
118
- @results[1].id.should eq(@ids[4].to_s)
119
- end
120
- end
121
- end
122
- end
123
-
124
- end
@@ -1,200 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe MongoEntityStore do
4
- class DummyEntity
5
- include EntityStore::Entity
6
-
7
- attr_accessor :name, :description
8
-
9
- def set_name(new_name)
10
- record_event DummyEntityNameSet.new(name: new_name)
11
- end
12
- end
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
40
- MongoEntityStore.connection_profile = "mongodb://localhost/entity_store_default"
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 => 2, :name => random_string) }
50
- let(:unrelated_event) { DummyEntityNameSet.new(:entity_id => random_object_id, :entity_version => 4, :name => random_string) }
51
- let(:fourth_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 3, :name => random_string) }
52
-
53
- before do
54
- store.add_events([ second_event, unrelated_event, first_event, third_event, fourth_event ])
55
- end
56
-
57
- subject { store.get_events( [{ id: event_entity_id, since_version: since_version }])[event_entity_id] }
58
-
59
- context "all events" do
60
- let(:event_entity_id) { entity_id }
61
- let(:since_version) { 0 }
62
-
63
- it "should return the four events in order" do
64
- subject.should == [first_event, second_event, third_event, fourth_event]
65
- end
66
- end
67
-
68
- context "subset of events" do
69
- let(:event_entity_id) { entity_id }
70
- let(:since_version) { second_event.entity_version }
71
-
72
- it "should only include events greater than the given version" do
73
- subject.should == [ fourth_event ]
74
- end
75
- end
76
-
77
- context "no events" do
78
- let(:event_entity_id) { random_object_id }
79
- let(:since_version) { 0 }
80
-
81
- it "should return an empty array" do
82
- subject.should be_empty
83
- end
84
- end
85
- end
86
-
87
- describe "#get_entities" do
88
- let(:entity_class) { DummyEntity }
89
-
90
- let(:saved_entity) do
91
- entity = entity_class.new(:name => random_string, :description => random_string)
92
- entity.id = store.add_entity(entity)
93
- entity
94
- end
95
-
96
- let(:id) { saved_entity.id }
97
- let(:options) { { } }
98
-
99
- subject { store.get_entities( [ id ], options) }
100
-
101
- it "should retrieve an entity from the store with the same ID" do
102
- subject.first.id.should == saved_entity.id
103
- end
104
-
105
- it "should retrieve an entity from the store with the same class" do
106
- subject.first.class.should == saved_entity.class
107
- end
108
-
109
- it "should have the same version" do
110
- subject.first.version.should == saved_entity.version
111
- end
112
-
113
- context "when a snapshot does not exist" do
114
- it "should not have set the name" do
115
- subject.first.name.should be_nil
116
- end
117
- end
118
-
119
- context "when a snapshot exists" do
120
- before do
121
- saved_entity.version = 10
122
- store.snapshot_entity(saved_entity)
123
- end
124
-
125
- context "when a snapshot key not in use" do
126
- it "should have set the name" do
127
- subject.first.name.should == saved_entity.name
128
- end
129
- end
130
-
131
- context "when a snapshot key is in use" do
132
- let(:entity_class) { DummyEntityWithSnapshotKey }
133
-
134
- context "when the key matches the class's key" do
135
- it "should have set the name" do
136
- subject.first.name.should == saved_entity.name
137
- end
138
- end
139
-
140
- context "when the key does not match the class's key" do
141
- before do
142
- entity_class.increment_entity_store_snapshot_key!
143
- end
144
-
145
- it "should ignore the invalidated snapshot" do
146
- subject.first.name.should be_nil
147
- end
148
- end
149
- end
150
- end
151
-
152
- describe "context when enable exceptions" do
153
- let(:options) do
154
- { raise_exception: true }
155
- end
156
-
157
- context "when invalid id format passed" do
158
- let(:id) { random_string }
159
-
160
- it "should raise not found" do
161
- expect { subject }.to raise_error(NotFound)
162
- end
163
- end
164
- end
165
-
166
- end
167
-
168
- describe "#snapshot_entity" do
169
- let(:entity_class) { DummyEntity }
170
-
171
- let(:entity) do
172
- entity_class.new(:id => random_object_id, :version => random_integer, :name => random_string)
173
- end
174
-
175
- let(:saved_entity) do
176
- store.entities.find_one({'_id' => BSON::ObjectId.from_string(entity.id)})
177
- end
178
-
179
- subject { store.snapshot_entity(entity) }
180
-
181
- it "should add a snaphot to the entity record" do
182
- subject
183
- snapshot = saved_entity['snapshot']
184
-
185
- snapshot['id'].should eq(entity.id)
186
- snapshot['version'].should eq(entity.version)
187
- snapshot['name'].should eq(entity.name)
188
- snapshot['description'].should eq(entity.description)
189
- end
190
-
191
- context "entity with snapshot key" do
192
- let(:entity_class) { DummyEntityWithSnapshotKey }
193
-
194
- it "should store the snapshot key" do
195
- subject
196
- saved_entity['snapshot_key'].should == entity.class.entity_store_snapshot_key
197
- end
198
- end
199
- end
200
- end