pod4 0.7.1 → 0.7.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 +4 -4
- data/.hgtags +1 -0
- data/Gemfile +10 -12
- data/lib/pod4/alert.rb +13 -16
- data/lib/pod4/basic_model.rb +15 -19
- data/lib/pod4/errors.rb +3 -4
- data/lib/pod4/interface.rb +25 -32
- data/lib/pod4/metaxing.rb +12 -15
- data/lib/pod4/model.rb +56 -81
- data/lib/pod4/nebulous_interface.rb +59 -74
- data/lib/pod4/null_interface.rb +8 -13
- data/lib/pod4/param.rb +2 -2
- data/lib/pod4/pg_interface.rb +42 -46
- data/lib/pod4/sequel_interface.rb +24 -36
- data/lib/pod4/tds_interface.rb +29 -37
- data/lib/pod4/typecasting.rb +7 -10
- data/lib/pod4/version.rb +1 -1
- data/lib/pod4.rb +8 -9
- data/md/fixme.md +0 -19
- data/pod4.gemspec +1 -1
- data/spec/jruby/pg_interface_spec.rb +16 -0
- data/spec/mri/pg_interface_spec.rb +13 -0
- data/spec/{common → mri}/sequel_interface_spec.rb +14 -1
- data/spec/mri/tds_interface_spec.rb +13 -0
- metadata +6 -7
@@ -8,25 +8,22 @@ module Pod4
|
|
8
8
|
##
|
9
9
|
# An interface to talk to a Nebulous Target.
|
10
10
|
#
|
11
|
-
# Each interface can only speak with one target, designated with #set_target
|
12
|
-
#
|
11
|
+
# Each interface can only speak with one target, designated with #set_target.# The developer must
|
12
|
+
# also set a unique ID key using #set_id_fld.
|
13
13
|
#
|
14
|
-
# The primary challenge here is to map the CRUDL methods (which interfaces
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# the verb name, and the rest are hash keys.
|
14
|
+
# The primary challenge here is to map the CRUDL methods (which interfaces contract to implement)
|
15
|
+
# to nebulous verbs. The programmer uses #set_verb for this purpose: the first parameter
|
16
|
+
# indicates the CRUDL method, the next is the verb name, and the rest are hash keys.
|
18
17
|
#
|
19
|
-
# In the case of the #create and #update methods, the list of keys controls
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# #set_id_fld.
|
18
|
+
# In the case of the #create and #update methods, the list of keys controls which parts of the
|
19
|
+
# incoming hash end up in the verb parameters, and in what order. For #update, the list must
|
20
|
+
# include the ID key that you gave to #set_id_fld.
|
23
21
|
#
|
24
|
-
# Parameters for the #list method similarly constrain how its selection
|
25
|
-
#
|
22
|
+
# Parameters for the #list method similarly constrain how its selection parameter is translated
|
23
|
+
# to a nebulous verb parameter string.
|
26
24
|
#
|
27
|
-
# Parameters for #read and #delete can be whatever you like, but since the
|
28
|
-
#
|
29
|
-
# same as the one in #set_id_fld.
|
25
|
+
# Parameters for #read and #delete can be whatever you like, but since the only value passed to
|
26
|
+
# read is the ID, the only symbol there should be the same as the one in #set_id_fld.
|
30
27
|
#
|
31
28
|
# class CustomerInterface < SwingShift::NebulousInterface
|
32
29
|
# set_target 'accord'
|
@@ -41,21 +38,20 @@ module Pod4
|
|
41
38
|
# end
|
42
39
|
# end
|
43
40
|
#
|
44
|
-
# In this example both the create and update methods point to the same
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# passed literally in the Nebulous parameter string.
|
41
|
+
# In this example both the create and update methods point to the same nebulous verb. Note that
|
42
|
+
# only keys which are symbols are translated to the corresponding values in the record or
|
43
|
+
# selection hash; anything else is passed literally in the Nebulous parameter string.
|
48
44
|
#
|
49
|
-
# When you subclass NebulousInterfce, you may want to override some or all of
|
50
|
-
#
|
51
|
-
#
|
45
|
+
# When you subclass NebulousInterfce, you may want to override some or all of the CRUDL methods
|
46
|
+
# so that your callers can pass specific parameters rather than a hash; the above example
|
47
|
+
# demonstrates this.
|
52
48
|
#
|
53
|
-
# We assume that the response to the #create message returns the ID as the
|
54
|
-
#
|
55
|
-
#
|
49
|
+
# We assume that the response to the #create message returns the ID as the parameter part of the
|
50
|
+
# success verb. If that's not true, then you will have to override #create and sort this out
|
51
|
+
# yourself.
|
56
52
|
#
|
57
|
-
# Finally, note that all values are returned as strings; there is no
|
58
|
-
#
|
53
|
+
# Finally, note that all values are returned as strings; there is no typecasting. This is a given
|
54
|
+
# limitation for Nebulous as a whole.
|
59
55
|
#
|
60
56
|
class NebulousInterface < Interface
|
61
57
|
|
@@ -72,8 +68,8 @@ module Pod4
|
|
72
68
|
# * :verbsuccess - we got a success verb in response
|
73
69
|
# * :response - we got some response that doesn't follow The Protocol
|
74
70
|
#
|
75
|
-
# NB: if we got an exception sending the message, we raised it on the
|
76
|
-
#
|
71
|
+
# NB: if we got an exception sending the message, we raised it on the caller, so there is no
|
72
|
+
# status for that.
|
77
73
|
attr_reader :response_status
|
78
74
|
|
79
75
|
|
@@ -82,9 +78,8 @@ module Pod4
|
|
82
78
|
|
83
79
|
class << self
|
84
80
|
#--
|
85
|
-
# These are set in the class because it keeps the model code cleaner: the
|
86
|
-
#
|
87
|
-
# out into the model.
|
81
|
+
# These are set in the class because it keeps the model code cleaner: the definition of the
|
82
|
+
# interface stays in the interface, and doesn't leak out into the model.
|
88
83
|
#++
|
89
84
|
|
90
85
|
##
|
@@ -94,8 +89,7 @@ module Pod4
|
|
94
89
|
# * parameters - array of symbols to order the hash passed to create, etc
|
95
90
|
#
|
96
91
|
def set_verb(action, verb, *paramKeys)
|
97
|
-
raise ArgumentError, "Bad action"
|
98
|
-
unless Interface::ACTIONS.include? action
|
92
|
+
raise ArgumentError, "Bad action" unless Interface::ACTIONS.include? action
|
99
93
|
|
100
94
|
v = verbs.dup
|
101
95
|
v[action] = Verb.new( verb, paramKeys.flatten )
|
@@ -110,6 +104,7 @@ module Pod4
|
|
110
104
|
# Set the name of the Nebulous target in the interface definition
|
111
105
|
#
|
112
106
|
# a reference to the interface object.
|
107
|
+
#
|
113
108
|
def set_target(target)
|
114
109
|
define_class_method(:target) {target.to_s}
|
115
110
|
end
|
@@ -120,8 +115,7 @@ module Pod4
|
|
120
115
|
|
121
116
|
|
122
117
|
##
|
123
|
-
# Set the name of the ID parameter (needs to be in the CRUD verbs param
|
124
|
-
# list)
|
118
|
+
# Set the name of the ID parameter (needs to be in the CRUD verbs param list)
|
125
119
|
def set_id_fld(idFld)
|
126
120
|
define_class_method(:id_fld) {idFld}
|
127
121
|
end
|
@@ -159,11 +153,10 @@ module Pod4
|
|
159
153
|
##
|
160
154
|
# In normal operation, takes no parameters.
|
161
155
|
#
|
162
|
-
# For testing purposes you may pass an instance of a class here. It must
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
# creating a NebRequest directly.
|
156
|
+
# For testing purposes you may pass an instance of a class here. It must respond to a #send
|
157
|
+
# method with parameters (verb, parameter string, cache yes/no) by returning some kind of
|
158
|
+
# NebRequest (presumably either a double or an instance of NebRequestNull). This method will be
|
159
|
+
# called instead of creating a NebRequest directly.
|
167
160
|
#
|
168
161
|
def initialize(requestObj=nil)
|
169
162
|
@request_object = requestObj # might as well be a reference
|
@@ -176,12 +169,12 @@ module Pod4
|
|
176
169
|
|
177
170
|
|
178
171
|
##
|
179
|
-
# Pass a parameter string or array (which will be taken as the literal
|
180
|
-
#
|
181
|
-
#
|
172
|
+
# Pass a parameter string or array (which will be taken as the literal Nebulous parameter) or a
|
173
|
+
# Hash or Octothorpe (which will be interpreted as per your list of keys set in add_verb
|
174
|
+
# :list).
|
182
175
|
#
|
183
|
-
# Returns an array of Octothorpes, or an empty array if the responder could
|
184
|
-
#
|
176
|
+
# Returns an array of Octothorpes, or an empty array if the responder could not make any
|
177
|
+
# records out of our message.
|
185
178
|
#
|
186
179
|
def list(selection=nil)
|
187
180
|
sel =
|
@@ -193,7 +186,7 @@ module Pod4
|
|
193
186
|
send_message( verb_for(:list), sel )
|
194
187
|
|
195
188
|
@response.body_to_h # should be an array irrespective of the method name
|
196
|
-
|
189
|
+
.map{|e| Octothorpe.new(e) }
|
197
190
|
|
198
191
|
rescue => e
|
199
192
|
handle_error(e)
|
@@ -201,17 +194,14 @@ module Pod4
|
|
201
194
|
|
202
195
|
|
203
196
|
##
|
204
|
-
# Pass a parameter string or an array as the record. returns the ID
|
205
|
-
#
|
206
|
-
#
|
207
|
-
# have to override #create and sort this out yourself.
|
197
|
+
# Pass a parameter string or an array as the record. returns the ID.# We assume that the
|
198
|
+
# response to the create message returns the ID as the parameter part of the success verb. If
|
199
|
+
# that's not true, then you will have to override #create and sort this out yourself.
|
208
200
|
#
|
209
201
|
def create(record)
|
210
|
-
raise ArgumentError, 'create takes a Hash or an Octothorpe'
|
211
|
-
unless hashy?(record)
|
202
|
+
raise ArgumentError, 'create takes a Hash or an Octothorpe' unless hashy?(record)
|
212
203
|
|
213
204
|
send_message( verb_for(:create), param_string(:create, record) )
|
214
|
-
|
215
205
|
@response.params
|
216
206
|
|
217
207
|
rescue => e
|
@@ -234,13 +224,11 @@ module Pod4
|
|
234
224
|
|
235
225
|
|
236
226
|
##
|
237
|
-
# Given an id an a record (Octothorpe or Hash), update the record.
|
238
|
-
# self.
|
227
|
+
# Given an id an a record (Octothorpe or Hash), update the record. Returns self.
|
239
228
|
#
|
240
229
|
def update(id, record)
|
241
230
|
raise ArgumentError, 'You must pass an ID to update' unless id
|
242
|
-
raise ArgumentError, 'update record takes a Hash or an Octothorpe'
|
243
|
-
unless hashy?(record)
|
231
|
+
raise ArgumentError, 'update record takes a Hash or an Octothorpe' unless hashy?(record)
|
244
232
|
|
245
233
|
send_message( verb_for(:update),
|
246
234
|
param_string(:update, record, id),
|
@@ -267,8 +255,9 @@ module Pod4
|
|
267
255
|
|
268
256
|
|
269
257
|
##
|
270
|
-
# Bonus method: chain this method before a CRUDL method to clear the cache
|
271
|
-
#
|
258
|
+
# Bonus method: chain this method before a CRUDL method to clear the cache for that parameter
|
259
|
+
# string:
|
260
|
+
#
|
272
261
|
# @interface.clearing_cache.read(14)
|
273
262
|
#
|
274
263
|
def clearing_cache
|
@@ -278,11 +267,10 @@ module Pod4
|
|
278
267
|
|
279
268
|
|
280
269
|
##
|
281
|
-
# Bonus method: send an arbitrary Nebulous message to the target and return
|
282
|
-
# the response object.
|
270
|
+
# Bonus method: send an arbitrary Nebulous message to the target and return the response object.
|
283
271
|
#
|
284
|
-
# We don't trap errors here - see #handle_error - but we raise extra ones
|
285
|
-
#
|
272
|
+
# We don't trap errors here - see #handle_error - but we raise extra ones if we think things
|
273
|
+
# look fishy.
|
286
274
|
#
|
287
275
|
def send_message(verb, paramStr, with_cache=true)
|
288
276
|
unless NebulousStomp.on?
|
@@ -305,8 +293,7 @@ module Pod4
|
|
305
293
|
else :response
|
306
294
|
end
|
307
295
|
|
308
|
-
raise Pod4::CantContinue, "Nebulous returned an error verb"
|
309
|
-
if @response_status == :verberror
|
296
|
+
raise Pod4::CantContinue, "Nebulous returned an error verb" if @response_status == :verberror
|
310
297
|
|
311
298
|
self
|
312
299
|
|
@@ -327,8 +314,8 @@ module Pod4
|
|
327
314
|
|
328
315
|
|
329
316
|
##
|
330
|
-
# Work out the parameter string based on the corresponding #set_Verb call.
|
331
|
-
#
|
317
|
+
# Work out the parameter string based on the corresponding #set_Verb call. Insert the ID value
|
318
|
+
# if given
|
332
319
|
#
|
333
320
|
def param_string(action, hashParam, id=nil)
|
334
321
|
hash = hashParam ? hashParam.dup : {}
|
@@ -346,8 +333,8 @@ module Pod4
|
|
346
333
|
##
|
347
334
|
# Deal with any exceptions that are raised.
|
348
335
|
#
|
349
|
-
# Our contract says that we should throw errors to the model, but those
|
350
|
-
# errors
|
336
|
+
# Our contract says that we should throw errors to the model, but those errors should be Pod4
|
337
|
+
# errors.
|
351
338
|
#
|
352
339
|
def handle_error(err, kaller=caller[1..-1])
|
353
340
|
Pod4.logger.error(__FILE__){ err.message }
|
@@ -372,9 +359,8 @@ module Pod4
|
|
372
359
|
|
373
360
|
|
374
361
|
##
|
375
|
-
# A little helper method to create a response object (unless we were given
|
376
|
-
#
|
377
|
-
# then send the message.
|
362
|
+
# A little helper method to create a response object (unless we were given one for testing
|
363
|
+
# purposes), clear the cache if we are supposed to, and then send the message.
|
378
364
|
#
|
379
365
|
# returns the response to the request.
|
380
366
|
#
|
@@ -399,7 +385,6 @@ module Pod4
|
|
399
385
|
obj.kind_of?(Hash) || obj.kind_of?(Octothorpe)
|
400
386
|
end
|
401
387
|
|
402
|
-
|
403
388
|
end
|
404
389
|
|
405
390
|
|
data/lib/pod4/null_interface.rb
CHANGED
@@ -16,8 +16,8 @@ module Pod4
|
|
16
16
|
# set_interface NullInterface.new( :one, :two [ {one: 1, two: 2} ] )
|
17
17
|
# ...
|
18
18
|
#
|
19
|
-
# The first column passed is taken to be the ID.
|
20
|
-
#
|
19
|
+
# The first column passed is taken to be the ID. Note that ID is not auto-assigned; you need to
|
20
|
+
# specify it in the record.
|
21
21
|
#
|
22
22
|
class NullInterface < Interface
|
23
23
|
|
@@ -25,8 +25,7 @@ module Pod4
|
|
25
25
|
|
26
26
|
|
27
27
|
##
|
28
|
-
# Initialise the interface by passing it a list of columns and an array of
|
29
|
-
# hashes to fill them.
|
28
|
+
# Initialise the interface by passing it a list of columns and an array of hashes to fill them.
|
30
29
|
#
|
31
30
|
def initialize(*cols, data)
|
32
31
|
raise ArgumentError, "no columns" if cols.nil? || cols == []
|
@@ -60,12 +59,11 @@ module Pod4
|
|
60
59
|
|
61
60
|
##
|
62
61
|
# Record is a hash of field: value
|
63
|
-
#
|
64
|
-
# new().
|
62
|
+
#
|
63
|
+
# Note that we will store any old crap, not just the fields you named in new().
|
65
64
|
#
|
66
65
|
def create(record)
|
67
|
-
raise(ArgumentError, "Create requires an ID")
|
68
|
-
if record.nil? || ! record.respond_to?(:to_h)
|
66
|
+
raise(ArgumentError, "Create requires an ID") if record.nil? || ! record.respond_to?(:to_h)
|
69
67
|
|
70
68
|
@data << record.to_h
|
71
69
|
record[@id_fld]
|
@@ -90,8 +88,7 @@ module Pod4
|
|
90
88
|
|
91
89
|
|
92
90
|
##
|
93
|
-
# ID is the first column you named in new()
|
94
|
-
# record should be a Hash or Octothorpe.
|
91
|
+
# ID is the first column you named in new(). Record should be a Hash or Octothorpe.
|
95
92
|
# Again, note that we don't care what columns you send us.
|
96
93
|
#
|
97
94
|
def update(id, record)
|
@@ -112,9 +109,7 @@ module Pod4
|
|
112
109
|
#
|
113
110
|
def delete(id)
|
114
111
|
raise(ArgumentError, "Delete requires an ID") if id.nil?
|
115
|
-
|
116
|
-
raise Pod4::CantContinue, "'No record found with ID '#{id}'" \
|
117
|
-
if read(id).empty?
|
112
|
+
raise(Pod4::CantContinue, "'No record found with ID '#{id}'") if read(id).empty?
|
118
113
|
|
119
114
|
@data.delete_if {|r| r[@id_fld] == id }
|
120
115
|
self
|
data/lib/pod4/param.rb
CHANGED
@@ -5,8 +5,8 @@ module Pod4
|
|
5
5
|
|
6
6
|
|
7
7
|
##
|
8
|
-
# This module implements the singleton pattern and is used internally to
|
9
|
-
#
|
8
|
+
# This module implements the singleton pattern and is used internally to store parameters passed
|
9
|
+
# to it from outside of Pod4
|
10
10
|
#
|
11
11
|
module Param
|
12
12
|
extend self
|
data/lib/pod4/pg_interface.rb
CHANGED
@@ -13,10 +13,9 @@ module Pod4
|
|
13
13
|
##
|
14
14
|
# Pod4 Interface for requests on a SQL table via pg, the PostgresQL adapter.
|
15
15
|
#
|
16
|
-
# If your DB table is one-one with your model, you shouldn't need to override
|
17
|
-
# anything.
|
18
|
-
#
|
16
|
+
# If your DB table is one-one with your model, you shouldn't need to override anything.
|
19
17
|
# Example:
|
18
|
+
#
|
20
19
|
# class CustomerInterface < SwingShift::PgInterface
|
21
20
|
# set_schema :public # optional
|
22
21
|
# set_table :customer
|
@@ -30,9 +29,8 @@ module Pod4
|
|
30
29
|
|
31
30
|
class << self
|
32
31
|
#--
|
33
|
-
# These are set in the class because it keeps the model code cleaner: the
|
34
|
-
#
|
35
|
-
# out into the model.
|
32
|
+
# These are set in the class because it keeps the model code cleaner: the definition of the
|
33
|
+
# interface stays in the interface, and doesn't leak out into the model.
|
36
34
|
#++
|
37
35
|
|
38
36
|
##
|
@@ -73,13 +71,12 @@ module Pod4
|
|
73
71
|
|
74
72
|
|
75
73
|
##
|
76
|
-
# Initialise the interface by passing it a Pg connection hash.
|
77
|
-
#
|
78
|
-
#
|
74
|
+
# Initialise the interface by passing it a Pg connection hash. For testing ONLY you can also
|
75
|
+
# pass an object which pretends to be a Pg client, in which case the hash is pretty much
|
76
|
+
# ignored.
|
79
77
|
#
|
80
78
|
def initialize(connectHash, testClient=nil)
|
81
|
-
raise(ArgumentError, 'invalid connection hash')
|
82
|
-
unless connectHash.kind_of?(Hash)
|
79
|
+
raise(ArgumentError, 'invalid connection hash') unless connectHash.kind_of?(Hash)
|
83
80
|
|
84
81
|
@connect_hash = connectHash.dup
|
85
82
|
@test_client = testClient
|
@@ -125,8 +122,9 @@ module Pod4
|
|
125
122
|
|
126
123
|
##
|
127
124
|
# Record is a hash of field: value
|
128
|
-
#
|
129
|
-
# which is just what we
|
125
|
+
#
|
126
|
+
# By a happy coincidence, insert returns the unique ID for the record, which is just what we
|
127
|
+
# want to do, too.
|
130
128
|
#
|
131
129
|
def create(record)
|
132
130
|
raise(ArgumentError, "Bad type for record parameter") \
|
@@ -161,8 +159,8 @@ module Pod4
|
|
161
159
|
Octothorpe.new( select(sql).first )
|
162
160
|
|
163
161
|
rescue => e
|
164
|
-
# Select has already wrapped the error in a Pod4Error, but in this case
|
165
|
-
#
|
162
|
+
# Select has already wrapped the error in a Pod4Error, but in this case we want to catch
|
163
|
+
# something
|
166
164
|
raise CantContinue, "That doesn't look like an ID" \
|
167
165
|
if e.cause.class == PG::InvalidTextRepresentation
|
168
166
|
|
@@ -171,8 +169,8 @@ module Pod4
|
|
171
169
|
|
172
170
|
|
173
171
|
##
|
174
|
-
# ID is whatever you set in the interface using set_id_fld
|
175
|
-
#
|
172
|
+
# ID is whatever you set in the interface using set_id_fld record should be a Hash or
|
173
|
+
# Octothorpe.
|
176
174
|
#
|
177
175
|
def update(id, record)
|
178
176
|
raise(ArgumentError, "Bad type for record parameter") \
|
@@ -211,15 +209,14 @@ module Pod4
|
|
211
209
|
##
|
212
210
|
# Run SQL code on the server. Return the results.
|
213
211
|
#
|
214
|
-
# Will return an array of records, or you can use it in block mode, like
|
215
|
-
# this:
|
212
|
+
# Will return an array of records, or you can use it in block mode, like this:
|
216
213
|
#
|
217
214
|
# select("select * from customer") do |r|
|
218
215
|
# # r is a single record
|
219
216
|
# end
|
220
217
|
#
|
221
|
-
# The returned results will be an array of hashes (or if you passed a
|
222
|
-
#
|
218
|
+
# The returned results will be an array of hashes (or if you passed a block, of whatever you
|
219
|
+
# returned from the block).
|
223
220
|
#
|
224
221
|
def select(sql)
|
225
222
|
raise(ArgumentError, "Bad SQL parameter") unless sql.kind_of?(String)
|
@@ -284,21 +281,19 @@ module Pod4
|
|
284
281
|
raise DataBaseError, "Bad Connection" \
|
285
282
|
unless client.status == PG::CONNECTION_OK
|
286
283
|
|
287
|
-
# This gives us type mapping for integers, floats, booleans, and dates
|
288
|
-
#
|
289
|
-
# strings... we fudge that elsewhere.
|
284
|
+
# This gives us type mapping for integers, floats, booleans, and dates -- but annoyingly the
|
285
|
+
# PostgreSQL types 'numeric' and 'money' remain as strings... we fudge that elsewhere.
|
290
286
|
#
|
291
|
-
# NOTE we now deal with ALL mapping elsewhere, since pg_jruby does
|
292
|
-
#
|
293
|
-
# seems to be a hell of a lot faster now...
|
287
|
+
# NOTE we now deal with ALL mapping elsewhere, since pg_jruby does not support type mapping.
|
288
|
+
# Also: no annoying error messages, and it seems to be a hell of a lot faster now...
|
294
289
|
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
290
|
+
# if defined?(PG::BasicTypeMapForQueries)
|
291
|
+
# client.type_map_for_queries = PG::BasicTypeMapForQueries.new(client)
|
292
|
+
# end
|
298
293
|
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
294
|
+
# if defined?(PG::BasicTypeMapForResults)
|
295
|
+
# client.type_map_for_results = PG::BasicTypeMapForResults.new(client)
|
296
|
+
# end
|
302
297
|
|
303
298
|
@client = client
|
304
299
|
self
|
@@ -310,8 +305,9 @@ module Pod4
|
|
310
305
|
|
311
306
|
##
|
312
307
|
# Close the connection to the database.
|
313
|
-
#
|
314
|
-
# caller will find it
|
308
|
+
#
|
309
|
+
# We don't actually use this, but it's here for completeness. Maybe a caller will find it
|
310
|
+
# useful.
|
315
311
|
#
|
316
312
|
def close
|
317
313
|
Pod4.logger.info(__FILE__){ "Closing connection to DB" }
|
@@ -339,8 +335,7 @@ module Pod4
|
|
339
335
|
|
340
336
|
|
341
337
|
##
|
342
|
-
# Since pg gives us @client.reset to reconnect, we should use it rather
|
343
|
-
# than just call open
|
338
|
+
# Since pg gives us @client.reset to reconnect, we should use it rather than just call open
|
344
339
|
#
|
345
340
|
def ensure_connection
|
346
341
|
|
@@ -377,8 +372,12 @@ module Pod4
|
|
377
372
|
def quote(fld)
|
378
373
|
|
379
374
|
case fld
|
380
|
-
when
|
375
|
+
when Date, Time
|
381
376
|
"'#{fld}'"
|
377
|
+
when String
|
378
|
+
"'#{fld.gsub("'", "''")}'"
|
379
|
+
when Symbol
|
380
|
+
"'#{fld.to_s.gsub("'", "''")}'"
|
382
381
|
when BigDecimal
|
383
382
|
fld.to_f
|
384
383
|
when nil
|
@@ -407,9 +406,9 @@ module Pod4
|
|
407
406
|
|
408
407
|
##
|
409
408
|
# Cast a query row
|
410
|
-
#
|
411
|
-
# There is definitely a way to tell pg to
|
412
|
-
# BigDecimal, but, it's not documented
|
409
|
+
#
|
410
|
+
# This is to step around problems with pg type mapping There is definitely a way to tell pg to
|
411
|
+
# cast money and numeric as BigDecimal, but, it's not documented...
|
413
412
|
#
|
414
413
|
# Also, for the pg_jruby gem, type mapping doesn't work at all?
|
415
414
|
#
|
@@ -447,13 +446,10 @@ module Pod4
|
|
447
446
|
|
448
447
|
|
449
448
|
def read_or_die(id)
|
450
|
-
raise CantContinue, "'No record found with ID '#{id}'"
|
451
|
-
if read(id).empty?
|
452
|
-
|
449
|
+
raise CantContinue, "'No record found with ID '#{id}'" if read(id).empty?
|
453
450
|
end
|
454
451
|
|
455
|
-
|
456
|
-
|
457
452
|
end
|
458
453
|
|
454
|
+
|
459
455
|
end
|
@@ -10,8 +10,7 @@ module Pod4
|
|
10
10
|
##
|
11
11
|
# Pod4 Interface for a Sequel table.
|
12
12
|
#
|
13
|
-
# If your DB table is one-one with your model, you shouldn't need to override
|
14
|
-
# anything.
|
13
|
+
# If your DB table is one-one with your model, you shouldn't need to override anything.
|
15
14
|
#
|
16
15
|
# Example:
|
17
16
|
# class CustomerInterface < SwingShift::SequelInterface
|
@@ -19,9 +18,9 @@ module Pod4
|
|
19
18
|
# set_id_fld :id
|
20
19
|
# end
|
21
20
|
#
|
22
|
-
# Data types: Sequel itself will translate to BigDecimal, Float, Integer,
|
23
|
-
#
|
24
|
-
#
|
21
|
+
# Data types: Sequel itself will translate to BigDecimal, Float, Integer, date, and datetime as
|
22
|
+
# appropriate -- but it also depends on the underlying adapter. TinyTds maps dates to strings,
|
23
|
+
# for example.
|
25
24
|
#
|
26
25
|
class SequelInterface < Interface
|
27
26
|
|
@@ -30,9 +29,8 @@ module Pod4
|
|
30
29
|
|
31
30
|
class << self
|
32
31
|
#---
|
33
|
-
# These are set in the class because it keeps the model code cleaner: the
|
34
|
-
#
|
35
|
-
# out into the model.
|
32
|
+
# These are set in the class because it keeps the model code cleaner: the definition of the
|
33
|
+
# interface stays in the interface, and doesn't leak out into the model.
|
36
34
|
#+++
|
37
35
|
|
38
36
|
|
@@ -78,12 +76,8 @@ module Pod4
|
|
78
76
|
#
|
79
77
|
def initialize(db)
|
80
78
|
raise(ArgumentError, "Bad database") unless db.kind_of? Sequel::Database
|
81
|
-
|
82
|
-
raise(Pod4Error, 'no call to
|
83
|
-
if self.class.table.nil?
|
84
|
-
|
85
|
-
raise(Pod4Error, 'no call to set_id_fld in the interface definition') \
|
86
|
-
if self.class.id_fld.nil?
|
79
|
+
raise(Pod4Error, 'no call to set_table in the interface definition') if self.class.table.nil?
|
80
|
+
raise(Pod4Error, 'no call to set_id_fld in the interface definition') if self.class.id_fld.nil?
|
87
81
|
|
88
82
|
@db = db # referemce to the db object
|
89
83
|
@table = db[schema ? "#{schema}__#{table}".to_sym : table]
|
@@ -113,10 +107,7 @@ module Pod4
|
|
113
107
|
#
|
114
108
|
def list(selection=nil)
|
115
109
|
sel = sanitise_hash(selection)
|
116
|
-
|
117
|
-
Pod4.logger.debug(__FILE__) do
|
118
|
-
"Listing #{self.class.table}: #{sel.inspect}"
|
119
|
-
end
|
110
|
+
Pod4.logger.debug(__FILE__) { "Listing #{self.class.table}: #{sel.inspect}" }
|
120
111
|
|
121
112
|
(sel ? @table.where(sel) : @table.all).map {|x| Octothorpe.new(x) }
|
122
113
|
rescue => e
|
@@ -126,16 +117,15 @@ module Pod4
|
|
126
117
|
|
127
118
|
##
|
128
119
|
# Record is a hash of field: value
|
129
|
-
#
|
130
|
-
# which is just what we
|
120
|
+
#
|
121
|
+
# By a happy coincidence, insert returns the unique ID for the record, which is just what we
|
122
|
+
# want to do, too.
|
131
123
|
#
|
132
124
|
def create(record)
|
133
125
|
raise(ArgumentError, "Bad type for record parameter") \
|
134
126
|
unless record.kind_of?(Hash) || record.kind_of?(Octothorpe)
|
135
127
|
|
136
|
-
Pod4.logger.debug(__FILE__)
|
137
|
-
"Creating #{self.class.table}: #{record.inspect}"
|
138
|
-
end
|
128
|
+
Pod4.logger.debug(__FILE__) { "Creating #{self.class.table}: #{record.inspect}" }
|
139
129
|
|
140
130
|
@table.insert( sanitise_hash(record.to_h) )
|
141
131
|
|
@@ -149,10 +139,7 @@ module Pod4
|
|
149
139
|
#
|
150
140
|
def read(id)
|
151
141
|
raise(ArgumentError, "ID parameter is nil") if id.nil?
|
152
|
-
|
153
|
-
Pod4.logger.debug(__FILE__) do
|
154
|
-
"Reading #{self.class.table} where #{@id_fld}=#{id}"
|
155
|
-
end
|
142
|
+
Pod4.logger.debug(__FILE__) { "Reading #{self.class.table} where #{@id_fld}=#{id}" }
|
156
143
|
|
157
144
|
Octothorpe.new( @table[@id_fld => id] )
|
158
145
|
|
@@ -165,8 +152,8 @@ module Pod4
|
|
165
152
|
|
166
153
|
|
167
154
|
##
|
168
|
-
# ID is whatever you set in the interface using set_id_fld
|
169
|
-
#
|
155
|
+
# ID is whatever you set in the interface using set_id_fld record should be a Hash or
|
156
|
+
# Octothorpe.
|
170
157
|
#
|
171
158
|
def update(id, record)
|
172
159
|
read_or_die(id)
|
@@ -213,8 +200,7 @@ module Pod4
|
|
213
200
|
|
214
201
|
|
215
202
|
##
|
216
|
-
# Bonus method: execute arbitrary SQL and return the resulting dataset as a
|
217
|
-
# Hash.
|
203
|
+
# Bonus method: execute arbitrary SQL and return the resulting dataset as a Hash.
|
218
204
|
#
|
219
205
|
def select(sql)
|
220
206
|
raise(ArgumentError, "Bad sql parameter") unless sql.kind_of?(String)
|
@@ -231,8 +217,9 @@ module Pod4
|
|
231
217
|
|
232
218
|
##
|
233
219
|
# Helper routine to handle or re-raise the right exception.
|
234
|
-
#
|
235
|
-
# is
|
220
|
+
#
|
221
|
+
# Unless kaller is passed, we re-raise on the caller of the caller, which is likely the
|
222
|
+
# original bug
|
236
223
|
#
|
237
224
|
def handle_error(err, kaller=nil)
|
238
225
|
kaller ||= caller[1..-1]
|
@@ -269,9 +256,9 @@ module Pod4
|
|
269
256
|
|
270
257
|
|
271
258
|
##
|
272
|
-
# Sequel behaves VERY oddly if you pass a symbol as a value to the hash you
|
273
|
-
#
|
274
|
-
#
|
259
|
+
# Sequel behaves VERY oddly if you pass a symbol as a value to the hash you give to a
|
260
|
+
# selection,etc on a dataset. (It raises an error complaining that the symbol does not exist as
|
261
|
+
# a column in the table...)
|
275
262
|
#
|
276
263
|
def sanitise_hash(sel)
|
277
264
|
|
@@ -300,3 +287,4 @@ module Pod4
|
|
300
287
|
|
301
288
|
|
302
289
|
end
|
290
|
+
|