pod4 0.6.2
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 +7 -0
- data/.hgignore +18 -0
- data/.hgtags +19 -0
- data/.rspec +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.md +21 -0
- data/README.md +556 -0
- data/Rakefile +30 -0
- data/lib/pod4/alert.rb +87 -0
- data/lib/pod4/basic_model.rb +137 -0
- data/lib/pod4/errors.rb +80 -0
- data/lib/pod4/interface.rb +110 -0
- data/lib/pod4/metaxing.rb +66 -0
- data/lib/pod4/model.rb +347 -0
- data/lib/pod4/nebulous_interface.rb +408 -0
- data/lib/pod4/null_interface.rb +148 -0
- data/lib/pod4/param.rb +29 -0
- data/lib/pod4/pg_interface.rb +460 -0
- data/lib/pod4/sequel_interface.rb +303 -0
- data/lib/pod4/tds_interface.rb +394 -0
- data/lib/pod4/version.rb +3 -0
- data/lib/pod4.rb +54 -0
- data/md/fixme.md +32 -0
- data/md/roadmap.md +69 -0
- data/pod4.gemspec +49 -0
- data/spec/README.md +19 -0
- data/spec/alert_spec.rb +173 -0
- data/spec/basic_model_spec.rb +220 -0
- data/spec/doc_no_pending.rb +5 -0
- data/spec/fixtures/database.rb +13 -0
- data/spec/model_spec.rb +760 -0
- data/spec/nebulous_interface_spec.rb +286 -0
- data/spec/null_interface_spec.rb +153 -0
- data/spec/param_spec.rb +89 -0
- data/spec/pg_interface_spec.rb +452 -0
- data/spec/pod4_spec.rb +88 -0
- data/spec/sequel_interface_spec.rb +466 -0
- data/spec/shared_examples_for_interface.rb +160 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/tds_interface_spec.rb +494 -0
- data/tags +106 -0
- metadata +316 -0
@@ -0,0 +1,466 @@
|
|
1
|
+
require 'pod4/sequel_interface'
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
require 'bigdecimal'
|
6
|
+
|
7
|
+
require_relative 'shared_examples_for_interface'
|
8
|
+
|
9
|
+
|
10
|
+
class TestSequelInterface < SequelInterface
|
11
|
+
set_table :customer
|
12
|
+
set_id_fld :id
|
13
|
+
end
|
14
|
+
|
15
|
+
class SchemaSequelInterface < SequelInterface
|
16
|
+
set_schema :public
|
17
|
+
set_table :customer
|
18
|
+
set_id_fld :id
|
19
|
+
end
|
20
|
+
|
21
|
+
class BadSequelInterface1 < SequelInterface
|
22
|
+
set_table :customer
|
23
|
+
end
|
24
|
+
|
25
|
+
class BadSequelInterface2 < SequelInterface
|
26
|
+
set_id_fld :id
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe TestSequelInterface do
|
31
|
+
|
32
|
+
# We actually connect to a special test database for this. I don't generally
|
33
|
+
# like unit tests to involve other classes at all, but otherwise we are
|
34
|
+
# hardly testing anything, and in any case we do need to test that this class
|
35
|
+
# successfully interfaces with Sequel. We can't really do that without
|
36
|
+
# talking to a database.
|
37
|
+
|
38
|
+
let(:data) do
|
39
|
+
d = []
|
40
|
+
d << { name: 'Barney',
|
41
|
+
level: 1.23,
|
42
|
+
day: Date.parse("2016-01-01"),
|
43
|
+
timestamp: Time.parse('2015-01-01 12:11'),
|
44
|
+
price: BigDecimal.new("1.24") }
|
45
|
+
|
46
|
+
d << { name: 'Fred',
|
47
|
+
level: 2.34,
|
48
|
+
day: Date.parse("2016-02-02"),
|
49
|
+
timestamp: Time.parse('2015-01-02 12:22'),
|
50
|
+
price: BigDecimal.new("2.35") }
|
51
|
+
|
52
|
+
d << { name: 'Betty',
|
53
|
+
level: 3.45,
|
54
|
+
day: Date.parse("2016-03-03"),
|
55
|
+
timestamp: Time.parse('2015-01-03 12:33'),
|
56
|
+
price: BigDecimal.new("3.46") }
|
57
|
+
|
58
|
+
d
|
59
|
+
end
|
60
|
+
|
61
|
+
def fill_data(ifce)
|
62
|
+
data.each{|r| ifce.create(r) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# This is stolen almost verbatim from the Sequel Readme. We use an in-memory
|
66
|
+
# sqlite database, and we assume that Sequel is sane and behaves broadly the
|
67
|
+
# same for our limited purposes as it would when talking to TinyTDS or Pg.
|
68
|
+
# This may be an entirely unwarranted assumption. If so, we will have to
|
69
|
+
# change this. But in any case, we are not in the business of testing Sequel:
|
70
|
+
# just our interface to it.
|
71
|
+
let (:db) do
|
72
|
+
db = Sequel.sqlite
|
73
|
+
db.create_table :customer do
|
74
|
+
primary_key :id
|
75
|
+
String :name
|
76
|
+
Float :level
|
77
|
+
Date :day
|
78
|
+
Time :timestamp
|
79
|
+
BigDecimal :price, :size=>[10.2] # Sequel doesn't support money
|
80
|
+
end
|
81
|
+
db
|
82
|
+
end
|
83
|
+
|
84
|
+
let(:interface) { TestSequelInterface.new(db) }
|
85
|
+
|
86
|
+
before do
|
87
|
+
fill_data(interface)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
|
92
|
+
|
93
|
+
it_behaves_like 'an interface' do
|
94
|
+
|
95
|
+
let(:interface) do
|
96
|
+
db2 = Sequel.sqlite
|
97
|
+
db2.create_table :customer do
|
98
|
+
primary_key :id
|
99
|
+
String :name
|
100
|
+
Float :level
|
101
|
+
Date :day
|
102
|
+
Time :timestamp
|
103
|
+
BigDecimal :price, :size=>[10.2]
|
104
|
+
end
|
105
|
+
|
106
|
+
TestSequelInterface.new(db2)
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:record) { {name: 'Barney', price: 1.11} }
|
110
|
+
#let(:record_id) { 'Barney' }
|
111
|
+
|
112
|
+
end
|
113
|
+
##
|
114
|
+
|
115
|
+
|
116
|
+
describe 'SequelInterface.set_schema' do
|
117
|
+
it 'takes one argument' do
|
118
|
+
expect( SequelInterface ).to respond_to(:set_schema).with(1).argument
|
119
|
+
end
|
120
|
+
end
|
121
|
+
##
|
122
|
+
|
123
|
+
|
124
|
+
describe 'SequelInterface.schema' do
|
125
|
+
it 'returns the schema' do
|
126
|
+
expect( SchemaSequelInterface.schema ).to eq :public
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'is optional' do
|
130
|
+
expect{ TestSequelInterface.schema }.not_to raise_exception
|
131
|
+
expect( TestSequelInterface.schema ).to eq nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
##
|
135
|
+
|
136
|
+
|
137
|
+
describe 'SequelInterface.set_table' do
|
138
|
+
it 'takes one argument' do
|
139
|
+
expect( SequelInterface ).to respond_to(:set_table).with(1).argument
|
140
|
+
end
|
141
|
+
end
|
142
|
+
##
|
143
|
+
|
144
|
+
|
145
|
+
describe 'SequelInterface.table' do
|
146
|
+
it 'returns the table' do
|
147
|
+
expect( TestSequelInterface.table ).to eq :customer
|
148
|
+
end
|
149
|
+
end
|
150
|
+
##
|
151
|
+
|
152
|
+
|
153
|
+
describe 'SequelInterface.set_id_fld' do
|
154
|
+
it 'takes one argument' do
|
155
|
+
expect( SequelInterface ).to respond_to(:set_id_fld).with(1).argument
|
156
|
+
end
|
157
|
+
end
|
158
|
+
##
|
159
|
+
|
160
|
+
|
161
|
+
describe 'SequelInterface.id_fld' do
|
162
|
+
it 'returns the ID field name' do
|
163
|
+
expect( TestSequelInterface.id_fld ).to eq :id
|
164
|
+
end
|
165
|
+
end
|
166
|
+
##
|
167
|
+
|
168
|
+
|
169
|
+
describe '#new' do
|
170
|
+
|
171
|
+
it 'requires a Sequel DB object' do
|
172
|
+
expect{ TestSequelInterface.new }.to raise_exception ArgumentError
|
173
|
+
expect{ TestSequelInterface.new(nil) }.to raise_exception ArgumentError
|
174
|
+
expect{ TestSequelInterface.new('foo') }.to raise_exception ArgumentError
|
175
|
+
|
176
|
+
expect{ TestSequelInterface.new(db) }.not_to raise_exception
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'requires the table and id field to be defined in the class' do
|
180
|
+
expect{ SequelInterface.new(db) }.to raise_exception Pod4Error
|
181
|
+
expect{ BadSequelInterface1.new(db) }.to raise_exception Pod4Error
|
182
|
+
expect{ BadSequelInterface2.new(db) }.to raise_exception Pod4Error
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
##
|
187
|
+
|
188
|
+
|
189
|
+
describe '#quoted_table' do
|
190
|
+
|
191
|
+
it 'returns just the table when the schema is not set' do
|
192
|
+
expect( interface.quoted_table ).to eq( %Q|`customer`| )
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'returns the schema plus table when the schema is set' do
|
196
|
+
ifce = SchemaSequelInterface.new(db)
|
197
|
+
expect( ifce.quoted_table ).to eq( %|`public`.`customer`| )
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
##
|
202
|
+
|
203
|
+
|
204
|
+
describe '#create' do
|
205
|
+
|
206
|
+
let(:hash) { {name: 'Bam-Bam', price: 4.44} }
|
207
|
+
let(:ot) { Octothorpe.new(name: 'Wilma', price: 5.55) }
|
208
|
+
|
209
|
+
it 'raises a Pod4::DatabaseError if anything goes wrong' do
|
210
|
+
expect{ interface.create(one: 'two') }.to raise_exception DatabaseError
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'creates the record when given a hash' do
|
214
|
+
# kinda impossible to seperate these two tests
|
215
|
+
id = interface.create(hash)
|
216
|
+
|
217
|
+
expect{ interface.read(id) }.not_to raise_exception
|
218
|
+
expect( interface.read(id).to_h ).to include hash
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'creates the record when given an Octothorpe' do
|
222
|
+
id = interface.create(ot)
|
223
|
+
|
224
|
+
expect{ interface.read(id) }.not_to raise_exception
|
225
|
+
expect( interface.read(id).to_h ).to include ot.to_h
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'does not freak out if the hash has symbol values' do
|
229
|
+
# Which, Sequel does
|
230
|
+
expect{ interface.create(name: :Booboo) }.not_to raise_exception
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'shouldnt have a problem with record values of nil' do
|
234
|
+
record = {name: 'Ranger', price: nil}
|
235
|
+
expect{ interface.create(record) }.not_to raise_exception
|
236
|
+
id = interface.create(record)
|
237
|
+
expect( interface.read(id).to_h ).to include(record)
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
##
|
242
|
+
|
243
|
+
|
244
|
+
describe '#read' do
|
245
|
+
|
246
|
+
it 'returns the record for the id as an Octothorpe' do
|
247
|
+
expect( interface.read(2).to_h ).to include(name: 'Fred', price: 2.35)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'raises a Pod4::CantContinue if the ID is bad' do
|
251
|
+
expect{ interface.read(:foo) }.to raise_exception CantContinue
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'returns an empty Octothorpe if no record matches the ID' do
|
255
|
+
expect{ interface.read(99) }.not_to raise_exception
|
256
|
+
expect( interface.read(99) ).to be_a_kind_of Octothorpe
|
257
|
+
expect( interface.read(99) ).to be_empty
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'returns real fields as Float' do
|
261
|
+
level = interface.read(1).>>.level
|
262
|
+
|
263
|
+
expect( level ).to be_a_kind_of Float
|
264
|
+
expect( level ).to be_within(0.001).of( data.first[:level] )
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'returns date fields as Date' do
|
268
|
+
date = interface.read(1).>>.day
|
269
|
+
|
270
|
+
expect( date ).to be_a_kind_of Date
|
271
|
+
expect( date ).to eq data.first[:day]
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'returns datetime fields as Time' do
|
275
|
+
timestamp = interface.read(1).>>.timestamp
|
276
|
+
|
277
|
+
expect( timestamp ).to be_a_kind_of Time
|
278
|
+
expect( timestamp ).to eq data.first[:timestamp]
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'returns numeric fields as BigDecimal' do
|
282
|
+
price = interface.read(1).>>.price
|
283
|
+
|
284
|
+
expect( price ).to be_a_kind_of BigDecimal
|
285
|
+
expect( price ).to eq data.first[:price]
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
##
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
describe '#list' do
|
294
|
+
|
295
|
+
it 'has an optional selection parameter, a hash' do
|
296
|
+
# Actually it does not have to be a hash, but FTTB we only support that.
|
297
|
+
expect{ interface.list(name: 'Barney') }.not_to raise_exception
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'returns an array of Octothorpes that match the records' do
|
301
|
+
# convert each OT to a hash and remove the ID key
|
302
|
+
arr = interface.list.map {|ot| x = ot.to_h; x.delete(:id); x }
|
303
|
+
|
304
|
+
expect( arr ).to match_array data
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'returns a subset of records based on the selection parameter' do
|
308
|
+
expect( interface.list(name: 'Fred').size ).to eq 1
|
309
|
+
|
310
|
+
expect( interface.list(name: 'Betty').first.to_h ).
|
311
|
+
to include(name: 'Betty', price: 3.46)
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'returns an empty Array if nothing matches' do
|
316
|
+
expect( interface.list(name: 'Yogi') ).to eq([])
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'raises DatabaseError if the selection criteria is nonsensical' do
|
320
|
+
expect{ interface.list('foo') }.to raise_exception Pod4::DatabaseError
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'returns an empty array if there is no data' do
|
324
|
+
interface.list.each {|x| interface.delete(x[interface.id_fld]) }
|
325
|
+
expect( interface.list ).to eq([])
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'does not freak out if the hash has symbol values' do
|
329
|
+
# Which, Sequel does
|
330
|
+
expect{ interface.list(name: :Barney) }.not_to raise_exception
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
end
|
335
|
+
##
|
336
|
+
|
337
|
+
|
338
|
+
describe '#update' do
|
339
|
+
|
340
|
+
let(:id) { interface.list.first[:id] }
|
341
|
+
|
342
|
+
it 'updates the record at ID with record parameter' do
|
343
|
+
record = {name: 'Booboo', price: 99.99}
|
344
|
+
interface.update(id, record)
|
345
|
+
|
346
|
+
booboo = interface.read(id)
|
347
|
+
expect( booboo.>>.name ).to eq( record[:name] )
|
348
|
+
expect( booboo.>>.price.to_f ).to eq( record[:price] )
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'raises a CantContinue if anything weird happens with the ID' do
|
352
|
+
expect{ interface.update(99, name: 'Booboo') }.
|
353
|
+
to raise_exception CantContinue
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'raises a DatabaseError if anything weird happensi with the record' do
|
358
|
+
expect{ interface.update(id, smarts: 'more') }.
|
359
|
+
to raise_exception DatabaseError
|
360
|
+
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'does not freak out if the hash has symbol values' do
|
364
|
+
# Which, Sequel does
|
365
|
+
expect{ interface.update(id, name: :Booboo) }.not_to raise_exception
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'shouldnt have a problem with record values of nil' do
|
369
|
+
record = {name: 'Ranger', price: nil}
|
370
|
+
expect{ interface.update(id, record) }.not_to raise_exception
|
371
|
+
expect( interface.read(id).to_h ).to include(record)
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
##
|
376
|
+
|
377
|
+
|
378
|
+
describe '#delete' do
|
379
|
+
|
380
|
+
def list_contains(id)
|
381
|
+
interface.list.find {|x| x[interface.id_fld] == id }
|
382
|
+
end
|
383
|
+
|
384
|
+
let(:id) { interface.list.first[:id] }
|
385
|
+
|
386
|
+
it 'raises CantContinue if anything hinky happens with the ID' do
|
387
|
+
expect{ interface.delete(:foo) }.to raise_exception CantContinue
|
388
|
+
expect{ interface.delete(99) }.to raise_exception CantContinue
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'makes the record at ID go away' do
|
392
|
+
expect( list_contains(id) ).to be_truthy
|
393
|
+
interface.delete(id)
|
394
|
+
expect( list_contains(id) ).to be_falsy
|
395
|
+
end
|
396
|
+
|
397
|
+
end
|
398
|
+
##
|
399
|
+
|
400
|
+
|
401
|
+
describe '#execute' do
|
402
|
+
|
403
|
+
let(:sql) { 'delete from customer where price < 2.0;' }
|
404
|
+
|
405
|
+
it 'requires an SQL string' do
|
406
|
+
expect{ interface.execute }.to raise_exception ArgumentError
|
407
|
+
expect{ interface.execute(nil) }.to raise_exception ArgumentError
|
408
|
+
expect{ interface.execute(14) }.to raise_exception ArgumentError
|
409
|
+
end
|
410
|
+
|
411
|
+
it 'raises some sort of Pod4 error if it runs into problems' do
|
412
|
+
expect{ interface.execute('delete from not_a_table') }.
|
413
|
+
to raise_exception Pod4Error
|
414
|
+
|
415
|
+
end
|
416
|
+
|
417
|
+
it 'executes the string' do
|
418
|
+
expect{ interface.execute(sql) }.not_to raise_exception
|
419
|
+
expect( interface.list.size ).to eq(data.size - 1)
|
420
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
##
|
425
|
+
|
426
|
+
|
427
|
+
describe '#select' do
|
428
|
+
|
429
|
+
it 'requires an SQL string' do
|
430
|
+
expect{ interface.select }.to raise_exception ArgumentError
|
431
|
+
expect{ interface.select(nil) }.to raise_exception ArgumentError
|
432
|
+
expect{ interface.select(14) }.to raise_exception ArgumentError
|
433
|
+
end
|
434
|
+
|
435
|
+
it 'raises some sort of Pod4 error if it runs into problems' do
|
436
|
+
expect{ interface.select('select * from not_a_table') }.
|
437
|
+
to raise_exception Pod4Error
|
438
|
+
|
439
|
+
end
|
440
|
+
|
441
|
+
it 'returns the result of the sql' do
|
442
|
+
sql1 = 'select name from customer where price < 2.0;'
|
443
|
+
sql2 = 'select name from customer where price < 0.0;'
|
444
|
+
|
445
|
+
expect{ interface.select(sql1) }.not_to raise_exception
|
446
|
+
expect( interface.select(sql1) ).to eq( [{name: 'Barney'}] )
|
447
|
+
expect( interface.select(sql2) ).to eq( [] )
|
448
|
+
end
|
449
|
+
|
450
|
+
it 'works if you pass a non-select' do
|
451
|
+
# By which I mean: still executes the SQL; returns []
|
452
|
+
sql = 'delete from customer where price < 2.0;'
|
453
|
+
ret = interface.select(sql)
|
454
|
+
|
455
|
+
expect( interface.list.size ).to eq(data.size - 1)
|
456
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
|
457
|
+
expect( ret ).to eq( [] )
|
458
|
+
end
|
459
|
+
|
460
|
+
|
461
|
+
end
|
462
|
+
##
|
463
|
+
|
464
|
+
|
465
|
+
end
|
466
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'octothorpe'
|
2
|
+
|
3
|
+
|
4
|
+
##
|
5
|
+
# These are the shared tests for all interfaces. To use them you need to
|
6
|
+
# supply:
|
7
|
+
#
|
8
|
+
# * record - a record to insert
|
9
|
+
# * interface - an instance of the interface to call.
|
10
|
+
#
|
11
|
+
# For example (from nebulous_interface_spec):
|
12
|
+
#
|
13
|
+
# it_behaves_like 'an interface' do
|
14
|
+
# let(:record) { {id: 1, name: 'percy'} }
|
15
|
+
#
|
16
|
+
# let(:interface) do
|
17
|
+
# init_nebulous
|
18
|
+
# TestNebulousInterface.new( FakeRequester.new )
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# 'record' does not have to include every column in your table, and you should
|
23
|
+
# actively exclude floats (where you cannot guarantee equality) or anything
|
24
|
+
# that is supposed to render to a BigDecimal. Make sure these fields are NULL
|
25
|
+
# in the table definition.
|
26
|
+
#
|
27
|
+
# Note that these shared tests only test the common parts of the API that the
|
28
|
+
# interface exposes to the *model*; they make no assumptions about where your
|
29
|
+
# test data is coming from, or how you are calling or mocking whatever library
|
30
|
+
# the interface is an adapter to.
|
31
|
+
#
|
32
|
+
# It's up to the individual specs to test that the interface is calling its
|
33
|
+
# library correctly and deal with all the things specific to that interface -
|
34
|
+
# which includes how the model calls new() and list(), for example.
|
35
|
+
#
|
36
|
+
RSpec.shared_examples 'an interface' do
|
37
|
+
|
38
|
+
let(:record_as_ot) { Octothorpe.new(record) }
|
39
|
+
|
40
|
+
|
41
|
+
describe '#id_fld' do
|
42
|
+
it 'is an attribute' do
|
43
|
+
expect( interface.id_fld ).not_to be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
##
|
47
|
+
|
48
|
+
|
49
|
+
describe '#create' do
|
50
|
+
|
51
|
+
it 'requires a hash or an Octothorpe' do
|
52
|
+
expect{ interface.create }.to raise_exception ArgumentError
|
53
|
+
expect{ interface.create(nil) }.to raise_exception ArgumentError
|
54
|
+
expect{ interface.create(3) }.to raise_exception ArgumentError
|
55
|
+
|
56
|
+
expect{ interface.create(record) }.not_to raise_exception
|
57
|
+
expect{ interface.create(record_as_ot) }.not_to raise_exception
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns the ID' do
|
61
|
+
record_id = interface.create(record)
|
62
|
+
expect{ interface.read(record_id) }.not_to raise_exception
|
63
|
+
expect( interface.read(record_id).to_h ).to include record
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
##
|
68
|
+
|
69
|
+
|
70
|
+
describe '#read' do
|
71
|
+
|
72
|
+
before do
|
73
|
+
interface.create(record)
|
74
|
+
@id = interface.list.first[interface.id_fld]
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'requires an ID' do
|
78
|
+
expect{ interface.read }.to raise_exception ArgumentError
|
79
|
+
expect{ interface.read(nil) }.to raise_exception ArgumentError
|
80
|
+
|
81
|
+
expect{ interface.read(@id) }.not_to raise_exception
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns an Octothorpe' do
|
85
|
+
expect( interface.read(@id) ).to be_a_kind_of Octothorpe
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
##
|
90
|
+
|
91
|
+
|
92
|
+
describe '#update' do
|
93
|
+
|
94
|
+
before do
|
95
|
+
interface.create(record)
|
96
|
+
@id = interface.list.first[interface.id_fld]
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
it 'requires an ID and a record (hash or OT)' do
|
101
|
+
expect{ interface.update }.to raise_exception ArgumentError
|
102
|
+
expect{ interface.update(nil) }.to raise_exception ArgumentError
|
103
|
+
expect{ interface.update(14) }.to raise_exception ArgumentError
|
104
|
+
|
105
|
+
expect{ interface.update(@id, record) }.not_to raise_exception
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns self' do
|
109
|
+
expect( interface.update(@id, record) ).to eq interface
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
##
|
114
|
+
|
115
|
+
|
116
|
+
describe '#delete' do
|
117
|
+
|
118
|
+
before do
|
119
|
+
interface.create(record)
|
120
|
+
@id = interface.list.first[interface.id_fld]
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'requires an id' do
|
124
|
+
expect{ interface.delete }.to raise_exception ArgumentError
|
125
|
+
expect{ interface.delete(nil) }.to raise_exception ArgumentError
|
126
|
+
|
127
|
+
expect{ interface.delete(@id) }.not_to raise_exception
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'returns self' do
|
131
|
+
expect( interface.delete(@id) ).to eq interface
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
##
|
136
|
+
|
137
|
+
|
138
|
+
describe '#list' do
|
139
|
+
|
140
|
+
it 'will allow itself to be called with no parameter' do
|
141
|
+
expect{ interface.list }.not_to raise_exception
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns an array of Octothorpes' do
|
145
|
+
interface.create(record)
|
146
|
+
expect( interface.list ).to be_a_kind_of Array
|
147
|
+
expect( interface.list.first ).to be_a_kind_of Octothorpe
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'has the ID field as one of the Octothorpe keys' do
|
151
|
+
interface.create(record)
|
152
|
+
expect( interface.list.first.to_h ).to have_key interface.id_fld
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
##
|
157
|
+
|
158
|
+
|
159
|
+
end
|
160
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pry' #always useful when debugging
|
2
|
+
require 'pod4'
|
3
|
+
|
4
|
+
|
5
|
+
include Pod4
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.mock_with :rspec do |mocks|
|
14
|
+
mocks.verify_partial_doubles = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.filter_run :focus
|
18
|
+
config.run_all_when_everything_filtered = true
|
19
|
+
|
20
|
+
if config.files_to_run.one?
|
21
|
+
config.default_formatter = 'doc'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|