entity_store_sequel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca75da75c0371ee57e8f87d2177691af50e1c231
4
+ data.tar.gz: a64bb369cb3b59e6fc065fdc209d4011499a8137
5
+ SHA512:
6
+ metadata.gz: 8e31b881de426b9590058dfe63cbc877e1921da6560e93b00c43e8a4082ffe2b75567861bfb47b4f5628c987caa9a8379afa4f524e7eb106ce0c266c48b0aecb
7
+ data.tar.gz: 0a7a1d86c96dc4cabd20b4b85d2cff026c74ffd197a96705058081f15c37a161504aa15190b6fa386da3941de709dcb5f93f94e54141d7044d9d857147c5f550
@@ -0,0 +1,226 @@
1
+ require 'sequel'
2
+ require 'uri'
3
+ require 'json'
4
+ require 'pigeon_hole'
5
+ require 'entity_store'
6
+
7
+ module EntityStoreSequel
8
+ class PostgresEntityStore
9
+ include EntityStore::Logging
10
+
11
+ Sequel.extension :pg_array_ops
12
+ Sequel.extension :pg_json_ops
13
+
14
+ class << self
15
+ attr_accessor :connection_string
16
+ attr_writer :connect_timeout
17
+
18
+ def database
19
+ return @_database if @_database
20
+
21
+ @_database ||= Sequel.connect(connection_string)
22
+ @_database.extension :pg_array
23
+ @_database.extension :pg_json
24
+
25
+ @_database
26
+ end
27
+
28
+ def init
29
+ unless database.table_exists?(:entities)
30
+ database.create_table :entities do
31
+ column :id, :char, primary_key: true, size: 24
32
+ String :_type
33
+ integer :snapshot_key
34
+ integer :version
35
+ column :snapshot, :jsonb
36
+ end
37
+ end
38
+
39
+ unless database.table_exists?(:entity_events)
40
+ database.create_table :entity_events do
41
+ column :id, :char, primary_key: true, size: 24
42
+ String :_type
43
+ column :_entity_id, :char, size: 24
44
+ integer :entity_version
45
+ column :data, :jsonb
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def open
52
+ PostgresEntityStore.database
53
+ end
54
+
55
+ def entities
56
+ @entities_collection ||= open[:entities]
57
+ end
58
+
59
+ def events
60
+ @events_collection ||= open[:entity_events]
61
+ end
62
+
63
+ def clear
64
+ open.drop_table(:entities, :entity_events)
65
+ @entities_collection = nil
66
+ @events_collection = nil
67
+ end
68
+
69
+ def ensure_indexes
70
+ end
71
+
72
+ def add_entity(entity, id = BSON::ObjectId.new)
73
+ entities.insert(:id => id.to_s, :_type => entity.class.name, :version => entity.version)
74
+ id.to_s
75
+ end
76
+
77
+ def save_entity(entity)
78
+ entities.where(:id => entity.id).update(:version => entity.version)
79
+ end
80
+
81
+ # Public: create a snapshot of the entity and store in the entities collection
82
+ #
83
+ def snapshot_entity(entity)
84
+ if entity.class.respond_to? :entity_store_snapshot_key
85
+ # If there is a snapshot key, store it too
86
+ snapshot_key = entity.class.entity_store_snapshot_key
87
+ else
88
+ # Otherwise, make sure there isn't one set
89
+ snapshot_key = nil
90
+ end
91
+
92
+ unless entities[:id => entity.id]
93
+ entities.insert(:id => entity.id, :_type => entity.class.name, :version => entity.version)
94
+ end
95
+
96
+ entities
97
+ .where(:id => entity.id)
98
+ .update(:snapshot => PigeonHole.generate(entity.attributes), :snapshot_key => snapshot_key )
99
+ end
100
+
101
+ # Public - remove the snapshot for an entity
102
+ #
103
+ def remove_entity_snapshot(id)
104
+ entities.where(:id => id).update(:snapshot => nil)
105
+ end
106
+
107
+ # Public: remove all snapshots
108
+ #
109
+ # type - String optional class name for the entity
110
+ #
111
+ def remove_snapshots(type=nil)
112
+ if type
113
+ entities.where(:_type => type).update(:snapshot => nil)
114
+ else
115
+ entities.update(:snapshot => nil)
116
+ end
117
+ end
118
+
119
+ def add_events(items)
120
+ events_with_id = items.map { |e| [ BSON::ObjectId.new, e ] }
121
+ add_events_with_ids(events_with_id)
122
+ end
123
+
124
+ def add_events_with_ids(event_id_map)
125
+ event_id_map.each do |id, event|
126
+ doc = {
127
+ :id => id.to_s,
128
+ :_type => event.class.name,
129
+ :_entity_id => BSON::ObjectId.from_string(event.entity_id).to_s,
130
+ :entity_version => event.entity_version,
131
+ :data => PigeonHole.generate(event.attributes),
132
+ }
133
+ events.insert(doc)
134
+ end
135
+ end
136
+
137
+ # Public: loads the entity from the store, including any available snapshots
138
+ # then loads the events to complete the state
139
+ #
140
+ # ids - Array of Strings representation of BSON::ObjectId
141
+ # options - Hash of options (default: {})
142
+ # :raise_exception - Boolean (default: true)
143
+ #
144
+ # Returns an array of entities
145
+ def get_entities(ids, options={})
146
+ ids.each do |id|
147
+ begin
148
+ BSON::ObjectId.from_string(id)
149
+ rescue BSON::InvalidObjectId
150
+ raise NotFound.new(id) if options.fetch(:raise_exception, true)
151
+ nil
152
+ end
153
+ end
154
+
155
+ entities.where(:id => ids).map do |attrs|
156
+ begin
157
+ entity_type = EntityStore::Config.load_type(attrs[:_type])
158
+
159
+ # Check if there is a snapshot key in use
160
+ if entity_type.respond_to? :entity_store_snapshot_key
161
+ active_key = entity_type.entity_store_snapshot_key
162
+
163
+ # Discard the snapshot if the keys don't match
164
+ unless active_key == attrs[:snapshot_key]
165
+ attrs.delete(:snapshot)
166
+ end
167
+ end
168
+
169
+ if attrs[:snapshot]
170
+ hash = attrs[:snapshot].to_h
171
+ entity = entity_type.new(hash)
172
+ else
173
+ entity = entity_type.new({'id' => attrs[:id].to_s })
174
+ end
175
+ rescue => e
176
+ log_error "Error loading type #{attrs[:_type]}", e
177
+ raise
178
+ end
179
+
180
+ entity
181
+ end
182
+
183
+ end
184
+
185
+ # Public: get events for an array of criteria objects
186
+ # because each entity could have a different reference
187
+ # version this allows optional criteria to be specifed
188
+ #
189
+ #
190
+ # criteria - Hash :id mandatory, :since_version optional
191
+ #
192
+ # Examples
193
+ #
194
+ # get_events_for_criteria([ { id: "23232323"}, { id: "2398429834", since_version: 4 } ] )
195
+ #
196
+ # Returns Hash with id as key and Array of Event instances as value
197
+ def get_events(criteria)
198
+ return {} if criteria.empty?
199
+
200
+ result = {}
201
+
202
+ criteria.each do |item|
203
+ raise ArgumentError.new(":id missing from criteria") unless item[:id]
204
+
205
+ query = events.where(:_entity_id => item[:id])
206
+
207
+ if item[:since_version]
208
+ query = query.where('entity_version > ?', item[:since_version])
209
+ end
210
+
211
+ result[item[:id]] = query.order(:entity_version, :id).map do |attrs|
212
+ begin
213
+ hash = attrs[:data].to_h
214
+ hash[:entity_version] = attrs[:entity_version]
215
+ EntityStore::Config.load_type(attrs[:_type]).new(hash)
216
+ rescue => e
217
+ log_error "Error loading type #{attrs[:_type]}", e
218
+ next
219
+ end
220
+ end.compact
221
+ end
222
+
223
+ result
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,3 @@
1
+ module EntityStoreSequel
2
+ VERSION = "0.0.1".freeze
3
+ end
@@ -0,0 +1,3 @@
1
+ module EntityStoreSequel
2
+ require_relative 'entity_store_sequel/postgres_entity_store'
3
+ end
@@ -0,0 +1,5 @@
1
+ module Sequel
2
+ def self.parse_json(json)
3
+ PigeonHole.parse(json)
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require "#{Rake.application.original_dir}/lib/entity_store"
2
+
3
+ namespace :entity_store_sequel do
4
+ task :drop_db do
5
+ EntityStore::PostgresEntityStore.new.clear
6
+ end
7
+
8
+ task :create_db do
9
+ EntityStore::PostgresEntityStore.init
10
+ end
11
+ end
@@ -0,0 +1,242 @@
1
+ require 'spec_helper'
2
+
3
+ describe PostgresEntityStore 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 DummyEntityWithDate
15
+ include EntityStore::Entity
16
+
17
+ attr_accessor :name, :description, :date, :symbol, :symbol_hash
18
+ attr_accessor :nested_hash
19
+
20
+ def set_name(new_name)
21
+ record_event DummyEntityNameSet.new(name: new_name)
22
+ end
23
+ end
24
+
25
+ class DummyEntityWithSnapshotKey < DummyEntity
26
+ def self.entity_store_snapshot_key
27
+ @entity_store_snapshot_key ||= 1
28
+ end
29
+
30
+ def self.increment_entity_store_snapshot_key!
31
+ @entity_store_snapshot_key = entity_store_snapshot_key + 1
32
+ end
33
+ end
34
+
35
+ class DummyEntityNameSet
36
+ include EntityStore::Event
37
+
38
+ attr_accessor :name
39
+
40
+ def apply(entity)
41
+ entity.name = self.name
42
+ end
43
+
44
+ def ==(other)
45
+ # Crude check relying on inspect, ok for tests
46
+ self.inspect == other.inspect
47
+ end
48
+ end
49
+
50
+ let(:store) do
51
+ described_class.connection_string = ENV['SQL_CONNECTION'] || 'postgres://localhost/cronofy_test'
52
+ described_class.init
53
+ described_class.new
54
+ end
55
+
56
+ describe "event storage" do
57
+ let(:entity_id) { random_object_id }
58
+
59
+ let(:first_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 1, :name => random_string) }
60
+ let(:second_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 2, :name => random_string) }
61
+ let(:third_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 2, :name => random_string) }
62
+ let(:unrelated_event) { DummyEntityNameSet.new(:entity_id => random_object_id, :entity_version => 4, :name => random_string) }
63
+ let(:fourth_event) { DummyEntityNameSet.new(:entity_id => entity_id, :entity_version => 3, :name => random_string) }
64
+
65
+ before do
66
+ store.add_events([ second_event, unrelated_event, first_event, third_event, fourth_event ])
67
+ end
68
+
69
+ subject { store.get_events( [{ id: event_entity_id, since_version: since_version }])[event_entity_id] }
70
+
71
+ context "all events" do
72
+ let(:event_entity_id) { entity_id }
73
+ let(:since_version) { 0 }
74
+
75
+ it "should return the four events in order" do
76
+ subject.should == [first_event, second_event, third_event, fourth_event]
77
+ end
78
+ end
79
+
80
+ context "subset of events" do
81
+ let(:event_entity_id) { entity_id }
82
+ let(:since_version) { second_event.entity_version }
83
+
84
+ it "should only include events greater than the given version" do
85
+ subject.should == [ fourth_event ]
86
+ end
87
+ end
88
+
89
+ context "no events" do
90
+ let(:event_entity_id) { random_object_id }
91
+ let(:since_version) { 0 }
92
+
93
+ it "should return an empty array" do
94
+ subject.should be_empty
95
+ end
96
+ end
97
+ end
98
+
99
+ describe "#get_entities" do
100
+ let(:entity_class) { DummyEntityWithDate }
101
+ let(:entity_date) { random_time }
102
+
103
+ let(:saved_entity) do
104
+ entity = entity_class.new(
105
+ :name => random_string,
106
+ :description => random_string,
107
+ :date => entity_date,
108
+ :symbol => :foo,
109
+ :symbol_hash => { foo: :bar },
110
+ :nested_hash => { foo: { bar: :baz } },
111
+ )
112
+ entity.id = store.add_entity(entity)
113
+ entity
114
+ end
115
+
116
+ let(:id) { saved_entity.id }
117
+ let(:options) { { } }
118
+
119
+ subject { store.get_entities( [ id ], options) }
120
+
121
+ it "should retrieve an entity from the store with the same ID" do
122
+ subject.first.id.should == saved_entity.id
123
+ end
124
+
125
+ it "should retrieve an entity from the store with the same class" do
126
+ subject.first.class.should == saved_entity.class
127
+ end
128
+
129
+ it "should have the same version" do
130
+ subject.first.version.should == saved_entity.version
131
+ end
132
+
133
+ context "when a snapshot does not exist" do
134
+ it "should not have set the name" do
135
+ subject.first.name.should be_nil
136
+ end
137
+
138
+ it "should not have a date set" do
139
+ subject.first.date.should be_nil
140
+ end
141
+
142
+ it "should not have a symbol set" do
143
+ subject.first.symbol.should be_nil
144
+ end
145
+ end
146
+
147
+ context "when a snapshot exists" do
148
+ before do
149
+ saved_entity.version = 10
150
+ store.snapshot_entity(saved_entity)
151
+ end
152
+
153
+ context "when a snapshot key not in use" do
154
+ it "should have set the name" do
155
+ subject.first.name.should == saved_entity.name
156
+ end
157
+
158
+ it "should have a date set" do
159
+ subject.first.date.should == saved_entity.date
160
+ end
161
+
162
+ it "should have a symbol set" do
163
+ subject.first.symbol.should == :foo
164
+ end
165
+
166
+ it "should have a symbol hash set" do
167
+ subject.first.symbol_hash.should == { "foo" => :bar }
168
+ subject.first.nested_hash.should == { "foo" => { "bar" => :baz } }
169
+ end
170
+ end
171
+
172
+ context "when a snapshot key is in use" do
173
+ let(:entity_class) { DummyEntityWithSnapshotKey }
174
+
175
+ context "when the key matches the class's key" do
176
+ it "should have set the name" do
177
+ subject.first.name.should == saved_entity.name
178
+ end
179
+ end
180
+
181
+ context "when the key does not match the class's key" do
182
+ before do
183
+ entity_class.increment_entity_store_snapshot_key!
184
+ end
185
+
186
+ it "should ignore the invalidated snapshot" do
187
+ subject.first.name.should be_nil
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ describe "context when enable exceptions" do
194
+ let(:options) do
195
+ { raise_exception: true }
196
+ end
197
+
198
+ context "when invalid id format passed" do
199
+ let(:id) { random_string }
200
+
201
+ it "should raise not found" do
202
+ expect { subject }.to raise_error(NotFound)
203
+ end
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ describe "#snapshot_entity" do
210
+ let(:entity_class) { DummyEntityWithDate }
211
+
212
+ let(:entity) do
213
+ entity_class.new(:id => random_object_id, :version => random_integer, :name => random_string)
214
+ end
215
+
216
+ let(:saved_entity) do
217
+ store.entities.where(:id => entity.id).first
218
+ end
219
+
220
+ let(:snapshot) { saved_entity[:snapshot] }
221
+
222
+ subject { store.snapshot_entity(entity) }
223
+
224
+ it "should add a snaphot to the entity record" do
225
+ subject
226
+
227
+ snapshot['id'].should eq(entity.id)
228
+ snapshot['version'].should eq(entity.version)
229
+ snapshot['name'].should eq(entity.name)
230
+ snapshot['description'].should eq(entity.description)
231
+ end
232
+
233
+ context "entity with snapshot key" do
234
+ let(:entity_class) { DummyEntityWithSnapshotKey }
235
+
236
+ it "should store the snapshot key" do
237
+ subject
238
+ saved_entity[:snapshot_key].should == entity.class.entity_store_snapshot_key
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ class DummyEntity
4
+ include Entity
5
+
6
+ attr_accessor :name
7
+
8
+ def set_name(name)
9
+ record_event DummyEntityNameSet.new(name: name)
10
+ end
11
+ end
12
+
13
+ class DummyEntityNameSet
14
+ include Event
15
+
16
+ attr_accessor :name
17
+
18
+ def apply(entity)
19
+ entity.name = name
20
+ end
21
+ end
22
+
23
+ class DummyEntitySubscriber
24
+ class << self
25
+ attr_accessor :event_name
26
+ end
27
+
28
+ def dummy_entity_name_set(event)
29
+ DummyEntitySubscriber.event_name = event.name
30
+ end
31
+ end
32
+
33
+ describe "end to end" do
34
+ before(:each) do
35
+ PostgresEntityStore.connection_string = ENV['SQL_CONNECTION'] || 'postgres://localhost/cronofy_test'
36
+ PostgresEntityStore.init
37
+
38
+ EntityStore::Config.setup do |config|
39
+ config.store = PostgresEntityStore.new
40
+ config.event_subscribers << DummyEntitySubscriber
41
+ end
42
+ end
43
+
44
+ context "when save entity" do
45
+ let(:name) { random_string }
46
+ before(:each) do
47
+ @entity = DummyEntity.new
48
+ @entity.set_name name
49
+ @id = Store.new.save @entity
50
+ end
51
+
52
+ it "publishes event to the subscriber" do
53
+ DummyEntitySubscriber.event_name.should eq(name)
54
+ end
55
+ it "is retrievable with the events applied" do
56
+ EntityStore::Store.new.get(@entity.id).name.should eq(name)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,42 @@
1
+ require 'rake'
2
+ require 'rspec'
3
+ require 'hatchet'
4
+ require 'sequel'
5
+
6
+ require "#{Rake.application.original_dir}/lib/entity_store_sequel"
7
+
8
+ require_relative '../lib/sequel/core_ext'
9
+
10
+ RSpec.configure do |config|
11
+ config.color_enabled = true
12
+ end
13
+
14
+ include EntityStore
15
+ include EntityStoreSequel
16
+
17
+ Hatchet.configure do |config|
18
+ config.level :fatal
19
+ config.formatter = Hatchet::SimpleFormatter.new
20
+ config.appenders << Hatchet::LoggerAppender.new do |appender|
21
+ appender.logger = Logger.new(STDOUT)
22
+ end
23
+ end
24
+ include Hatchet
25
+
26
+ EntityStore::Config.logger = log
27
+
28
+ def random_string
29
+ (0...24).map{ ('a'..'z').to_a[rand(26)] }.join
30
+ end
31
+
32
+ def random_integer
33
+ rand(9999)
34
+ end
35
+
36
+ def random_time
37
+ Time.at(Time.now.to_i - random_integer)
38
+ end
39
+
40
+ def random_object_id
41
+ BSON::ObjectId.from_time(random_time, :unique => true).to_s
42
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entity_store_sequel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Stephen Binns
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sequel_pg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: entity_store
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pigeon_hole
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: bson
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.8'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: hatchet
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.2'
111
+ description: Sequel body for Entity Store
112
+ email: stephen@cronofy.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - lib/entity_store_sequel.rb
118
+ - lib/entity_store_sequel/postgres_entity_store.rb
119
+ - lib/entity_store_sequel/version.rb
120
+ - lib/sequel/core_ext.rb
121
+ - lib/tasks/entity_store.rake
122
+ - spec/entity_store_sequel/postgres_entity_store_spec.rb
123
+ - spec/entity_store_spec.rb
124
+ - spec/spec_helper.rb
125
+ homepage: http://github.com/cronofy/entity_store_sequel
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.6.6
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Sequel body for Entity Store
149
+ test_files:
150
+ - spec/entity_store_sequel/postgres_entity_store_spec.rb
151
+ - spec/entity_store_spec.rb
152
+ - spec/spec_helper.rb
153
+ has_rdoc: