pod4 0.10.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,53 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require "pod4/tds_interface"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "tiny_tds"
|
4
|
+
require "date"
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "../common/shared_examples_for_interface"
|
7
|
+
require_relative "../fixtures/database"
|
8
8
|
|
9
9
|
|
10
10
|
describe "TdsInterface" do
|
11
11
|
|
12
|
-
let(:tds_interface_class) do
|
13
|
-
Class.new TdsInterface do
|
14
|
-
set_db :pod4_test
|
15
|
-
set_table :customer
|
16
|
-
set_id_fld :id
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
let(:schema_interface_class) do
|
21
|
-
Class.new TdsInterface do
|
22
|
-
set_db :pod4_test
|
23
|
-
set_schema :public
|
24
|
-
set_table :customer
|
25
|
-
set_id_fld :id
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
let(:bad_interface_class1) do
|
30
|
-
Class.new TdsInterface do
|
31
|
-
set_db :pod4_test
|
32
|
-
set_table :customer
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:bad_interface_class2) do
|
37
|
-
Class.new TdsInterface do
|
38
|
-
set_db :pod4_test
|
39
|
-
set_id_fld :id
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
let(:prod_interface_class) do
|
44
|
-
Class.new TdsInterface do
|
45
|
-
set_db :pod4_test
|
46
|
-
set_table :product
|
47
|
-
set_id_fld :code
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
12
|
def db_setup(connect)
|
52
13
|
client = TinyTds::Client.new(connect)
|
53
14
|
client.execute(%Q|use [pod4_test];|).do
|
@@ -82,7 +43,6 @@ describe "TdsInterface" do
|
|
82
43
|
client.close if client
|
83
44
|
end
|
84
45
|
|
85
|
-
|
86
46
|
def fill_data(ifce)
|
87
47
|
@data.each{|r| ifce.create(r) }
|
88
48
|
end
|
@@ -91,185 +51,261 @@ describe "TdsInterface" do
|
|
91
51
|
ifce.create( {code: "foo", name: "bar"} )
|
92
52
|
end
|
93
53
|
|
54
|
+
def float_price(row)
|
55
|
+
row[:price] = row[:price].to_f
|
56
|
+
row
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_contains(ifce, id)
|
60
|
+
ifce.list.find {|x| x[ifce.id_fld] == id }
|
61
|
+
end
|
62
|
+
|
63
|
+
let(:tds_interface_class) do
|
64
|
+
Class.new TdsInterface do
|
65
|
+
set_db :pod4_test
|
66
|
+
set_table :customer
|
67
|
+
set_id_fld :id
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:schema_interface_class) do
|
72
|
+
Class.new TdsInterface do
|
73
|
+
set_db :pod4_test
|
74
|
+
set_schema :public
|
75
|
+
set_table :customer
|
76
|
+
set_id_fld :id, autoincrement: true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
let(:bad_interface_class1) do
|
81
|
+
Class.new TdsInterface do
|
82
|
+
set_table :customer
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
let(:bad_interface_class2) do
|
87
|
+
Class.new TdsInterface do
|
88
|
+
set_db :pod4_test
|
89
|
+
set_id_fld :id
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
let(:prod_interface_class) do
|
94
|
+
Class.new TdsInterface do
|
95
|
+
set_db :pod4_test
|
96
|
+
set_table :product
|
97
|
+
set_id_fld :code, autoincrement: false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
let(:interface) do
|
102
|
+
tds_interface_class.new(@connect_hash)
|
103
|
+
end
|
104
|
+
|
105
|
+
let(:prod_interface) do
|
106
|
+
prod_interface_class.new(@connect_hash)
|
107
|
+
end
|
94
108
|
|
95
109
|
before(:all) do
|
96
110
|
@connect_hash = DB[:tds]
|
97
111
|
db_setup(@connect_hash)
|
98
112
|
|
99
113
|
@data = []
|
100
|
-
@data << { name:
|
114
|
+
@data << { name: "Barney",
|
101
115
|
level: 1.23,
|
102
116
|
day: Date.parse("2016-01-01"),
|
103
|
-
timestamp: Time.parse(
|
117
|
+
timestamp: Time.parse("2015-01-01 12:11"),
|
104
118
|
price: BigDecimal("1.24"),
|
105
119
|
qty: BigDecimal("1.25") }
|
106
120
|
|
107
|
-
@data << { name:
|
121
|
+
@data << { name: "Fred",
|
108
122
|
level: 2.34,
|
109
123
|
day: Date.parse("2016-02-02"),
|
110
|
-
timestamp: Time.parse(
|
124
|
+
timestamp: Time.parse("2015-01-02 12:22"),
|
111
125
|
price: BigDecimal("2.35"),
|
112
126
|
qty: BigDecimal("2.36") }
|
113
127
|
|
114
|
-
@data << { name:
|
128
|
+
@data << { name: "Betty",
|
115
129
|
level: 3.45,
|
116
130
|
day: Date.parse("2016-03-03"),
|
117
|
-
timestamp: Time.parse(
|
131
|
+
timestamp: Time.parse("2015-01-03 12:33"),
|
118
132
|
price: BigDecimal("3.46"),
|
119
133
|
qty: BigDecimal("3.47") }
|
120
134
|
|
121
135
|
end
|
122
136
|
|
123
|
-
|
124
|
-
before do
|
137
|
+
before(:each) do
|
125
138
|
# TRUNCATE TABLE also resets the identity counter
|
126
139
|
interface.execute(%Q|truncate table customer;|)
|
127
140
|
end
|
128
141
|
|
129
142
|
|
130
|
-
let(:interface) do
|
131
|
-
tds_interface_class.new(@connect_hash)
|
132
|
-
end
|
133
|
-
|
134
|
-
let(:prod_interface) do
|
135
|
-
prod_interface_class.new(@connect_hash)
|
136
|
-
end
|
137
|
-
|
138
|
-
#####
|
139
|
-
|
140
|
-
|
141
|
-
it_behaves_like 'an interface' do
|
142
143
|
|
144
|
+
it_behaves_like "an interface" do
|
143
145
|
let(:interface) do
|
144
146
|
tds_interface_class.new(@connect_hash)
|
145
147
|
end
|
146
148
|
|
147
|
-
|
148
|
-
let(:record) { {name: 'Barney'} }
|
149
|
+
let(:record) { {name: "Barney"} }
|
149
150
|
|
150
151
|
end
|
151
|
-
##
|
152
152
|
|
153
153
|
|
154
|
-
describe
|
155
|
-
|
154
|
+
describe "TdsInterface.set_db" do
|
155
|
+
|
156
|
+
it "takes one argument" do
|
156
157
|
expect( TdsInterface ).to respond_to(:set_db).with(1).argument
|
157
158
|
end
|
159
|
+
|
158
160
|
end
|
159
|
-
##
|
160
161
|
|
161
162
|
|
162
|
-
describe
|
163
|
-
|
163
|
+
describe "TdsInterface.db" do
|
164
|
+
|
165
|
+
it "returns the table" do
|
164
166
|
expect( tds_interface_class.db ).to eq :pod4_test
|
165
167
|
end
|
168
|
+
|
166
169
|
end
|
167
|
-
##
|
168
170
|
|
169
171
|
|
170
|
-
describe
|
171
|
-
|
172
|
+
describe "TdsInterface.set_schema" do
|
173
|
+
|
174
|
+
it "takes one argument" do
|
172
175
|
expect( TdsInterface ).to respond_to(:set_schema).with(1).argument
|
173
176
|
end
|
177
|
+
|
174
178
|
end
|
175
|
-
##
|
176
179
|
|
177
180
|
|
178
|
-
describe
|
179
|
-
|
181
|
+
describe "TdsInterface.schema" do
|
182
|
+
|
183
|
+
it "returns the schema" do
|
180
184
|
expect( schema_interface_class.schema ).to eq :public
|
181
185
|
end
|
182
186
|
|
183
|
-
it
|
187
|
+
it "is optional" do
|
184
188
|
expect{ tds_interface_class.schema }.not_to raise_exception
|
185
189
|
expect( tds_interface_class.schema ).to eq nil
|
186
190
|
end
|
191
|
+
|
187
192
|
end
|
188
|
-
##
|
189
193
|
|
190
194
|
|
191
|
-
describe
|
192
|
-
|
195
|
+
describe "TdsInterface.set_table" do
|
196
|
+
|
197
|
+
it "takes one argument" do
|
193
198
|
expect( TdsInterface ).to respond_to(:set_table).with(1).argument
|
194
199
|
end
|
200
|
+
|
195
201
|
end
|
196
|
-
##
|
197
202
|
|
198
203
|
|
199
|
-
describe
|
200
|
-
|
204
|
+
describe "TdsInterface.table" do
|
205
|
+
|
206
|
+
it "returns the table" do
|
201
207
|
expect( tds_interface_class.table ).to eq :customer
|
202
208
|
end
|
209
|
+
|
203
210
|
end
|
204
|
-
##
|
205
211
|
|
206
212
|
|
207
|
-
describe
|
208
|
-
|
213
|
+
describe "TdsInterface.set_id_fld" do
|
214
|
+
|
215
|
+
it "takes one argument" do
|
209
216
|
expect( TdsInterface ).to respond_to(:set_id_fld).with(1).argument
|
210
217
|
end
|
218
|
+
|
219
|
+
it "takes an optional second 'autoincrement' argument" do
|
220
|
+
expect{ TdsInterface.set_id_fld(:foo, autoincrement: false) }.not_to raise_error
|
221
|
+
end
|
222
|
+
|
211
223
|
end
|
212
|
-
##
|
213
224
|
|
214
225
|
|
215
|
-
describe
|
216
|
-
|
226
|
+
describe "TdsInterface.id_ai" do
|
227
|
+
|
228
|
+
it "returns true if autoincrement is true" do
|
229
|
+
expect( schema_interface_class.id_ai ).to eq true
|
230
|
+
end
|
231
|
+
|
232
|
+
it "returns false if autoincrement is false" do
|
233
|
+
expect( prod_interface_class.id_ai ).to eq false
|
234
|
+
end
|
235
|
+
|
236
|
+
it "returns true if autoincrement is not specified" do
|
237
|
+
expect( tds_interface_class.id_ai ).to eq true
|
238
|
+
end
|
239
|
+
|
240
|
+
end # of PgInterface.id_ai
|
241
|
+
|
242
|
+
|
243
|
+
describe "TdsInterface.id_fld" do
|
244
|
+
|
245
|
+
it "returns the ID field name" do
|
217
246
|
expect( tds_interface_class.id_fld ).to eq :id
|
218
247
|
end
|
219
|
-
end
|
220
|
-
##
|
221
248
|
|
249
|
+
end
|
222
250
|
|
223
|
-
describe '#new' do
|
224
251
|
|
225
|
-
|
226
|
-
expect{ tds_interface_class.new }.to raise_exception ArgumentError
|
227
|
-
expect{ tds_interface_class.new(nil) }.to raise_exception ArgumentError
|
228
|
-
expect{ tds_interface_class.new('foo') }.to raise_exception ArgumentError
|
252
|
+
describe "#new" do
|
229
253
|
|
230
|
-
|
254
|
+
it "creates a ConnectionPool when passed a TDS connection hash" do
|
255
|
+
ifce = tds_interface_class.new(@connect_hash)
|
256
|
+
expect( ifce._connection ).to be_a ConnectionPool
|
231
257
|
end
|
232
258
|
|
233
|
-
it
|
234
|
-
|
259
|
+
it "uses the ConnectionPool when given one" do
|
260
|
+
pool = ConnectionPool.new(interface: tds_interface_class)
|
261
|
+
ifce = tds_interface_class.new(pool)
|
235
262
|
|
236
|
-
expect
|
237
|
-
|
263
|
+
expect( ifce._connection ).to eq pool
|
264
|
+
end
|
238
265
|
|
239
|
-
|
240
|
-
to raise_exception
|
266
|
+
it "raises ArgumentError when passed something else" do
|
267
|
+
expect{ tds_interface_class.new }.to raise_exception ArgumentError
|
268
|
+
expect{ tds_interface_class.new(nil) }.to raise_exception ArgumentError
|
269
|
+
expect{ tds_interface_class.new("foo") }.to raise_exception ArgumentError
|
270
|
+
end
|
241
271
|
|
272
|
+
it "requires the table and id field to be defined in the class" do
|
273
|
+
expect{ TdsInterface.new(@connect_hash) }.to raise_exception Pod4Error
|
274
|
+
expect{ bad_interface_class1.new(@connect_hash) }.to raise_exception Pod4Error
|
275
|
+
expect{ bad_interface_class2.new(@connect_hash) }.to raise_exception Pod4Error
|
242
276
|
end
|
243
277
|
|
244
|
-
end
|
245
|
-
##
|
278
|
+
end # of #new
|
246
279
|
|
247
280
|
|
248
|
-
describe
|
281
|
+
describe "#quoted_table" do
|
249
282
|
|
250
|
-
it
|
283
|
+
it "returns just the table when the schema is not set" do
|
251
284
|
expect( interface.quoted_table ).to eq( %Q|[customer]| )
|
252
285
|
end
|
253
286
|
|
254
|
-
it
|
287
|
+
it "returns the schema plus table when the schema is set" do
|
255
288
|
ifce = schema_interface_class.new(@connect_hash)
|
256
289
|
expect( ifce.quoted_table ).to eq( %|[public].[customer]| )
|
257
290
|
end
|
258
291
|
|
259
|
-
end
|
260
|
-
##
|
292
|
+
end # of #quoted_table
|
261
293
|
|
262
294
|
|
263
|
-
describe
|
295
|
+
describe "#create" do
|
296
|
+
let(:hash) { {name: "Bam-Bam", price: 4.44} }
|
297
|
+
let(:ot) { Octothorpe.new(name: "Wilma", price: 5.55) }
|
264
298
|
|
265
|
-
|
266
|
-
|
299
|
+
it "raises a Pod4::DatabaseError if anything goes wrong" do
|
300
|
+
expect{ interface.create(one: "two") }.to raise_exception DatabaseError
|
301
|
+
end
|
267
302
|
|
268
|
-
it
|
269
|
-
|
303
|
+
it "raises an ArgumentError if ID field is missing in hash and not AI" do
|
304
|
+
hash = {name: "bar"}
|
305
|
+
expect{ prod_interface.create(Octothorpe.new hash) }.to raise_error ArgumentError
|
270
306
|
end
|
271
307
|
|
272
|
-
it
|
308
|
+
it "creates the record when given a hash" do
|
273
309
|
# kinda impossible to seperate these two tests
|
274
310
|
id = interface.create(hash)
|
275
311
|
|
@@ -277,28 +313,28 @@ describe "TdsInterface" do
|
|
277
313
|
expect( interface.read(id).to_h ).to include hash
|
278
314
|
end
|
279
315
|
|
280
|
-
it
|
316
|
+
it "creates the record when given an Octothorpe" do
|
281
317
|
id = interface.create(ot)
|
282
318
|
|
283
319
|
expect{ interface.read(id) }.not_to raise_exception
|
284
320
|
expect( interface.read(id).to_h ).to include ot.to_h
|
285
321
|
end
|
286
322
|
|
287
|
-
it
|
288
|
-
hash2 = {name:
|
323
|
+
it "has no problem with record values of nil" do
|
324
|
+
hash2 = {name: "Ranger", price: nil}
|
289
325
|
expect{ interface.create(hash2) }.not_to raise_exception
|
290
326
|
id = interface.create(hash2)
|
291
327
|
expect( interface.read(id).to_h ).to include(hash2)
|
292
328
|
end
|
293
329
|
|
294
|
-
it
|
330
|
+
it "has no problem with strings containing special characters" do
|
295
331
|
hash2 = {name: "T'Challa[]", price: nil}
|
296
332
|
expect{ interface.create(hash2) }.not_to raise_exception
|
297
333
|
id = interface.create(hash2)
|
298
334
|
expect( interface.read(id).to_h ).to include(hash2)
|
299
335
|
end
|
300
336
|
|
301
|
-
it
|
337
|
+
it "has no problem with non-integer keys" do
|
302
338
|
hash = {code: "foo", name: "bar"}
|
303
339
|
id = prod_interface.create( Octothorpe.new(hash) )
|
304
340
|
|
@@ -307,37 +343,51 @@ describe "TdsInterface" do
|
|
307
343
|
expect( prod_interface.read("foo").to_h ).to include hash
|
308
344
|
end
|
309
345
|
|
310
|
-
|
311
|
-
|
346
|
+
it "calls ConnectionPool#client" do
|
347
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
348
|
+
interface.create(ot)
|
349
|
+
end
|
312
350
|
|
351
|
+
it "copes with an OT with the ID field in it when the ID field autoincrements" do
|
352
|
+
h = {id: nil, name: "Bam-Bam", qty: 4.44}
|
353
|
+
expect{ interface.create(h) }.not_to raise_error
|
313
354
|
|
314
|
-
|
315
|
-
|
355
|
+
id = interface.create(h)
|
356
|
+
expect( id ).not_to be_nil
|
357
|
+
expect{ interface.read(id) }.not_to raise_exception
|
358
|
+
expect( interface.read(id).to_h ).to include( {name: "Bam-Bam", qty: 4.44} )
|
359
|
+
end
|
316
360
|
|
317
|
-
|
361
|
+
end # of #create
|
362
|
+
|
363
|
+
|
364
|
+
describe "#read" do
|
365
|
+
before(:each) { fill_data(interface) }
|
366
|
+
|
367
|
+
it "returns the record for the id as an Octothorpe" do
|
318
368
|
rec = interface.read(2)
|
319
369
|
expect( rec ).to be_a_kind_of Octothorpe
|
320
|
-
expect( rec.>>.name ).to eq
|
370
|
+
expect( rec.>>.name ).to eq "Fred"
|
321
371
|
end
|
322
372
|
|
323
|
-
it
|
373
|
+
it "raises a Pod4::CantContinue if anything goes wrong with the ID" do
|
324
374
|
expect{ interface.read(:foo) }.to raise_exception CantContinue
|
325
375
|
end
|
326
376
|
|
327
|
-
it
|
377
|
+
it "returns an empty Octothorpe if no record matches the ID" do
|
328
378
|
expect{ interface.read(99) }.not_to raise_exception
|
329
379
|
expect( interface.read(99) ).to be_a_kind_of Octothorpe
|
330
380
|
expect( interface.read(99) ).to be_empty
|
331
381
|
end
|
332
382
|
|
333
|
-
it
|
383
|
+
it "returns real fields as Float" do
|
334
384
|
level = interface.read(1).>>.level
|
335
385
|
|
336
386
|
expect( level ).to be_a_kind_of Float
|
337
387
|
expect( level ).to be_within(0.001).of( @data.first[:level] )
|
338
388
|
end
|
339
389
|
|
340
|
-
it
|
390
|
+
it "returns date fields as Date" do
|
341
391
|
skip "not supported by TinyTds ¬_¬ "
|
342
392
|
date = interface.read(1).>>.day
|
343
393
|
|
@@ -345,28 +395,28 @@ describe "TdsInterface" do
|
|
345
395
|
expect( date ).to eq @data.first[:day]
|
346
396
|
end
|
347
397
|
|
348
|
-
it
|
398
|
+
it "returns datetime fields as Time" do
|
349
399
|
timestamp = interface.read(1).>>.timestamp
|
350
400
|
|
351
401
|
expect( timestamp ).to be_a_kind_of Time
|
352
402
|
expect( timestamp ).to eq @data.first[:timestamp]
|
353
403
|
end
|
354
404
|
|
355
|
-
it
|
405
|
+
it "returns numeric fields as BigDecimal" do
|
356
406
|
qty = interface.read(1).>>.qty
|
357
407
|
|
358
408
|
expect( qty ).to be_a_kind_of BigDecimal
|
359
409
|
expect( qty ).to eq @data.first[:qty]
|
360
410
|
end
|
361
411
|
|
362
|
-
it
|
412
|
+
it "returns money fields as BigDecimal" do
|
363
413
|
price = interface.read(1).>>.price
|
364
414
|
|
365
415
|
expect( price ).to be_a_kind_of BigDecimal
|
366
416
|
expect( price ).to eq @data.first[:price]
|
367
417
|
end
|
368
418
|
|
369
|
-
it
|
419
|
+
it "has no problem with non-integer keys" do
|
370
420
|
# this is a 100% overlap with the create test above...
|
371
421
|
fill_product_data(prod_interface)
|
372
422
|
|
@@ -374,57 +424,60 @@ describe "TdsInterface" do
|
|
374
424
|
expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
|
375
425
|
end
|
376
426
|
|
377
|
-
|
378
|
-
|
427
|
+
it "calls ConnectionPool#client" do
|
428
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
429
|
+
interface.read(2)
|
430
|
+
end
|
379
431
|
|
432
|
+
end # of #read
|
380
433
|
|
381
|
-
describe '#list' do
|
382
|
-
before { fill_data(interface) }
|
383
434
|
|
384
|
-
|
435
|
+
describe "#list" do
|
436
|
+
before(:each) { fill_data(interface) }
|
437
|
+
|
438
|
+
it "has an optional selection parameter, a hash" do
|
385
439
|
# Actually it does not have to be a hash, but FTTB we only support that.
|
386
|
-
expect{ interface.list(name:
|
440
|
+
expect{ interface.list(name: "Barney") }.not_to raise_exception
|
387
441
|
end
|
388
442
|
|
389
|
-
it
|
443
|
+
it "returns an array of Octothorpes that match the records" do
|
390
444
|
list = interface.list
|
391
445
|
|
392
446
|
expect( list.first ).to be_a_kind_of Octothorpe
|
393
447
|
expect( list.map{|x| x.>>.name} ).to match_array @data.map{|y| y[:name]}
|
394
448
|
end
|
395
449
|
|
396
|
-
it
|
397
|
-
expect( interface.list(name:
|
450
|
+
it "returns a subset of records based on the selection parameter" do
|
451
|
+
expect( interface.list(name: "Fred").size ).to eq 1
|
398
452
|
|
399
|
-
expect( interface.list(name:
|
400
|
-
to include(name:
|
453
|
+
expect( interface.list(name: "Betty").first.to_h ).
|
454
|
+
to include(name: "Betty", price: 3.46)
|
401
455
|
|
402
456
|
end
|
403
457
|
|
404
|
-
it
|
405
|
-
expect( interface.list(name:
|
458
|
+
it "returns an empty Array if nothing matches" do
|
459
|
+
expect( interface.list(name: "Yogi") ).to eq([])
|
406
460
|
end
|
407
461
|
|
408
|
-
it
|
409
|
-
expect{ interface.list(
|
462
|
+
it "raises DatabaseError if the selection criteria is nonsensical" do
|
463
|
+
expect{ interface.list("foo") }.to raise_exception Pod4::DatabaseError
|
410
464
|
end
|
411
465
|
|
412
|
-
|
413
|
-
|
466
|
+
it "calls ConnectionPool#client" do
|
467
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
468
|
+
interface.list
|
469
|
+
end
|
470
|
+
|
471
|
+
end # of #list
|
414
472
|
|
415
473
|
|
416
|
-
describe
|
417
|
-
before { fill_data(interface) }
|
474
|
+
describe "#update" do
|
475
|
+
before(:each) { fill_data(interface) }
|
418
476
|
|
419
477
|
let(:id) { interface.list.first[:id] }
|
420
478
|
|
421
|
-
|
422
|
-
|
423
|
-
row
|
424
|
-
end
|
425
|
-
|
426
|
-
it 'updates the record at ID with record parameter' do
|
427
|
-
record = {name: 'Booboo', price: 99.99}
|
479
|
+
it "updates the record at ID with record parameter" do
|
480
|
+
record = {name: "Booboo", price: 99.99}
|
428
481
|
interface.update(id, record)
|
429
482
|
|
430
483
|
# It so happens that TinyTds returns money as BigDecimal --
|
@@ -432,138 +485,148 @@ describe "TdsInterface" do
|
|
432
485
|
expect( float_price( interface.read(id).to_h ) ).to include(record)
|
433
486
|
end
|
434
487
|
|
435
|
-
it
|
436
|
-
expect{ interface.update(99, name:
|
488
|
+
it "raises a CantContinue if anything weird happens with the ID" do
|
489
|
+
expect{ interface.update(99, name: "Booboo") }.
|
437
490
|
to raise_exception CantContinue
|
438
491
|
|
439
492
|
end
|
440
493
|
|
441
|
-
it
|
442
|
-
expect{ interface.update(id, smarts:
|
494
|
+
it "raises a DatabaseError if anything weird happens with the record" do
|
495
|
+
expect{ interface.update(id, smarts: "more") }.
|
443
496
|
to raise_exception DatabaseError
|
444
497
|
|
445
498
|
end
|
446
499
|
|
447
|
-
it
|
448
|
-
record = {name:
|
500
|
+
it "has no problem with record values of nil" do
|
501
|
+
record = {name: "Ranger", price: nil}
|
449
502
|
expect{ interface.update(id, record) }.not_to raise_exception
|
450
503
|
expect( interface.read(id).to_h ).to include(record)
|
451
504
|
end
|
452
505
|
|
453
|
-
it
|
506
|
+
it "has no problem with strings containing special characters" do
|
454
507
|
record = {name: "T'Challa[]", price: nil}
|
455
508
|
expect{ interface.update(id, record) }.not_to raise_exception
|
456
509
|
expect( interface.read(id).to_h ).to include(record)
|
457
510
|
end
|
458
511
|
|
459
|
-
it
|
512
|
+
it "has no problem with non-integer keys" do
|
460
513
|
fill_product_data(prod_interface)
|
461
514
|
expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
|
462
515
|
expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
|
463
516
|
end
|
464
517
|
|
465
|
-
|
466
|
-
|
467
|
-
|
518
|
+
it "calls ConnectionPool#client" do
|
519
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
520
|
+
record = {name: "Booboo", price: 99.99}
|
521
|
+
interface.update(id, record)
|
522
|
+
end
|
468
523
|
|
469
|
-
|
524
|
+
end # of #update
|
470
525
|
|
471
|
-
def list_contains(ifce, id)
|
472
|
-
ifce.list.find {|x| x[ifce.id_fld] == id }
|
473
|
-
end
|
474
526
|
|
527
|
+
describe "#delete" do
|
475
528
|
let(:id) { interface.list.first[:id] }
|
476
529
|
|
477
|
-
before { fill_data(interface) }
|
530
|
+
before(:each) { fill_data(interface) }
|
478
531
|
|
479
|
-
it
|
532
|
+
it "raises CantContinue if anything hinky happens with the ID" do
|
480
533
|
expect{ interface.delete(:foo) }.to raise_exception CantContinue
|
481
534
|
expect{ interface.delete(99) }.to raise_exception CantContinue
|
482
535
|
end
|
483
536
|
|
484
|
-
it
|
537
|
+
it "makes the record at ID go away" do
|
485
538
|
expect( list_contains(interface, id) ).to be_truthy
|
486
539
|
interface.delete(id)
|
487
540
|
expect( list_contains(interface, id) ).to be_falsy
|
488
541
|
end
|
489
542
|
|
490
|
-
it
|
543
|
+
it "has no problem with non-integer keys" do
|
491
544
|
fill_product_data(prod_interface)
|
492
545
|
expect( list_contains(prod_interface, "foo") ).to be_truthy
|
493
546
|
prod_interface.delete("foo")
|
494
547
|
expect( list_contains(prod_interface, "foo") ).to be_falsy
|
495
548
|
end
|
496
549
|
|
497
|
-
|
498
|
-
|
550
|
+
it "calls ConnectionPool#client" do
|
551
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
552
|
+
interface.delete(id)
|
553
|
+
end
|
499
554
|
|
555
|
+
end # of #delete
|
500
556
|
|
501
|
-
describe '#execute' do
|
502
557
|
|
503
|
-
|
558
|
+
describe "#execute" do
|
559
|
+
let(:sql) { "delete from customer where price < 2.0;" }
|
504
560
|
|
505
|
-
before { fill_data(interface) }
|
561
|
+
before(:each) { fill_data(interface) }
|
506
562
|
|
507
|
-
it
|
563
|
+
it "requires an SQL string" do
|
508
564
|
expect{ interface.execute }.to raise_exception ArgumentError
|
509
565
|
expect{ interface.execute(nil) }.to raise_exception ArgumentError
|
510
566
|
expect{ interface.execute(14) }.to raise_exception ArgumentError
|
511
567
|
end
|
512
568
|
|
513
|
-
it
|
514
|
-
expect{ interface.execute(
|
569
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
570
|
+
expect{ interface.execute("delete from not_a_table") }.
|
515
571
|
to raise_exception Pod4Error
|
516
572
|
|
517
573
|
end
|
518
574
|
|
519
|
-
it
|
575
|
+
it "executes the string" do
|
520
576
|
expect{ interface.execute(sql) }.not_to raise_exception
|
521
577
|
expect( interface.list.size ).to eq(@data.size - 1)
|
522
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
578
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
523
579
|
end
|
524
580
|
|
525
|
-
|
526
|
-
|
581
|
+
it "calls ConnectionPool#client" do
|
582
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
583
|
+
interface.execute(sql)
|
584
|
+
end
|
527
585
|
|
586
|
+
end # of #execute
|
528
587
|
|
529
|
-
describe '#select' do
|
530
588
|
|
531
|
-
|
589
|
+
describe "#select" do
|
590
|
+
before(:each) { fill_data(interface) }
|
532
591
|
|
533
|
-
it
|
592
|
+
it "requires an SQL string" do
|
534
593
|
expect{ interface.select }.to raise_exception ArgumentError
|
535
594
|
expect{ interface.select(nil) }.to raise_exception ArgumentError
|
536
595
|
expect{ interface.select(14) }.to raise_exception ArgumentError
|
537
596
|
end
|
538
597
|
|
539
|
-
it
|
540
|
-
expect{ interface.select(
|
598
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
599
|
+
expect{ interface.select("select * from not_a_table") }.
|
541
600
|
to raise_exception Pod4Error
|
542
601
|
|
543
602
|
end
|
544
603
|
|
545
|
-
it
|
546
|
-
sql1 =
|
547
|
-
sql2 =
|
604
|
+
it "returns the result of the sql" do
|
605
|
+
sql1 = "select name from customer where price < 2.0;"
|
606
|
+
sql2 = "select name from customer where price < 0.0;"
|
548
607
|
|
549
608
|
expect{ interface.select(sql1) }.not_to raise_exception
|
550
|
-
expect( interface.select(sql1) ).to eq( [{name:
|
609
|
+
expect( interface.select(sql1) ).to eq( [{name: "Barney"}] )
|
551
610
|
expect( interface.select(sql2) ).to eq( [] )
|
552
611
|
end
|
553
612
|
|
554
|
-
it
|
613
|
+
it "works if you pass a non-select" do
|
555
614
|
# By which I mean: still executes the SQL; returns []
|
556
|
-
sql =
|
615
|
+
sql = "delete from customer where price < 2.0;"
|
557
616
|
ret = interface.select(sql)
|
558
617
|
|
559
618
|
expect( interface.list.size ).to eq(@data.size - 1)
|
560
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
619
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
561
620
|
expect( ret ).to eq( [] )
|
562
621
|
end
|
563
622
|
|
623
|
+
it "calls ConnectionPool#client" do
|
624
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
625
|
+
sql = "delete from customer where price < 2.0;"
|
626
|
+
interface.select(sql)
|
627
|
+
end
|
564
628
|
|
565
|
-
end
|
566
|
-
##
|
629
|
+
end # of #select
|
567
630
|
|
568
631
|
|
569
632
|
describe "#escape" do
|
@@ -579,9 +642,30 @@ describe "TdsInterface" do
|
|
579
642
|
expect( interface.escape "G'Kar" ).to eq %Q|G''Kar|
|
580
643
|
end
|
581
644
|
|
582
|
-
end
|
583
|
-
|
645
|
+
end # of #escape
|
646
|
+
|
647
|
+
|
648
|
+
describe "#new_connection" do
|
649
|
+
|
650
|
+
it "returns a PG Client object" do
|
651
|
+
expect( interface.new_connection @connect_hash ).to be_a TinyTds::Client
|
652
|
+
end
|
653
|
+
|
654
|
+
end # of #new_connection
|
655
|
+
|
656
|
+
|
657
|
+
describe "#close_connection" do
|
658
|
+
|
659
|
+
it "closes the given PG Client object" do
|
660
|
+
client = interface.new_connection(@connect_hash)
|
661
|
+
|
662
|
+
expect( client ).to receive(:close)
|
663
|
+
|
664
|
+
interface.close_connection(client)
|
665
|
+
end
|
666
|
+
|
667
|
+
end # of #close_connection
|
668
|
+
|
584
669
|
|
585
|
-
|
586
670
|
end
|
587
671
|
|