mongo_entity_store 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 381bcb65dda4bd6210e20e336d8c0e2936016921
4
+ data.tar.gz: 9f31363aa40b798f859541c04c99a50f360cbb04
5
+ SHA512:
6
+ metadata.gz: 9a59c6440e303d4ed2b77e6e1112cc7749cabe45e5f6bca87f9c1196f8ff205b507893f6f55f8e5e7d80ba3683810e48031cc61cc2bf6ab900977a24d8b31c9a
7
+ data.tar.gz: bbadc70bf04b5d300ebed283cec85b79077da636ba25865d3e7900a3273a7aaeddbc0094152eff67ff58924a54df0b92c546ae9d0a79952dfea75fc589d6eaed
@@ -0,0 +1,62 @@
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
@@ -0,0 +1,204 @@
1
+ require 'mongo'
2
+ require 'entity_store'
3
+ require 'uri'
4
+
5
+ module EntityStore
6
+ class MongoEntityStore
7
+ include Mongo
8
+ include Logging
9
+
10
+ class << self
11
+ attr_accessor :connection_profile
12
+ attr_writer :connect_timeout
13
+
14
+ def connection
15
+ @_connection ||= Mongo::MongoClient.from_uri(MongoEntityStore.connection_profile, :connect_timeout => EntityStore::Config.connect_timeout, :refresh_mode => :sync)
16
+ end
17
+
18
+ def database
19
+ @_database ||= MongoEntityStore.connection_profile.split('/').last.split('?').first
20
+ end
21
+ end
22
+
23
+ def open
24
+ MongoEntityStore.connection.db(MongoEntityStore.database)
25
+ end
26
+
27
+ def entities
28
+ @entities_collection ||= open['entities']
29
+ end
30
+
31
+ def events
32
+ @events_collection ||= open['entity_events']
33
+ end
34
+
35
+ def clear
36
+ entities.drop
37
+ @entities_collection = nil
38
+ events.drop
39
+ @events_collection = nil
40
+ end
41
+
42
+ def ensure_indexes
43
+ events.ensure_index([['_entity_id', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]])
44
+ events.ensure_index([['_entity_id', Mongo::ASCENDING], ['entity_version', Mongo::ASCENDING], ['_id', Mongo::ASCENDING]])
45
+ end
46
+
47
+ def add_entity(entity, id = BSON::ObjectId.new)
48
+ entities.insert('_id' => id, '_type' => entity.class.name, 'version' => entity.version).to_s
49
+ end
50
+
51
+ def save_entity(entity)
52
+ entities.update({'_id' => BSON::ObjectId.from_string(entity.id)}, { '$set' => { 'version' => entity.version } })
53
+ end
54
+
55
+ # Public: create a snapshot of the entity and store in the entities collection
56
+ #
57
+ def snapshot_entity(entity)
58
+ query = {'_id' => BSON::ObjectId.from_string(entity.id)}
59
+ updates = { '$set' => { 'snapshot' => entity.attributes } }
60
+
61
+ if entity.class.respond_to? :entity_store_snapshot_key
62
+ # If there is a snapshot key, store it too
63
+ updates['$set']['snapshot_key'] = entity.class.entity_store_snapshot_key
64
+ else
65
+ # Otherwise, make sure there isn't one set
66
+ updates['$unset'] = { 'snapshot_key' => '' }
67
+ end
68
+
69
+ entities.update(query, updates, { :upsert => true} )
70
+ end
71
+
72
+ # Public - remove the snapshot for an entity
73
+ #
74
+ def remove_entity_snapshot(id)
75
+ entities.update({'_id' => BSON::ObjectId.from_string(id)}, { '$unset' => { 'snapshot' => 1}})
76
+ end
77
+
78
+ # Public: remove all snapshots
79
+ #
80
+ # type - String optional class name for the entity
81
+ #
82
+ def remove_snapshots type=nil
83
+ query = {}
84
+ query['_type'] = type if type
85
+ entities.update(query, { '$unset' => { 'snapshot' => 1 } }, { multi: true })
86
+ end
87
+
88
+ def clear_entity_events(id, excluded_types)
89
+ events.remove({
90
+ '_entity_id' => BSON::ObjectId.from_string(id),
91
+ '_type' => { '$nin' => excluded_types },
92
+ })
93
+ end
94
+
95
+ def add_events(items)
96
+ events_with_id = items.map { |e| [ BSON::ObjectId.new, e ] }
97
+ add_events_with_ids(events_with_id)
98
+ end
99
+
100
+ def add_events_with_ids(event_id_map)
101
+ docs = event_id_map.map do |id, event|
102
+ {
103
+ '_id' => id,
104
+ '_type' => event.class.name,
105
+ '_entity_id' => BSON::ObjectId.from_string(event.entity_id)
106
+ }.merge(event.attributes)
107
+ end
108
+
109
+ events.insert(docs)
110
+ end
111
+
112
+ # Public: loads the entity from the store, including any available snapshots
113
+ # then loads the events to complete the state
114
+ #
115
+ # ids - Array of Strings representation of BSON::ObjectId
116
+ # options - Hash of options (default: {})
117
+ # :raise_exception - Boolean (default: true)
118
+ #
119
+ # Returns an array of entities
120
+ def get_entities(ids, options={})
121
+
122
+ object_ids = ids.map do |id|
123
+ begin
124
+ BSON::ObjectId.from_string(id)
125
+ rescue BSON::InvalidObjectId
126
+ raise NotFound.new(id) if options.fetch(:raise_exception, true)
127
+ nil
128
+ end
129
+ end
130
+
131
+ entities.find('_id' => { '$in' => object_ids }).map do |attrs|
132
+ begin
133
+ entity_type = EntityStore::Config.load_type(attrs['_type'])
134
+
135
+ # Check if there is a snapshot key in use
136
+ if entity_type.respond_to? :entity_store_snapshot_key
137
+ active_key = entity_type.entity_store_snapshot_key
138
+ # Discard the snapshot if the keys don't match
139
+ attrs.delete('snapshot') unless active_key == attrs['snapshot_key']
140
+ end
141
+
142
+ entity = entity_type.new(attrs['snapshot'] || {'id' => attrs['_id'].to_s })
143
+ rescue => e
144
+ log_error "Error loading type #{attrs['_type']}", e
145
+ raise
146
+ end
147
+
148
+ entity
149
+ end
150
+
151
+ end
152
+
153
+ # Public: get events for an array of criteria objects
154
+ # because each entity could have a different reference
155
+ # version this allows optional criteria to be specifed
156
+ #
157
+ #
158
+ # criteria - Hash :id mandatory, :since_version optional
159
+ #
160
+ # Examples
161
+ #
162
+ # get_events_for_criteria([ { id: "23232323"}, { id: "2398429834", since_version: 4 } ] )
163
+ #
164
+ # Returns Hash with id as key and Array of Event instances as value
165
+ def get_events(criteria)
166
+ return {} if criteria.empty?
167
+
168
+ query_items = criteria.map do |item|
169
+ raise ArgumentError.new(":id missing from criteria") unless item[:id]
170
+ item_query = { '_entity_id' => BSON::ObjectId.from_string(item[:id]) }
171
+ item_query['entity_version'] = { '$gt' => item[:since_version] } if item[:since_version]
172
+ item_query
173
+ end
174
+
175
+ query = { '$or' => query_items }
176
+
177
+ result = Hash[ criteria.map { |item| [ item[:id], [] ] } ]
178
+
179
+ events.find(query).each do |attrs|
180
+ result[attrs['_entity_id'].to_s] << attrs
181
+ end
182
+
183
+ result.each do |id, events|
184
+ # Have to do the sort client side as otherwise the query will not use
185
+ # indexes (http://docs.mongodb.org/manual/reference/operator/query/or/#or-and-sort-operations)
186
+ events.sort_by! { |attrs| [attrs['entity_version'], attrs['_id'].to_s] }
187
+
188
+ # Convert the attributes into event objects
189
+ events.map! do |attrs|
190
+ begin
191
+ EntityStore::Config.load_type(attrs['_type']).new(attrs)
192
+ rescue => e
193
+ log_error "Error loading type #{attrs['_type']}", e
194
+ nil
195
+ end
196
+ end
197
+
198
+ events.compact!
199
+ end
200
+
201
+ result
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,3 @@
1
+ module EntityStoreMongo
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ module EntityStore
2
+ require_relative 'mongo_entity_store/mongo_entity_store'
3
+ require_relative 'mongo_entity_store/external_store'
4
+ end
@@ -0,0 +1,124 @@
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
@@ -0,0 +1,229 @@
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 "#clear_entity_events" do
88
+ let(:entity_id) { random_object_id }
89
+ let(:second_entity_id) { random_object_id }
90
+
91
+ let(:first_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 1, :name => random_string) }
92
+ let(:second_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 2, :name => random_string) }
93
+ let(:third_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 2, :name => random_string) }
94
+ let(:unrelated_event) { DummyEntityNameSet.new(:entity_id => second_entity_id, :entity_version => 4, :name => random_string) }
95
+ let(:fourth_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 3, :name => random_string) }
96
+
97
+ before do
98
+ store.add_events([ second_event, unrelated_event, first_event, third_event, fourth_event ])
99
+ end
100
+
101
+ subject { store.clear_entity_events(entity_id, []) }
102
+
103
+ it "clears the events from the entity" do
104
+ subject
105
+ events = store.get_events( [ id: entity_id ])[entity_id]
106
+ expect(events).to be_empty
107
+ end
108
+
109
+ it "does not delete unrelated the events" do
110
+ subject
111
+ events = store.get_events( [ id: second_entity_id ])[second_entity_id]
112
+ expect(events.count).to eq(1)
113
+ end
114
+ end
115
+
116
+ describe "#get_entities" do
117
+ let(:entity_class) { DummyEntity }
118
+
119
+ let(:saved_entity) do
120
+ entity = entity_class.new(:name => random_string, :description => random_string)
121
+ entity.id = store.add_entity(entity)
122
+ entity
123
+ end
124
+
125
+ let(:id) { saved_entity.id }
126
+ let(:options) { { } }
127
+
128
+ subject { store.get_entities( [ id ], options) }
129
+
130
+ it "should retrieve an entity from the store with the same ID" do
131
+ subject.first.id.should == saved_entity.id
132
+ end
133
+
134
+ it "should retrieve an entity from the store with the same class" do
135
+ subject.first.class.should == saved_entity.class
136
+ end
137
+
138
+ it "should have the same version" do
139
+ subject.first.version.should == saved_entity.version
140
+ end
141
+
142
+ context "when a snapshot does not exist" do
143
+ it "should not have set the name" do
144
+ subject.first.name.should be_nil
145
+ end
146
+ end
147
+
148
+ context "when a snapshot exists" do
149
+ before do
150
+ saved_entity.version = 10
151
+ store.snapshot_entity(saved_entity)
152
+ end
153
+
154
+ context "when a snapshot key not in use" do
155
+ it "should have set the name" do
156
+ subject.first.name.should == saved_entity.name
157
+ end
158
+ end
159
+
160
+ context "when a snapshot key is in use" do
161
+ let(:entity_class) { DummyEntityWithSnapshotKey }
162
+
163
+ context "when the key matches the class's key" do
164
+ it "should have set the name" do
165
+ subject.first.name.should == saved_entity.name
166
+ end
167
+ end
168
+
169
+ context "when the key does not match the class's key" do
170
+ before do
171
+ entity_class.increment_entity_store_snapshot_key!
172
+ end
173
+
174
+ it "should ignore the invalidated snapshot" do
175
+ subject.first.name.should be_nil
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ describe "context when enable exceptions" do
182
+ let(:options) do
183
+ { raise_exception: true }
184
+ end
185
+
186
+ context "when invalid id format passed" do
187
+ let(:id) { random_string }
188
+
189
+ it "should raise not found" do
190
+ expect { subject }.to raise_error(NotFound)
191
+ end
192
+ end
193
+ end
194
+
195
+ end
196
+
197
+ describe "#snapshot_entity" do
198
+ let(:entity_class) { DummyEntity }
199
+
200
+ let(:entity) do
201
+ entity_class.new(:id => random_object_id, :version => random_integer, :name => random_string)
202
+ end
203
+
204
+ let(:saved_entity) do
205
+ store.entities.find_one({'_id' => BSON::ObjectId.from_string(entity.id)})
206
+ end
207
+
208
+ subject { store.snapshot_entity(entity) }
209
+
210
+ it "should add a snaphot to the entity record" do
211
+ subject
212
+ snapshot = saved_entity['snapshot']
213
+
214
+ snapshot['id'].should eq(entity.id)
215
+ snapshot['version'].should eq(entity.version)
216
+ snapshot['name'].should eq(entity.name)
217
+ snapshot['description'].should eq(entity.description)
218
+ end
219
+
220
+ context "entity with snapshot key" do
221
+ let(:entity_class) { DummyEntityWithSnapshotKey }
222
+
223
+ it "should store the snapshot key" do
224
+ subject
225
+ saved_entity['snapshot_key'].should == entity.class.entity_store_snapshot_key
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rspec'
3
+ require 'mongo'
4
+ require 'hatchet'
5
+
6
+ require "#{Rake.application.original_dir}/lib/mongo_entity_store"
7
+
8
+ RSpec.configure do |config|
9
+ config.color_enabled = true
10
+ end
11
+
12
+ include EntityStore
13
+
14
+ Hatchet.configure do |config|
15
+ config.level :fatal
16
+ config.formatter = Hatchet::SimpleFormatter.new
17
+ config.appenders << Hatchet::LoggerAppender.new do |appender|
18
+ appender.logger = Logger.new(STDOUT)
19
+ end
20
+ end
21
+ include Hatchet
22
+
23
+ EntityStore::Config.logger = log
24
+
25
+ def random_string
26
+ (0...24).map{ ('a'..'z').to_a[rand(26)] }.join
27
+ end
28
+
29
+ def random_integer
30
+ rand(9999)
31
+ end
32
+
33
+ def random_time
34
+ Time.now - random_integer
35
+ end
36
+
37
+ def random_object_id
38
+ BSON::ObjectId.from_time(random_time, :unique => true).to_s
39
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo_entity_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Bird
8
+ - Stepheb Binns
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-01-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: entity_store
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: mongo
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.8'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.8'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bson
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.8'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.8'
56
+ - !ruby/object:Gem::Dependency
57
+ name: hatchet
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.2'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.2'
70
+ description: Event sourced entity store with a mongo body
71
+ email: adam.bird@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/mongo_entity_store.rb
77
+ - lib/mongo_entity_store/external_store.rb
78
+ - lib/mongo_entity_store/mongo_entity_store.rb
79
+ - lib/mongo_entity_store/version.rb
80
+ - spec/mongo_entity_store/external_store_spec.rb
81
+ - spec/mongo_entity_store/mongo_entity_store_spec.rb
82
+ - spec/spec_helper.rb
83
+ homepage: http://github.com/cronogy/mongo_entity_store
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.5.1
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Event sourced entity store with a mongo body
107
+ test_files:
108
+ - spec/spec_helper.rb
109
+ - spec/mongo_entity_store/external_store_spec.rb
110
+ - spec/mongo_entity_store/mongo_entity_store_spec.rb