pod4 0.10.6 → 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 +5 -5
- data/.bugs/bugs +2 -1
- data/.bugs/details/b5368c7ef19065fc597b5692314da71772660963.txt +53 -0
- data/.hgtags +1 -0
- data/Gemfile +5 -5
- data/README.md +157 -46
- data/lib/pod4/basic_model.rb +9 -22
- data/lib/pod4/connection.rb +67 -0
- data/lib/pod4/connection_pool.rb +154 -0
- data/lib/pod4/errors.rb +20 -0
- data/lib/pod4/interface.rb +34 -12
- data/lib/pod4/model.rb +32 -27
- data/lib/pod4/nebulous_interface.rb +25 -30
- data/lib/pod4/null_interface.rb +22 -16
- data/lib/pod4/pg_interface.rb +84 -104
- data/lib/pod4/sequel_interface.rb +138 -82
- data/lib/pod4/tds_interface.rb +83 -70
- data/lib/pod4/tweaking.rb +105 -0
- data/lib/pod4/version.rb +1 -1
- data/md/breaking_changes.md +80 -0
- data/spec/common/basic_model_spec.rb +67 -70
- data/spec/common/connection_pool_parallelism_spec.rb +154 -0
- data/spec/common/connection_pool_spec.rb +246 -0
- data/spec/common/connection_spec.rb +129 -0
- data/spec/common/model_ai_missing_id_spec.rb +256 -0
- data/spec/common/model_plus_encrypting_spec.rb +16 -4
- data/spec/common/model_plus_tweaking_spec.rb +128 -0
- data/spec/common/model_plus_typecasting_spec.rb +10 -4
- data/spec/common/model_spec.rb +283 -363
- data/spec/common/nebulous_interface_spec.rb +159 -108
- data/spec/common/null_interface_spec.rb +88 -65
- data/spec/common/sequel_interface_pg_spec.rb +217 -161
- data/spec/common/shared_examples_for_interface.rb +50 -50
- data/spec/jruby/sequel_encrypting_jdbc_pg_spec.rb +1 -1
- data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +3 -3
- data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +3 -23
- data/spec/mri/pg_encrypting_spec.rb +1 -1
- data/spec/mri/pg_interface_spec.rb +311 -223
- data/spec/mri/sequel_encrypting_spec.rb +1 -1
- data/spec/mri/sequel_interface_spec.rb +177 -180
- data/spec/mri/tds_encrypting_spec.rb +1 -1
- data/spec/mri/tds_interface_spec.rb +296 -212
- data/tags +340 -174
- metadata +19 -11
- data/md/fixme.md +0 -3
- data/md/roadmap.md +0 -125
- data/md/typecasting.md +0 -80
- data/spec/common/model_new_validate_spec.rb +0 -204
@@ -1,16 +1,28 @@
|
|
1
|
-
require
|
1
|
+
require "pod4/sequel_interface"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "sequel"
|
4
|
+
require "date"
|
5
|
+
require "time"
|
6
|
+
require "bigdecimal"
|
7
7
|
|
8
|
-
require_relative
|
8
|
+
require_relative "../common/shared_examples_for_interface"
|
9
9
|
|
10
10
|
|
11
11
|
|
12
12
|
describe "SequelInterface" do
|
13
13
|
|
14
|
+
def fill_data(ifce)
|
15
|
+
data.each{|r| ifce.create(r) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def fill_product_data(ifce)
|
19
|
+
ifce.create( {code: "foo", name: "bar"} )
|
20
|
+
end
|
21
|
+
|
22
|
+
def list_contains(ifce, id)
|
23
|
+
ifce.list.find {|x| x[ifce.id_fld] == id }
|
24
|
+
end
|
25
|
+
|
14
26
|
let(:sequel_interface_class) do
|
15
27
|
Class.new SequelInterface do
|
16
28
|
set_table :customer
|
@@ -22,14 +34,14 @@ describe "SequelInterface" do
|
|
22
34
|
Class.new SequelInterface do
|
23
35
|
set_schema :public
|
24
36
|
set_table :customer
|
25
|
-
set_id_fld :id
|
37
|
+
set_id_fld :id, autoincrement: true
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
41
|
let(:prod_interface_class) do
|
30
42
|
Class.new SequelInterface do
|
31
43
|
set_table :product
|
32
|
-
set_id_fld :code
|
44
|
+
set_id_fld :code, autoincrement: false
|
33
45
|
end
|
34
46
|
end
|
35
47
|
|
@@ -45,43 +57,32 @@ describe "SequelInterface" do
|
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
48
|
-
|
49
|
-
|
50
60
|
let(:data) do
|
51
61
|
d = []
|
52
|
-
d << { name:
|
62
|
+
d << { name: "Barney",
|
53
63
|
level: 1.23,
|
54
64
|
day: Date.parse("2016-01-01"),
|
55
|
-
timestamp: Time.parse(
|
65
|
+
timestamp: Time.parse("2015-01-01 12:11"),
|
56
66
|
flag: true,
|
57
67
|
price: BigDecimal("1.24") }
|
58
68
|
|
59
|
-
d << { name:
|
69
|
+
d << { name: "Fred",
|
60
70
|
level: 2.34,
|
61
71
|
day: Date.parse("2016-02-02"),
|
62
|
-
timestamp: Time.parse(
|
72
|
+
timestamp: Time.parse("2015-01-02 12:22"),
|
63
73
|
flag: false,
|
64
74
|
price: BigDecimal("2.35") }
|
65
75
|
|
66
|
-
d << { name:
|
76
|
+
d << { name: "Betty",
|
67
77
|
level: 3.45,
|
68
78
|
day: Date.parse("2016-03-03"),
|
69
|
-
timestamp: Time.parse(
|
79
|
+
timestamp: Time.parse("2015-01-03 12:33"),
|
70
80
|
flag: nil,
|
71
81
|
price: BigDecimal("3.46") }
|
72
82
|
|
73
83
|
d
|
74
84
|
end
|
75
85
|
|
76
|
-
def fill_data(ifce)
|
77
|
-
data.each{|r| ifce.create(r) }
|
78
|
-
end
|
79
|
-
|
80
|
-
def fill_product_data(ifce)
|
81
|
-
ifce.create( {code: "foo", name: "bar"} )
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
86
|
# This is stolen almost verbatim from the Sequel Readme. We use an in-memory
|
86
87
|
# sqlite database, and we assume that Sequel is sane and behaves broadly the
|
87
88
|
# same for our limited purposes as it would when talking to TinyTDS or Pg.
|
@@ -110,15 +111,13 @@ describe "SequelInterface" do
|
|
110
111
|
let(:interface) { sequel_interface_class.new(db) }
|
111
112
|
let(:prod_interface) { prod_interface_class.new(db) }
|
112
113
|
|
113
|
-
before do
|
114
|
+
before(:each) do
|
114
115
|
fill_data(interface)
|
115
116
|
end
|
116
117
|
|
117
|
-
##
|
118
118
|
|
119
119
|
|
120
|
-
it_behaves_like
|
121
|
-
|
120
|
+
it_behaves_like "an interface" do
|
122
121
|
let(:interface) do
|
123
122
|
db2 = Sequel.sqlite
|
124
123
|
db2.create_table :customer do
|
@@ -134,109 +133,113 @@ describe "SequelInterface" do
|
|
134
133
|
sequel_interface_class.new(db2)
|
135
134
|
end
|
136
135
|
|
137
|
-
let(:record)
|
136
|
+
let(:record) { {name: "Barney", price: 1.11} }
|
138
137
|
end
|
139
|
-
##
|
140
138
|
|
141
139
|
|
142
|
-
describe
|
143
|
-
|
140
|
+
describe "SequelInterface.set_schema" do
|
141
|
+
|
142
|
+
it "takes one argument" do
|
144
143
|
expect( sequel_interface_class ).to respond_to(:set_schema).with(1).argument
|
145
144
|
end
|
145
|
+
|
146
146
|
end
|
147
|
-
##
|
148
147
|
|
149
148
|
|
150
|
-
describe
|
151
|
-
|
149
|
+
describe "SequelInterface.schema" do
|
150
|
+
|
151
|
+
it "returns the schema" do
|
152
152
|
expect( schema_interface_class.schema ).to eq :public
|
153
153
|
end
|
154
154
|
|
155
|
-
it
|
155
|
+
it "is optional" do
|
156
156
|
expect{ sequel_interface_class.schema }.not_to raise_exception
|
157
157
|
expect( sequel_interface_class.schema ).to eq nil
|
158
158
|
end
|
159
|
+
|
159
160
|
end
|
160
|
-
##
|
161
161
|
|
162
162
|
|
163
|
-
describe
|
164
|
-
|
163
|
+
describe "SequelInterface.set_table" do
|
164
|
+
|
165
|
+
it "takes one argument" do
|
165
166
|
expect( sequel_interface_class ).to respond_to(:set_table).with(1).argument
|
166
167
|
end
|
168
|
+
|
167
169
|
end
|
168
|
-
##
|
169
170
|
|
170
171
|
|
171
|
-
describe
|
172
|
-
|
172
|
+
describe "SequelInterface.table" do
|
173
|
+
|
174
|
+
it "returns the table" do
|
173
175
|
expect( sequel_interface_class.table ).to eq :customer
|
174
176
|
end
|
177
|
+
|
175
178
|
end
|
176
|
-
##
|
177
179
|
|
178
180
|
|
179
|
-
describe
|
180
|
-
|
181
|
+
describe "SequelInterface.set_id_fld" do
|
182
|
+
|
183
|
+
it "takes one argument" do
|
181
184
|
expect( sequel_interface_class ).to respond_to(:set_id_fld).with(1).argument
|
182
185
|
end
|
186
|
+
|
187
|
+
it "takes an optional second 'autoincrement' argument" do
|
188
|
+
expect{ prod_interface_class.set_id_fld(:foo, autoincrement: false) }.not_to raise_error
|
189
|
+
end
|
190
|
+
|
183
191
|
end
|
184
|
-
##
|
185
192
|
|
186
193
|
|
187
|
-
describe
|
188
|
-
|
194
|
+
describe "SequelInterface.id_fld" do
|
195
|
+
|
196
|
+
it "returns the ID field name" do
|
189
197
|
expect( sequel_interface_class.id_fld ).to eq :id
|
190
198
|
end
|
191
|
-
end
|
192
|
-
##
|
193
|
-
|
194
199
|
|
195
|
-
|
200
|
+
end
|
196
201
|
|
197
|
-
it 'requires a Sequel DB object' do
|
198
|
-
expect{ sequel_interface_class.new }.to raise_exception ArgumentError
|
199
|
-
expect{ sequel_interface_class.new(nil) }.to raise_exception ArgumentError
|
200
|
-
expect{ sequel_interface_class.new('foo') }.to raise_exception ArgumentError
|
201
202
|
|
202
|
-
|
203
|
-
|
203
|
+
describe "#new" do
|
204
|
+
# See also common/sequel_interface_pg_spec
|
204
205
|
|
205
|
-
it
|
206
|
+
it "requires the table and id field to be defined in the class" do
|
206
207
|
expect{ SequelInterface.new(db) }.to raise_exception Pod4Error
|
207
208
|
expect{ bad_interface_class1.new(db) }.to raise_exception Pod4Error
|
208
209
|
expect{ bad_interface_class2.new(db) }.to raise_exception Pod4Error
|
209
210
|
end
|
210
211
|
|
211
|
-
end
|
212
|
-
##
|
212
|
+
end # of #new
|
213
213
|
|
214
214
|
|
215
|
-
describe
|
215
|
+
describe "#quoted_table" do
|
216
216
|
|
217
|
-
it
|
217
|
+
it "returns just the table when the schema is not set" do
|
218
218
|
expect( interface.quoted_table ).to eq( %Q|`customer`| )
|
219
219
|
end
|
220
220
|
|
221
|
-
it
|
221
|
+
it "returns the schema plus table when the schema is set" do
|
222
222
|
ifce = schema_interface_class.new(db)
|
223
223
|
expect( ifce.quoted_table ).to eq( %|`public`.`customer`| )
|
224
224
|
end
|
225
225
|
|
226
|
-
end
|
227
|
-
##
|
226
|
+
end # of #quoted_table
|
228
227
|
|
229
228
|
|
230
|
-
describe
|
229
|
+
describe "#create" do
|
230
|
+
let(:hash) { {name: "Bam-Bam", price: 4.44} }
|
231
|
+
let(:ot) { Octothorpe.new(name: "Wilma", price: 5.55) }
|
231
232
|
|
232
|
-
|
233
|
-
|
233
|
+
it "raises a Pod4::DatabaseError if anything goes wrong" do
|
234
|
+
expect{ interface.create(one: "two") }.to raise_exception DatabaseError
|
235
|
+
end
|
234
236
|
|
235
|
-
it
|
236
|
-
|
237
|
+
it "raises an ArgumentError if ID field is missing in hash and not AI" do
|
238
|
+
hash = {name: "bar"}
|
239
|
+
expect{ prod_interface.create(Octothorpe.new hash) }.to raise_error ArgumentError
|
237
240
|
end
|
238
241
|
|
239
|
-
it
|
242
|
+
it "creates the record when given a hash" do
|
240
243
|
# kinda impossible to seperate these two tests
|
241
244
|
id = interface.create(hash)
|
242
245
|
|
@@ -245,7 +248,7 @@ describe "SequelInterface" do
|
|
245
248
|
expect( interface.read(id).to_h ).to include hash
|
246
249
|
end
|
247
250
|
|
248
|
-
it
|
251
|
+
it "creates the record when given an Octothorpe" do
|
249
252
|
id = interface.create(ot)
|
250
253
|
|
251
254
|
expect( id ).not_to be_nil
|
@@ -253,26 +256,26 @@ describe "SequelInterface" do
|
|
253
256
|
expect( interface.read(id).to_h ).to include ot.to_h
|
254
257
|
end
|
255
258
|
|
256
|
-
it
|
259
|
+
it "does not freak out if the hash has symbol values" do
|
257
260
|
# Which, Sequel does
|
258
261
|
expect{ interface.create(name: :Booboo) }.not_to raise_exception
|
259
262
|
end
|
260
263
|
|
261
|
-
it
|
262
|
-
record = {name:
|
264
|
+
it "has no problem with record values of nil" do
|
265
|
+
record = {name: "Ranger", price: nil}
|
263
266
|
expect{ interface.create(record) }.not_to raise_exception
|
264
267
|
id = interface.create(record)
|
265
268
|
expect( interface.read(id).to_h ).to include(record)
|
266
269
|
end
|
267
270
|
|
268
|
-
it
|
271
|
+
it "has no problem with strings containing special characters" do
|
269
272
|
record = {name: "T'Challa[]", price: nil}
|
270
273
|
expect{ interface.create(record) }.not_to raise_exception
|
271
274
|
id = interface.create(record)
|
272
275
|
expect( interface.read(id).to_h ).to include(record)
|
273
276
|
end
|
274
277
|
|
275
|
-
it
|
278
|
+
it "has no problem with non-integer keys" do
|
276
279
|
hash = {code: "foo", name: "bar"}
|
277
280
|
id = prod_interface.create( Octothorpe.new(hash) )
|
278
281
|
|
@@ -281,56 +284,65 @@ describe "SequelInterface" do
|
|
281
284
|
expect( prod_interface.read("foo").to_h ).to include hash
|
282
285
|
end
|
283
286
|
|
284
|
-
|
285
|
-
|
287
|
+
it "copes with an OT with the ID field in it when the ID field autoincrements" do
|
288
|
+
h = {id: nil, name: "Bam-Bam", price: 4.44}
|
289
|
+
expect{ interface.create(h) }.not_to raise_error
|
290
|
+
|
291
|
+
id = interface.create(h)
|
292
|
+
expect( id ).not_to be_nil
|
293
|
+
expect{ interface.read(id) }.not_to raise_exception
|
294
|
+
expect( interface.read(id).to_h ).to include( {name: "Bam-Bam", price: 4.44} )
|
295
|
+
end
|
286
296
|
|
297
|
+
end # of #create
|
287
298
|
|
288
|
-
describe '#read' do
|
289
299
|
|
290
|
-
|
291
|
-
|
300
|
+
describe "#read" do
|
301
|
+
|
302
|
+
it "returns the record for the id as an Octothorpe" do
|
303
|
+
expect( interface.read(2).to_h ).to include(name: "Fred", price: 2.35)
|
292
304
|
end
|
293
305
|
|
294
|
-
it
|
306
|
+
it "raises a Pod4::CantContinue if the ID is bad" do
|
295
307
|
expect{ interface.read(:foo) }.to raise_exception CantContinue
|
296
308
|
end
|
297
309
|
|
298
|
-
it
|
310
|
+
it "returns an empty Octothorpe if no record matches the ID" do
|
299
311
|
expect{ interface.read(99) }.not_to raise_exception
|
300
312
|
expect( interface.read(99) ).to be_a_kind_of Octothorpe
|
301
313
|
expect( interface.read(99) ).to be_empty
|
302
314
|
end
|
303
315
|
|
304
|
-
it
|
316
|
+
it "returns real fields as Float" do
|
305
317
|
level = interface.read(1).>>.level
|
306
318
|
|
307
319
|
expect( level ).to be_a_kind_of Float
|
308
320
|
expect( level ).to be_within(0.001).of( data.first[:level] )
|
309
321
|
end
|
310
322
|
|
311
|
-
it
|
323
|
+
it "returns date fields as Date" do
|
312
324
|
date = interface.read(1).>>.day
|
313
325
|
|
314
326
|
expect( date ).to be_a_kind_of Date
|
315
327
|
expect( date ).to eq data.first[:day]
|
316
328
|
end
|
317
329
|
|
318
|
-
it
|
330
|
+
it "returns datetime fields as Time" do
|
319
331
|
timestamp = interface.read(1).>>.timestamp
|
320
332
|
|
321
333
|
expect( timestamp ).to be_a_kind_of Time
|
322
334
|
expect( timestamp ).to eq data.first[:timestamp]
|
323
335
|
end
|
324
336
|
|
325
|
-
it
|
337
|
+
it "returns numeric fields as BigDecimal" do
|
326
338
|
price = interface.read(1).>>.price
|
327
339
|
|
328
340
|
expect( price ).to be_a_kind_of BigDecimal
|
329
341
|
expect( price ).to eq data.first[:price]
|
330
342
|
end
|
331
343
|
|
332
|
-
# Not sure how this passes since SQLite doesn
|
333
|
-
it
|
344
|
+
# Not sure how this passes since SQLite doesn"t have a boolean class, but, Sequel handles it.
|
345
|
+
it "returns boolean fields as boolean" do
|
334
346
|
[1,2,3].each do |i|
|
335
347
|
flag = interface.read(i).>>.flag
|
336
348
|
expect( [true, false, nil].include? flag ).to be true
|
@@ -338,7 +350,7 @@ describe "SequelInterface" do
|
|
338
350
|
end
|
339
351
|
end
|
340
352
|
|
341
|
-
it
|
353
|
+
it "has no problem with non-integer keys" do
|
342
354
|
# this is a 100% overlap with the create test above...
|
343
355
|
fill_product_data(prod_interface)
|
344
356
|
|
@@ -346,19 +358,17 @@ describe "SequelInterface" do
|
|
346
358
|
expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
|
347
359
|
end
|
348
360
|
|
349
|
-
end
|
350
|
-
##
|
351
|
-
|
361
|
+
end # of #read
|
352
362
|
|
353
363
|
|
354
|
-
describe
|
364
|
+
describe "#list" do
|
355
365
|
|
356
|
-
it
|
366
|
+
it "has an optional selection parameter, a hash" do
|
357
367
|
# Actually it does not have to be a hash, but FTTB we only support that.
|
358
|
-
expect{ interface.list(name:
|
368
|
+
expect{ interface.list(name: "Barney") }.not_to raise_exception
|
359
369
|
end
|
360
370
|
|
361
|
-
it
|
371
|
+
it "returns an array of Octothorpes that match the records" do
|
362
372
|
arr = interface.list.map {|ot| x = ot.to_h}
|
363
373
|
|
364
374
|
expect( arr.size ).to eq(data.size)
|
@@ -371,47 +381,42 @@ describe "SequelInterface" do
|
|
371
381
|
expect( r[:timestamp] ).to eq d[:timestamp]
|
372
382
|
expect( r[:qty] ).to eq d[:qty]
|
373
383
|
end
|
374
|
-
|
375
384
|
end
|
376
385
|
|
386
|
+
it "returns a subset of records based on the selection parameter" do
|
387
|
+
expect( interface.list(name: "Fred").size ).to eq 1
|
377
388
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
expect( interface.list(name: 'Betty').first.to_h ).
|
382
|
-
to include(name: 'Betty', price: 3.46)
|
389
|
+
expect( interface.list(name: "Betty").first.to_h ).
|
390
|
+
to include(name: "Betty", price: 3.46)
|
383
391
|
|
384
392
|
end
|
385
393
|
|
386
|
-
it
|
387
|
-
expect( interface.list(name:
|
394
|
+
it "returns an empty Array if nothing matches" do
|
395
|
+
expect( interface.list(name: "Yogi") ).to eq([])
|
388
396
|
end
|
389
397
|
|
390
|
-
it
|
391
|
-
expect{ interface.list(
|
398
|
+
it "raises DatabaseError if the selection criteria is nonsensical" do
|
399
|
+
expect{ interface.list("foo") }.to raise_exception Pod4::DatabaseError
|
392
400
|
end
|
393
401
|
|
394
|
-
it
|
402
|
+
it "returns an empty array if there is no data" do
|
395
403
|
interface.list.each {|x| interface.delete(x[interface.id_fld]) }
|
396
404
|
expect( interface.list ).to eq([])
|
397
405
|
end
|
398
406
|
|
399
|
-
it
|
407
|
+
it "does not freak out if the hash has symbol values" do
|
400
408
|
# Which, Sequel does
|
401
409
|
expect{ interface.list(name: :Barney) }.not_to raise_exception
|
402
410
|
end
|
403
411
|
|
404
|
-
|
405
|
-
end
|
406
|
-
##
|
412
|
+
end # of #list
|
407
413
|
|
408
414
|
|
409
|
-
describe
|
410
|
-
|
415
|
+
describe "#update" do
|
411
416
|
let(:id) { interface.list.first[:id] }
|
412
417
|
|
413
|
-
it
|
414
|
-
record = {name:
|
418
|
+
it "updates the record at ID with record parameter" do
|
419
|
+
record = {name: "Booboo", price: 99.99}
|
415
420
|
interface.update(id, record)
|
416
421
|
|
417
422
|
booboo = interface.read(id)
|
@@ -419,203 +424,195 @@ describe "SequelInterface" do
|
|
419
424
|
expect( booboo.>>.price.to_f ).to eq( record[:price] )
|
420
425
|
end
|
421
426
|
|
422
|
-
it
|
423
|
-
expect{ interface.update(99, name:
|
427
|
+
it "raises a CantContinue if anything weird happens with the ID" do
|
428
|
+
expect{ interface.update(99, name: "Booboo") }.
|
424
429
|
to raise_exception CantContinue
|
425
430
|
|
426
431
|
end
|
427
432
|
|
428
|
-
it
|
429
|
-
expect{ interface.update(id, smarts:
|
433
|
+
it "raises a DatabaseError if anything weird happens with the record" do
|
434
|
+
expect{ interface.update(id, smarts: "more") }.
|
430
435
|
to raise_exception DatabaseError
|
431
436
|
|
432
437
|
end
|
433
438
|
|
434
|
-
it
|
439
|
+
it "does not freak out if the hash has symbol values" do
|
435
440
|
# Which, Sequel does
|
436
441
|
expect{ interface.update(id, name: :Booboo) }.not_to raise_exception
|
437
442
|
end
|
438
443
|
|
439
|
-
it
|
440
|
-
record = {name:
|
444
|
+
it "has no problem with record values of nil" do
|
445
|
+
record = {name: "Ranger", price: nil}
|
441
446
|
expect{ interface.update(id, record) }.not_to raise_exception
|
442
447
|
expect( interface.read(id).to_h ).to include(record)
|
443
448
|
end
|
444
449
|
|
445
|
-
it
|
450
|
+
it "has no problem with strings containing special characters" do
|
446
451
|
record = {name: "T'Challa[]", price: nil}
|
447
452
|
expect{ interface.update(id, record) }.not_to raise_exception
|
448
453
|
expect( interface.read(id).to_h ).to include(record)
|
449
454
|
end
|
450
455
|
|
451
|
-
it
|
456
|
+
it "has no problem with non-integer keys" do
|
452
457
|
fill_product_data(prod_interface)
|
453
458
|
expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
|
454
459
|
expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
|
455
460
|
end
|
456
461
|
|
457
|
-
end
|
458
|
-
##
|
459
|
-
|
462
|
+
end # of #update
|
460
463
|
|
461
|
-
describe '#delete' do
|
462
464
|
|
463
|
-
|
464
|
-
ifce.list.find {|x| x[ifce.id_fld] == id }
|
465
|
-
end
|
465
|
+
describe "#delete" do
|
466
466
|
|
467
467
|
let(:id) { interface.list.first[:id] }
|
468
468
|
|
469
|
-
it
|
469
|
+
it "raises CantContinue if anything hinky happens with the ID" do
|
470
470
|
expect{ interface.delete(:foo) }.to raise_exception CantContinue
|
471
471
|
expect{ interface.delete(99) }.to raise_exception CantContinue
|
472
472
|
end
|
473
473
|
|
474
|
-
it
|
474
|
+
it "makes the record at ID go away" do
|
475
475
|
expect( list_contains(interface, id) ).to be_truthy
|
476
476
|
interface.delete(id)
|
477
477
|
expect( list_contains(interface, id) ).to be_falsy
|
478
478
|
end
|
479
479
|
|
480
|
-
it
|
480
|
+
it "has no problem with non-integer keys" do
|
481
481
|
fill_product_data(prod_interface)
|
482
482
|
expect( list_contains(prod_interface, "foo") ).to be_truthy
|
483
483
|
prod_interface.delete("foo")
|
484
484
|
expect( list_contains(prod_interface, "foo") ).to be_falsy
|
485
485
|
end
|
486
486
|
|
487
|
-
end
|
488
|
-
##
|
487
|
+
end # of #delete
|
489
488
|
|
490
489
|
|
491
|
-
describe
|
490
|
+
describe "#execute" do
|
492
491
|
|
493
|
-
let(:sql) {
|
492
|
+
let(:sql) { "delete from customer where price < 2.0;" }
|
494
493
|
|
495
|
-
it
|
494
|
+
it "requires an SQL string" do
|
496
495
|
expect{ interface.execute }.to raise_exception ArgumentError
|
497
496
|
expect{ interface.execute(nil) }.to raise_exception ArgumentError
|
498
497
|
expect{ interface.execute(14) }.to raise_exception ArgumentError
|
499
498
|
end
|
500
499
|
|
501
|
-
it
|
502
|
-
expect{ interface.execute(
|
500
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
501
|
+
expect{ interface.execute("delete from not_a_table") }.
|
503
502
|
to raise_exception Pod4Error
|
504
503
|
|
505
504
|
end
|
506
505
|
|
507
|
-
it
|
506
|
+
it "executes the string" do
|
508
507
|
expect{ interface.execute(sql) }.not_to raise_exception
|
509
508
|
expect( interface.list.size ).to eq(data.size - 1)
|
510
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
509
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
511
510
|
end
|
512
511
|
|
513
|
-
end
|
514
|
-
##
|
512
|
+
end # of #execute
|
515
513
|
|
516
514
|
|
517
|
-
describe
|
515
|
+
describe "#select" do
|
518
516
|
|
519
|
-
it
|
517
|
+
it "requires an SQL string" do
|
520
518
|
expect{ interface.select }.to raise_exception ArgumentError
|
521
519
|
expect{ interface.select(nil) }.to raise_exception ArgumentError
|
522
520
|
expect{ interface.select(14) }.to raise_exception ArgumentError
|
523
521
|
end
|
524
522
|
|
525
|
-
it
|
526
|
-
expect{ interface.select(
|
523
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
524
|
+
expect{ interface.select("select * from not_a_table") }.
|
527
525
|
to raise_exception Pod4Error
|
528
526
|
|
529
527
|
end
|
530
528
|
|
531
|
-
it
|
532
|
-
sql1 =
|
533
|
-
sql2 =
|
529
|
+
it "returns the result of the sql" do
|
530
|
+
sql1 = "select name from customer where price < 2.0;"
|
531
|
+
sql2 = "select name from customer where price < 0.0;"
|
534
532
|
|
535
533
|
expect{ interface.select(sql1) }.not_to raise_exception
|
536
|
-
expect( interface.select(sql1) ).to eq( [{name:
|
534
|
+
expect( interface.select(sql1) ).to eq( [{name: "Barney"}] )
|
537
535
|
expect( interface.select(sql2) ).to eq( [] )
|
538
536
|
end
|
539
537
|
|
540
|
-
it
|
538
|
+
it "works if you pass a non-select" do
|
541
539
|
# By which I mean: still executes the SQL; returns []
|
542
|
-
sql =
|
540
|
+
sql = "delete from customer where price < 2.0;"
|
543
541
|
ret = interface.select(sql)
|
544
542
|
|
545
543
|
expect( interface.list.size ).to eq(data.size - 1)
|
546
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
544
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
547
545
|
expect( ret ).to eq( [] )
|
548
546
|
end
|
549
547
|
|
550
|
-
end
|
551
|
-
##
|
548
|
+
end # of #select
|
552
549
|
|
553
550
|
|
554
551
|
describe "#executep" do
|
555
552
|
# For the time being lets assume that Sequel does its job and the three modes we are calling
|
556
553
|
# actually work
|
557
554
|
|
558
|
-
let(:sql) {
|
555
|
+
let(:sql) { "delete from customer where price < ?;" }
|
559
556
|
|
560
|
-
it
|
557
|
+
it "requires an SQL string and a mode" do
|
561
558
|
expect{ interface.executep }.to raise_exception ArgumentError
|
562
559
|
expect{ interface.executep(nil) }.to raise_exception ArgumentError
|
563
560
|
expect{ interface.executep(14, :update) }.to raise_exception ArgumentError
|
564
561
|
expect{ interface.executep(14, :update, 2) }.to raise_exception ArgumentError
|
565
562
|
end
|
566
563
|
|
567
|
-
it
|
564
|
+
it "requires the mode to be valid" do
|
568
565
|
expect{ interface.executep(sql, :foo, 2) }.to raise_exception ArgumentError
|
569
566
|
end
|
570
567
|
|
571
|
-
it
|
572
|
-
expect{ interface.executep(
|
568
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
569
|
+
expect{ interface.executep("delete from not_a_table where thingy = ?", :delete, 14) }.
|
573
570
|
to raise_exception Pod4Error
|
574
571
|
|
575
572
|
end
|
576
573
|
|
577
|
-
it
|
574
|
+
it "executes the string" do
|
578
575
|
expect{ interface.executep(sql, :delete, 2.0) }.not_to raise_exception
|
579
576
|
expect( interface.list.size ).to eq(data.size - 1)
|
580
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
577
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
581
578
|
end
|
582
579
|
|
583
|
-
end
|
580
|
+
end # of #executep
|
584
581
|
|
585
582
|
|
586
583
|
describe "#selectp" do
|
587
584
|
|
588
|
-
it
|
585
|
+
it "requires an SQL string" do
|
589
586
|
expect{ interface.selectp }.to raise_exception ArgumentError
|
590
587
|
expect{ interface.selectp(nil) }.to raise_exception ArgumentError
|
591
588
|
expect{ interface.selectp(14) }.to raise_exception ArgumentError
|
592
589
|
end
|
593
590
|
|
594
|
-
it
|
595
|
-
expect{ interface.selectp(
|
591
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
592
|
+
expect{ interface.selectp("select * from not_a_table where thingy = ?", 14) }.
|
596
593
|
to raise_exception Pod4Error
|
597
594
|
|
598
595
|
end
|
599
596
|
|
600
|
-
it
|
601
|
-
sql =
|
597
|
+
it "returns the result of the sql" do
|
598
|
+
sql = "select name from customer where price < ?;"
|
602
599
|
|
603
600
|
expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
|
604
|
-
expect( interface.selectp(sql, 2.0) ).to eq( [{name:
|
601
|
+
expect( interface.selectp(sql, 2.0) ).to eq( [{name: "Barney"}] )
|
605
602
|
expect( interface.selectp(sql, 0.0) ).to eq( [] )
|
606
603
|
end
|
607
604
|
|
608
|
-
it
|
605
|
+
it "works if you pass a non-select" do
|
609
606
|
# By which I mean: still executes the SQL; returns []
|
610
|
-
sql =
|
607
|
+
sql = "delete from customer where price < ?;"
|
611
608
|
ret = interface.selectp(sql, 2.0)
|
612
609
|
|
613
610
|
expect( interface.list.size ).to eq(data.size - 1)
|
614
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
611
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
615
612
|
expect( ret ).to eq( [] )
|
616
613
|
end
|
617
614
|
|
618
|
-
end
|
615
|
+
end # of #selectp
|
619
616
|
|
620
617
|
|
621
618
|
end
|