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.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.bugs/bugs +2 -1
  3. data/.bugs/details/b5368c7ef19065fc597b5692314da71772660963.txt +53 -0
  4. data/.hgtags +1 -0
  5. data/Gemfile +5 -5
  6. data/README.md +157 -46
  7. data/lib/pod4/basic_model.rb +9 -22
  8. data/lib/pod4/connection.rb +67 -0
  9. data/lib/pod4/connection_pool.rb +154 -0
  10. data/lib/pod4/errors.rb +20 -0
  11. data/lib/pod4/interface.rb +34 -12
  12. data/lib/pod4/model.rb +32 -27
  13. data/lib/pod4/nebulous_interface.rb +25 -30
  14. data/lib/pod4/null_interface.rb +22 -16
  15. data/lib/pod4/pg_interface.rb +84 -104
  16. data/lib/pod4/sequel_interface.rb +138 -82
  17. data/lib/pod4/tds_interface.rb +83 -70
  18. data/lib/pod4/tweaking.rb +105 -0
  19. data/lib/pod4/version.rb +1 -1
  20. data/md/breaking_changes.md +80 -0
  21. data/spec/common/basic_model_spec.rb +67 -70
  22. data/spec/common/connection_pool_parallelism_spec.rb +154 -0
  23. data/spec/common/connection_pool_spec.rb +246 -0
  24. data/spec/common/connection_spec.rb +129 -0
  25. data/spec/common/model_ai_missing_id_spec.rb +256 -0
  26. data/spec/common/model_plus_encrypting_spec.rb +16 -4
  27. data/spec/common/model_plus_tweaking_spec.rb +128 -0
  28. data/spec/common/model_plus_typecasting_spec.rb +10 -4
  29. data/spec/common/model_spec.rb +283 -363
  30. data/spec/common/nebulous_interface_spec.rb +159 -108
  31. data/spec/common/null_interface_spec.rb +88 -65
  32. data/spec/common/sequel_interface_pg_spec.rb +217 -161
  33. data/spec/common/shared_examples_for_interface.rb +50 -50
  34. data/spec/jruby/sequel_encrypting_jdbc_pg_spec.rb +1 -1
  35. data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +3 -3
  36. data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +3 -23
  37. data/spec/mri/pg_encrypting_spec.rb +1 -1
  38. data/spec/mri/pg_interface_spec.rb +311 -223
  39. data/spec/mri/sequel_encrypting_spec.rb +1 -1
  40. data/spec/mri/sequel_interface_spec.rb +177 -180
  41. data/spec/mri/tds_encrypting_spec.rb +1 -1
  42. data/spec/mri/tds_interface_spec.rb +296 -212
  43. data/tags +340 -174
  44. metadata +19 -11
  45. data/md/fixme.md +0 -3
  46. data/md/roadmap.md +0 -125
  47. data/md/typecasting.md +0 -80
  48. data/spec/common/model_new_validate_spec.rb +0 -204
@@ -1,9 +1,9 @@
1
- require 'pod4/nebulous_interface'
1
+ require "pod4/nebulous_interface"
2
2
 
3
- require 'nebulous_stomp'
4
- require 'nebulous_stomp/stomp_handler_null'
3
+ require "nebulous_stomp"
4
+ require "nebulous_stomp/stomp_handler_null"
5
5
 
6
- require_relative 'shared_examples_for_interface'
6
+ require_relative "shared_examples_for_interface"
7
7
 
8
8
 
9
9
  ##
@@ -14,10 +14,10 @@ require_relative 'shared_examples_for_interface'
14
14
  # creating a Request instance by itself. (It expects send to return a Request instance, or
15
15
  # something that behaves like one.)
16
16
  #
17
- # This means we can cut Nebulous out of the loop and don't need a real responder. We can also check
18
- # the behaviour of NebulousInterface by using RSpec 'inspect' syntax on our cutout object.
17
+ # This means we can cut Nebulous out of the loop and don"t need a real responder. We can also check
18
+ # the behaviour of NebulousInterface by using RSpec "inspect" syntax on our cutout object.
19
19
  #
20
- # We're basically emulating both a responder and a data source here (!)
20
+ # We"re basically emulating both a responder and a data source here (!)
21
21
  #
22
22
  class FakeRequester
23
23
 
