entity_store_sequel 0.0.1

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