entity_store 0.6.0 → 1.0.0
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/store.rb +4 -0
- data/lib/entity_store/version.rb +1 -1
- data/lib/entity_store.rb +0 -3
- data/spec/entity_store/event_bus_spec.rb +5 -2
- data/spec/entity_store_spec.rb +52 -4
- data/spec/spec_helper.rb +1 -1
- metadata +7 -27
- data/lib/entity_store/external_store.rb +0 -62
- data/lib/entity_store/mongo_entity_store.rb +0 -196
- data/spec/entity_store/external_store_spec.rb +0 -124
- data/spec/entity_store/mongo_entity_store_spec.rb +0 -200
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1bf9f57ff36a9e94b582cec4dd38d5364536455
|
4
|
+
data.tar.gz: 27b4160e5a19ee2e315a8a4f526e7bfd46f0c39b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 763f6f18705899dcf36b63b526a7dd97dd40896409401a2405a1609c3ea4f026c344c7f0e8d66af5d8e1f77c5558dc98da044346b598064c02233e3c01e5fbae
|
7
|
+
data.tar.gz: cb1c725e9c7befe55fe237510bc20a37ab4752c375e7395373489ddfd8064f28b31de00c9752c8c73f58a2bcea2e68b5d6071dc6a6dc6b453dd362da4929aaaf
|
data/lib/entity_store/store.rb
CHANGED
@@ -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
|
data/lib/entity_store/version.rb
CHANGED
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(
|
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(
|
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)
|
data/spec/entity_store_spec.rb
CHANGED
@@ -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 =
|
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
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.
|
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:
|
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.
|
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/
|
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
|