@@ -70,96 +70,115 @@ class FakeRequester
70
70
  end
71
71
 
72
72
  def response_message_hash(requestmsg)
73
- hash1 = { contentType: 'application/json' }
73
+ hash1 = { contentType: "application/json" }
74
74
 
75
75
  if requestmsg.params.is_a?(Array)
76
76
  array = requestmsg.params
77
- paramstr = array.join(',')
77
+ paramstr = array.join(",")
78
78
  else
79
79
  paramstr = requestmsg.params.to_s
80
- array = paramstr.split(',')
80
+ array = paramstr.split(",")
81
81
  end
82
82
 
83
83
  case requestmsg.verb
84
- when 'custcreate'
84
+ when "custcreate"
85
85
  id = create(*array)
86
- hash2 = {verb: 'success', params: id.to_s}
86
+ hash2 = {verb: "success", params: id.to_s}
87
87
 
88
- when 'custread'
88
+ when "custread"
89
89
  record = @data[paramstr.to_i]
90
- hash2 = { stompBody: (record ? record.to_json : ''.to_json) }
90
+ hash2 = { stompBody: (record ? record.to_json : "".to_json) }
91
91
 
92
- when 'custupdate'
93
- hash2 = update(*array) ? {verb: 'success'} : {verb: 'error' }
92
+ when "custupdate"
93
+ hash2 = update(*array) ? {verb: "success"} : {verb: "error" }
94
94
 
95
- when 'custdelete'
96
- hash2 = @data.delete(paramstr.to_i) ? {verb: 'success'} : {verb: 'error'}
95
+ when "custdelete"
96
+ hash2 = @data.delete(paramstr.to_i) ? {verb: "success"} : {verb: "error"}
97
97
 
98
- when 'custlist'
98
+ when "custlist"
99
99
  subset = @data.values
100
100
  subset.select!{|x| x[:name] == array[0] } if (!paramstr.empty? && !array[0].empty?)
101
101
  hash2 = { stompBody: subset.to_json }
102
102
 
103
- when 'custbad'
104
- hash2 = { verb: 'error', description: 'error verb description' }
103
+ when "custbad"
104
+ hash2 = { verb: "error", description: "error verb description" }
105
105
 
106
106
  end
107
107
 
108
108
  hash1.merge(hash2)
109
109
  end
110
110
 
111
- end
112
- ##
111
+ end # of FakeRequester
113
112
 
113
+ def init_nebulous
114
+ stomp_hash = { hosts: [{ login: "guest",
115
+ passcode: "guest",
116
+ host: "10.0.0.150",
117
+ port: 61613,
118
+ ssl: false }],
119
+ reliable: false }
120
+
121
+ # We turn Redis off for this test; we"re not testing Nebulous here.
122
+ NebulousStomp.init( :stompConnectHash => stomp_hash,
123
+ :redisConnectHash => {},
124
+ :messageTimeout => 5,
125
+ :cacheTimeout => 20 )
126
+
127
+ NebulousStomp.add_target( :faketarget,
128
+ :sendQueue => "/queue/fake.in",
129
+ :receiveQueue => "/queue/fake.out",
130
+ :messageTimeout => 1 )
131
+
132
+ end
114
133
 
115
134
 
116
135
  describe "NebulousInterface" do
117
136
 
137
+ def list_contains(id)
138
+ interface.list.find {|x| x[interface.id_fld] == id }
139
+ end
140
+
118
141
  ##
119
- # In order to conform with 'acts_like an interface', our CRUDL routines must
142
+ # In order to conform with "acts_like an interface", our CRUDL routines must
120
143
  # pass a single value as the record, which we are making an array. When you
121
- # subclass NebulousInterface for your model, you don't have to follow that; you
144
+ # subclass NebulousInterface for your model, you don"t have to follow that; you
122
145
  # can have a parameter per nebulous parameter, if you want, by overriding the
123
146
  # CRUDL methods.
124
147
  #
125
148
  let(:nebulous_interface_class) do
126
149
  Class.new NebulousInterface do
127
- set_target 'faketarget'
150
+ set_target "faketarget"
128
151
  set_id_fld :id
129
152
 
