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,9 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "pod4/nebulous_interface"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "nebulous_stomp"
|
4
|
+
require "nebulous_stomp/stomp_handler_null"
|
5
5
|
|
6
|
-
require_relative
|
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
|
18
|
-
# the behaviour of NebulousInterface by using RSpec
|
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
|
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:
|
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
|
84
|
+
when "custcreate"
|
85
85
|
id = create(*array)
|
86
|
-
hash2 = {verb:
|
86
|
+
hash2 = {verb: "success", params: id.to_s}
|
87
87
|
|
88
|
-
when
|
88
|
+
when "custread"
|
89
89
|
record = @data[paramstr.to_i]
|
90
|
-
hash2 = { stompBody: (record ? record.to_json :
|
90
|
+
hash2 = { stompBody: (record ? record.to_json : "".to_json) }
|
91
91
|
|
92
|
-
when
|
93
|
-
hash2 = update(*array) ? {verb:
|
92
|
+
when "custupdate"
|
93
|
+
hash2 = update(*array) ? {verb: "success"} : {verb: "error" }
|
94
94
|
|
95
|
-
when
|
96
|
-
hash2 = @data.delete(paramstr.to_i) ? {verb:
|
95
|
+
when "custdelete"
|
96
|
+
hash2 = @data.delete(paramstr.to_i) ? {verb: "success"} : {verb: "error"}
|
97
97
|
|
98
|
-
when
|
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
|
104
|
-
hash2 = { verb:
|
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
|
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
|
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
|
150
|
+
set_target "faketarget"
|
128
151
|
set_id_fld :id
|
129
152
|
|
130
|
-
set_verb :create,
|
131
|
-
set_verb :read,
|
132
|
-
set_verb :update,
|
133
|
-
set_verb :delete,
|
134
|
-
set_verb :list,
|
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
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
162
|
-
let(:record) { {id: 1, name:
|
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:
|
175
|
-
2 => {id: 2, name:
|
176
|
-
3 => {id: 3, name:
|
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
|
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
|
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
|
-
|
256
|
+
let(:hash) { {name: "Bam-Bam", price: 4.44} }
|
257
|
+
let(:ot) { Octothorpe.new(name: "Wilma", price: 5.55) }
|
198
258
|
|
199
|
-
|
200
|
-
|
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
|
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
|
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
|
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
|
287
|
+
describe "#read" do
|
227
288
|
|
228
|
-
it
|
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:
|
292
|
+
to include(name: "Fred", price: 2.22)
|
232
293
|
|
233
294
|
end
|
234
295
|
|
235
|
-
it
|
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
|
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
|
317
|
+
describe "#list" do
|
258
318
|
|
259
|
-
it
|
319
|
+
it "has an optional selection parameter, a hash" do
|
260
320
|
expect{ interface.list }.not_to raise_exception
|
261
|
-
expect{ interface.list(name:
|
321
|
+
expect{ interface.list(name: "Barney") }.not_to raise_exception
|
262
322
|
end
|
263
323
|
|
264
|
-
it
|
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
|
270
|
-
expect( interface.list(name:
|
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:
|
273
|
-
to include(name:
|
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
|
278
|
-
expect( interface.list(name:
|
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
|
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
|
346
|
+
it "creates a caching request" do
|
287
347
|
FakeRequester.clear_method
|
288
|
-
interface.list(name:
|
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:
|
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
|
304
|
-
|
361
|
+
describe "#update" do
|
305
362
|
let(:id) { interface.list.first[:id] }
|
306
363
|
|
307
|
-
it
|
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
|
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
|
384
|
+
describe "#delete" do
|
329
385
|
|
330
386
|
let(:id) { interface.list.first[:id] }
|
331
387
|
|
332
|
-
|
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
|
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
|
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
|
+
|