130
- set_verb :create, 'custcreate', :name, :price
131
- set_verb :read, 'custread', :id
132
- set_verb :update, 'custupdate', :id, :name, :price
133
- set_verb :delete, 'custdelete', :id
134
- set_verb :list, 'custlist', :name
153
+ set_verb :create, "custcreate", :name, :price
154
+ set_verb :read, "custread", :id
155
+ set_verb :update, "custupdate", :id, :name, :price
156
+ set_verb :delete, "custdelete", :id
157
+ set_verb :list, "custlist", :name
135
158
  end
136
159
  end
137
160
 
161
+ let(:nebulous_interface_class2) do
162
+ Class.new NebulousInterface do
163
+ set_target "faketarget"
164
+ set_id_fld :id, autoincrement: false
138
165
 
139
- def init_nebulous
140
- stomp_hash = { hosts: [{ login: 'guest',
141
- passcode: 'guest',
142
- host: '10.0.0.150',
143
- port: 61613,
144
- ssl: false }],
145
- reliable: false }
146
-
147
- # We turn Redis off for this test; we're not testing Nebulous here.
148
- NebulousStomp.init( :stompConnectHash => stomp_hash,
149
- :redisConnectHash => {},
150
- :messageTimeout => 5,
151
- :cacheTimeout => 20 )
166
+ set_verb :create, "custcreate", :name, :price
167
+ end
168
+ end
152
169
 
153
- NebulousStomp.add_target( :faketarget,
154
- :sendQueue => "/queue/fake.in",
155
- :receiveQueue => "/queue/fake.out",
156
- :messageTimeout => 1 )
170
+ let(:nebulous_interface_class3) do
171
+ Class.new NebulousInterface do
172
+ set_target "faketarget"
173
+ set_id_fld :id, autoincrement: true
157
174
 
175
+ set_verb :create, "custcreate", :name, :price
176
+ end
158
177
  end
159
178
 
160
179
 
161
- it_behaves_like 'an interface' do
162
- let(:record) { {id: 1, name: 'percy', price: 1.23} }
180
+ it_behaves_like "an interface" do
181
+ let(:record) { {id: 1, name: "percy", price: 1.23} }
163
182
 
164
183
  let(:interface) do
165
184
  init_nebulous
@@ -171,9 +190,9 @@ describe "NebulousInterface" do
171
190
 
172
191
 
173
192
  let(:data) do
174
- { 1 => {id: 1, name: 'Barney', price: 1.11},
175
- 2 => {id: 2, name: 'Fred', price: 2.22},
176
- 3 => {id: 3, name: 'Betty', price: 3.33} }
193
+ { 1 => {id: 1, name: "Barney", price: 1.11},
194
+ 2 => {id: 2, name: "Fred", price: 2.22},
195
+ 3 => {id: 3, name: "Betty", price: 3.33} }
177
196
  end
178
197
 
179
198
  let(:fake) { FakeRequester.new(data) }
@@ -184,61 +203,103 @@ describe "NebulousInterface" do
184
203
  end
185
204
 
186
205
 
187
- describe '#new' do
206
+ describe ".set_id_fld" do
207
+
208
+ it "takes one argument" do
209
+ expect{ nebulous_interface_class.set_id_fld(:id) }.not_to raise_error
210
+ end
211
+
212
+ it "takes an optional second 'autoincrement' argument" do
213
+ expect{ nebulous_interface_class.set_id_fld(:id, autoincrement: true) }.not_to raise_error
214
+ end
215
+
216
+ end # of .set_id_fld
217
+
218
+
219
+ describe ".id_fld" do
220
+
221
+ it "returns the ID field name" do
222
+ expect( nebulous_interface_class.id_fld ).to eq :id
223
+ end
224
+
225
+ end # of .id_fld
226
+
227
+
228
+ describe "id_ai" do
229
+
230
+ it "returns true if autoincrement is true" do
231
+ expect( nebulous_interface_class3.id_ai ).to eq true
232
+ end
233
+
234
+ it "returns false if autoincrement is false" do
235
+ expect( nebulous_interface_class2.id_ai ).to eq false
236
+ end
237
+
238
+ it "returns true if autoincrement is not specified" do
239
+ expect( nebulous_interface_class.id_ai ).to eq true
240
+ end
241
+
242
+ end # of id_ai
243
+
244
+
245
+ describe "#new" do
188
246
 
189
- it 'requires no parameters' do
247
+ it "requires no parameters" do
190
248
  expect{ nebulous_interface_class.new }.not_to raise_exception
191
249
  end
192
250
 
193
- end
194
- ##
251
+ end # of #new
252
+
195
253
 
254
+ describe "#create" do
196
255
 
197
- describe '#create' do
256
+ let(:hash) { {name: "Bam-Bam", price: 4.44} }
257
+ let(:ot) { Octothorpe.new(name: "Wilma", price: 5.55) }
198
258
 
199
- let(:hash) { {name: 'Bam-Bam', price: 4.44} }
200
- let(:ot) { Octothorpe.new(name: 'Wilma', price: 5.55) }
259
+ it "raises an ArgmentError if the ID field is missing from the hash and not AI" do
260
+ interface2 = nebulous_interface_class2.new(fake)
261
+ expect{ interface2.create(hash) }.to raise_error ArgumentError
262
+ end
201
263
 
202
- it 'creates the record when given a hash' do
264
+ it "creates the record when given a hash" do
203
265
  id = interface.create(hash)
204
266
 
205
267
  expect{ interface.read(id) }.not_to raise_exception
206
268
  expect( interface.read(id).to_h ).to include hash
207
269
  end
208
270
 
209
- it 'creates the record when given an Octothorpe' do
271
+ it "creates the record when given an Octothorpe" do
210
272
  id = interface.create(ot)
211
273
 
212
274
  expect{ interface.read(id) }.not_to raise_exception
213
275
  expect( interface.read(id).to_h ).to include ot.to_h
214
276
  end
215
277
 
216
- it 'creates a non-caching request' do
278
+ it "creates a non-caching request" do
217
279
  FakeRequester.clear_method
218
280
  id = interface.create(ot)
219
281
  expect( FakeRequester.method ).to eq :send_no_cache
220
282
  end
221
283
 
222
- end
223
- ##
284
+ end # of #create
224
285
 
225
286
 
226
- describe '#read' do
287
+ describe "#read" do
227
288
 
228
- it 'returns the record for the id as an Octothorpe' do
289
+ it "returns the record for the id as an Octothorpe" do
229
290
  expect( interface.read(1) ).to be_a_kind_of Octothorpe
230
291
  expect( interface.read(2).to_h ).
231
- to include(name: 'Fred', price: 2.22)
292
+ to include(name: "Fred", price: 2.22)
232
293
 
233
294
  end
234
295
 
235
- it 'returns an empty Octothorpe if no record matches the ID' do
296
+ it "returns an empty Octothorpe if no record matches the ID" do
236
297
  expect{ interface.read(99) }.not_to raise_exception
237
298
  expect( interface.read(99) ).to be_a_kind_of Octothorpe
238
299
  expect( interface.read(99) ).to be_empty
239
300
  end
240
301
 
241
- it 'creates a caching request' do
302
+ it "creates a caching request" do
242
303
  FakeRequester.clear_method
243
304
  id = interface.read(1)
244
305
  expect( FakeRequester.method ).to eq :send
@@ -250,61 +311,57 @@ describe "NebulousInterface" do
250
311
  expect( FakeRequester.method ).to eq :send_no_cache
251
312
  end
252
313
 
253
- end
254
- ##
314
+ end # of #read
255
315
 
256
316
 
257
- describe '#list' do
317
+ describe "#list" do
258
318
 
259
- it 'has an optional selection parameter, a hash' do
319
+ it "has an optional selection parameter, a hash" do
260
320
  expect{ interface.list }.not_to raise_exception
261
- expect{ interface.list(name: 'Barney') }.not_to raise_exception
321
+ expect{ interface.list(name: "Barney") }.not_to raise_exception
262
322
  end
263
323
 
264
- it 'returns an array of Octothorpes that match the records' do
324
+ it "returns an array of Octothorpes that match the records" do
265
325
  arr = interface.list.map(&:to_h)
266
326
  expect( arr ).to match_array data.values
267
327
  end
268
328
 
269
- it 'returns a subset of records based on the selection parameter' do
270
- expect( interface.list(name: 'Fred').size ).to eq 1
329
+ it "returns a subset of records based on the selection parameter" do
330
+ expect( interface.list(name: "Fred").size ).to eq 1
271
331
 
272
- expect( interface.list(name: 'Betty').first.to_h ).
273
- to include(name: 'Betty', price: 3.33)
332
+ expect( interface.list(name: "Betty").first.to_h ).
333
+ to include(name: "Betty", price: 3.33)
274
334
 
275
335
  end
276
336
 
277
- it 'returns an empty Array if nothing matches' do
278
- expect( interface.list(name: 'Yogi') ).to eq([])
337
+ it "returns an empty Array if nothing matches" do
338
+ expect( interface.list(name: "Yogi") ).to eq([])
279
339
  end
280
340
 
281
- it 'returns an empty array if there is no data' do
341
+ it "returns an empty array if there is no data" do
282
342
  interface.list.each{|x| interface.delete(x[interface.id_fld]) }
283
343
  expect( interface.list ).to eq([])
284
344
  end
285
345
 
286
- it 'creates a caching request' do
346
+ it "creates a caching request" do
287
347
  FakeRequester.clear_method
288
- interface.list(name: 'Fred')
348
+ interface.list(name: "Fred")
289
349
  expect( FakeRequester.method ).to eq :send
290
350
  end
291
351
 
292
352
  it "creates a non-caching request when passed an option" do
293
353
  FakeRequester.clear_method
294
- interface.list({name: 'Fred'}, caching: false)
354
+ interface.list({name: "Fred"}, caching: false)
295
355
  expect( FakeRequester.method ).to eq :send_no_cache
296
356
  end
297
357
 
298
-
299
- end
300
- ##
358
+ end # of #list
301
359
 
302
360
 
303
- describe '#update' do
304
-
361
+ describe "#update" do
305
362
  let(:id) { interface.list.first[:id] }
306
363
 
307
- it 'updates the record at ID with record parameter' do
364
+ it "updates the record at ID with record parameter" do
308
365
  rec = Octothorpe.new(price: 98.23)
309
366
  interface.update(id, rec)
310
367
 
@@ -312,7 +369,7 @@ describe "NebulousInterface" do
312
369
  expect( x.>>.price ).to eq 98.23
313
370
  end
314
371
 
315
- it 'creates a non-caching request' do
372
+ it "creates a non-caching request" do
316
373
  i = id # creates a request, so get it done now
317
374
  rec = Octothorpe.new(price: 99.99)
318
375
 
@@ -321,30 +378,25 @@ describe "NebulousInterface" do
321
378
  expect( FakeRequester.method ).to eq :send_no_cache
322
379
  end
323
380
 
324
- end
325
- ##
381
+ end # of #update
326
382
 
327
383
 
328
- describe '#delete' do
384
+ describe "#delete" do
329
385
 
330
386
  let(:id) { interface.list.first[:id] }
331
387
 
332
- def list_contains(id)
333
- interface.list.find {|x| x[interface.id_fld] == id }
334
- end
335
-
336
- it 'raises WeakError if anything hinky happens with the ID' do
388
+ it "raises WeakError if anything hinky happens with the ID" do
337
389
  expect{ interface.delete(:foo) }.to raise_exception WeakError
338
390
  expect{ interface.delete(99) }.to raise_exception WeakError
339
391
  end
340
392
 
341
- it 'makes the record at ID go away' do
393
+ it "makes the record at ID go away" do
342
394
  expect( list_contains(id) ).to be_truthy
343
395
  interface.delete(id)
344
396
  expect( list_contains(id) ).to be_falsy
345
397
  end
346
398
 
347
- it 'creates a non-caching request' do
399
+ it "creates a non-caching request" do
348
400
  i = id # creates a request, so get it done now
349
401
 
350
402
  FakeRequester.clear_method
@@ -352,20 +404,19 @@ describe "NebulousInterface" do
352
404
  expect( FakeRequester.method ).to eq :send_no_cache
353
405
  end
354
406
 
355
- end
356
- ##
407
+ end # of #delete
357
408
 
358
409
 
359
410
  describe "#send_message" do
360
411
 
361
412
  context "when nebulous returns an error verb" do
362
-
363
413
  it "raises a Pod4::WeakError" do
364
414
  expect{ interface.send_message("custbad", nil) }.to raise_exception Pod4::WeakError
365
415
  end
366
-
367
416
  end
368
417
 
369
- end
418
+ end # of #send_message
419
+
370
420
 
371
421
  end
422
+