quickbase_client 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -0
- data/lib/QuickBaseClient.rb +145 -91
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -29,6 +29,8 @@ More information about the QuickBase Client is available here -
|
|
29
29
|
|
30
30
|
== Change History
|
31
31
|
|
32
|
+
1.0.3 - 12/07/2010 - Various small method improvements.
|
33
|
+
|
32
34
|
1.0.2 - 12/05/2010 - Added removeFileAttachment().
|
33
35
|
|
34
36
|
1.0.1 - 11/26/2010 - Will now use HTTPClient instead of Net::HTTP, if you already have HTTPClient.
|
data/lib/QuickBaseClient.rb
CHANGED
@@ -13,10 +13,16 @@
|
|
13
13
|
|
14
14
|
require 'rexml/document'
|
15
15
|
require 'net/https'
|
16
|
-
require 'socket'
|
17
16
|
require 'json'
|
18
17
|
require 'QuickBaseMisc'
|
19
18
|
|
19
|
+
begin
|
20
|
+
require 'httpclient'
|
21
|
+
USING_HTTPCLIENT = true
|
22
|
+
rescue LoadError
|
23
|
+
USING_HTTPCLIENT = false
|
24
|
+
end
|
25
|
+
|
20
26
|
module QuickBase
|
21
27
|
|
22
28
|
# QuickBase client: Version 1.0.0: Ruby wrapper class for QuickBase HTTP API.
|
@@ -144,25 +150,36 @@ class Client
|
|
144
150
|
|
145
151
|
# Initializes the connection to QuickBase.
|
146
152
|
def setHTTPConnection( useSSL, org = "www", domain = "quickbase", proxy_options = nil )
|
153
|
+
@useSSL = useSSL
|
147
154
|
@org = org
|
148
155
|
@domain = domain
|
149
|
-
if
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
156
|
+
if USING_HTTPCLIENT
|
157
|
+
if proxy_options
|
158
|
+
@httpConnection = HTTPClient.new( "#{proxy_options["proxy_server"]}:#{proxy_options["proxy_port"] || useSSL ? "443" : "80"}" )
|
159
|
+
@httpConnection.set_auth(proxy_options["proxy_server"], proxy_options["proxy_user"], proxy_options["proxy_password"])
|
160
|
+
else
|
161
|
+
@httpConnection = HTTPClient.new
|
162
|
+
end
|
163
|
+
else
|
164
|
+
if proxy_options
|
165
|
+
@httpProxy = Net::HTTP::Proxy(proxy_options["proxy_server"], proxy_options["proxy_port"], proxy_options["proxy_user"], proxy_options["proxy_password"])
|
166
|
+
@httpConnection = @httpProxy.new( "#{@org}.#{@domain}.com", useSSL ? 443 : 80)
|
167
|
+
else
|
168
|
+
@httpConnection = Net::HTTP.new( "#{@org}.#{@domain}.com", useSSL ? 443 : 80 )
|
169
|
+
end
|
170
|
+
@httpConnection.use_ssl = useSSL
|
171
|
+
@httpConnection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
172
|
+
end
|
157
173
|
end
|
158
174
|
|
159
175
|
# Causes useful information to be printed to the screen for every HTTP request.
|
160
176
|
def debugHTTPConnection()
|
161
|
-
@httpConnection.set_debug_output $stdout if @httpConnection
|
177
|
+
@httpConnection.set_debug_output $stdout if @httpConnection and USING_HTTPCLIENT == false
|
162
178
|
end
|
163
179
|
|
164
180
|
# Sets the QuickBase URL and port to use for requests.
|
165
181
|
def setqbhost( useSSL, org = "www", domain = "quickbase" )
|
182
|
+
@useSSL = useSSL
|
166
183
|
@org = org
|
167
184
|
@domain = domain
|
168
185
|
@qbhost = useSSL ? "https://#{@org}.#{@domain}.com:443" : "http://#{@org}.#{@domain}.com"
|
@@ -216,8 +233,14 @@ class Client
|
|
216
233
|
begin
|
217
234
|
|
218
235
|
# send the request
|
219
|
-
|
220
|
-
|
236
|
+
if USING_HTTPCLIENT
|
237
|
+
response = @httpConnection.post( @requestURL, @requestXML, @requestHeaders )
|
238
|
+
@responseCode = response.status
|
239
|
+
@responseXML = response.content
|
240
|
+
else
|
241
|
+
@responseCode, @responseXML = @httpConnection.post( @requestURL, @requestXML, @requestHeaders )
|
242
|
+
end
|
243
|
+
|
221
244
|
printResponse( @responseCode, @responseXML ) if @printRequestsAndResponses
|
222
245
|
|
223
246
|
if not isHTMLRequest
|
@@ -352,7 +375,7 @@ class Client
|
|
352
375
|
fire( "onProcessResponse" )
|
353
376
|
|
354
377
|
parseResponseXML( responseXML )
|
355
|
-
@ticket
|
378
|
+
@ticket ||= getResponseValue( :ticket )
|
356
379
|
@udata = getResponseValue( :udata )
|
357
380
|
getErrorInfoFromResponse
|
358
381
|
end
|
@@ -388,6 +411,7 @@ class Client
|
|
388
411
|
# Gets the value for a specific field at the top level
|
389
412
|
# of the XML returned from QuickBase.
|
390
413
|
def getResponseValue( field )
|
414
|
+
@fieldValue = nil
|
391
415
|
if field and @responseXMLdoc
|
392
416
|
@fieldValue = @responseXMLdoc.root.elements[ field.to_s ]
|
393
417
|
@fieldValue = fieldValue.text if fieldValue and fieldValue.has_text?
|
@@ -490,13 +514,14 @@ class Client
|
|
490
514
|
|
491
515
|
# Returns the value of a field property, or nil.
|
492
516
|
def lookupFieldPropertyByName( fieldName, property )
|
517
|
+
theproperty = nil
|
493
518
|
if isValidFieldProperty?(property)
|
494
519
|
fid = lookupFieldIDByName( fieldName )
|
495
520
|
field = lookupField( fid ) if fid
|
496
|
-
theproperty = nil
|
497
521
|
theproperty = field.elements[ property ] if field
|
498
522
|
theproperty = theproperty.text if theproperty and theproperty.has_text?
|
499
523
|
end
|
524
|
+
theproperty
|
500
525
|
end
|
501
526
|
|
502
527
|
# Returns whether a field will show a Total on reports.
|
@@ -691,7 +716,7 @@ class Client
|
|
691
716
|
|
692
717
|
# Returns an array of XML sub-elements with the specified attribute value.
|
693
718
|
def findElementsByAttributeValue( elements, attribute_name, attribute_value )
|
694
|
-
elementArray =
|
719
|
+
elementArray = []
|
695
720
|
if elements
|
696
721
|
if elements.is_a?( REXML::Element )
|
697
722
|
elements.each_element_with_attribute( attribute_name, attribute_value ) { |e| elementArray << e }
|
@@ -708,7 +733,7 @@ class Client
|
|
708
733
|
|
709
734
|
# Returns an array of XML sub-elements with the specified attribute name.
|
710
735
|
def findElementsByAttributeName( elements, attribute_name )
|
711
|
-
elementArray =
|
736
|
+
elementArray = []
|
712
737
|
if elements
|
713
738
|
elements.each_element_with_attribute( attribute_name ) { |e| elementArray << e }
|
714
739
|
end
|
@@ -955,7 +980,7 @@ class Client
|
|
955
980
|
if getSchema(dbid)
|
956
981
|
if @chdbids
|
957
982
|
chdbidArray = findElementsByAttributeName( @chdbids, "name" )
|
958
|
-
|
983
|
+
numTables = chdbidArray.length
|
959
984
|
end
|
960
985
|
end
|
961
986
|
numTables
|
@@ -990,11 +1015,9 @@ class Client
|
|
990
1015
|
|
991
1016
|
# Returns whether a given string represents a valid QuickBase field type.
|
992
1017
|
def isValidFieldType?( type )
|
993
|
-
|
994
|
-
@validFieldTypes = %w{ checkbox dblink date duration email file fkey float formula currency
|
1018
|
+
@validFieldTypes ||= %w{ checkbox dblink date duration email file fkey float formula currency
|
995
1019
|
lookup phone percent rating recordid text timeofday timestamp url userid icalendarbutton }
|
996
|
-
|
997
|
-
ret = @validFieldTypes.include?( type )
|
1020
|
+
@validFieldTypes.include?( type )
|
998
1021
|
end
|
999
1022
|
|
1000
1023
|
# Returns a field type string given the more human-friendly label for a field type.
|
@@ -1109,7 +1132,7 @@ class Client
|
|
1109
1132
|
# * filename: if the field is a file attachment field, the name of the file that should be displayed in QuickBase.
|
1110
1133
|
# * value: the value to set in this field. If the field is a file attachment field, the name of the file that should be uploaded into QuickBase.
|
1111
1134
|
def addFieldValuePair( name, fid, filename, value )
|
1112
|
-
@fvlist
|
1135
|
+
@fvlist ||= []
|
1113
1136
|
@fvlist << FieldValuePairXML.new( self, name, fid, filename, value ).to_s
|
1114
1137
|
@fvlist
|
1115
1138
|
end
|
@@ -1147,7 +1170,7 @@ class Client
|
|
1147
1170
|
fid = lookupField( id )
|
1148
1171
|
if fid
|
1149
1172
|
fname = lookupFieldNameFromID( id )
|
1150
|
-
@fnames
|
1173
|
+
@fnames ||= []
|
1151
1174
|
@fnames << fname
|
1152
1175
|
else
|
1153
1176
|
raise "verifyFieldList: '#{id}' is not a valid field ID"
|
@@ -1162,7 +1185,7 @@ class Client
|
|
1162
1185
|
fnames.each { |name|
|
1163
1186
|
fid = lookupFieldIDByName( name )
|
1164
1187
|
if fid
|
1165
|
-
@fids
|
1188
|
+
@fids ||= []
|
1166
1189
|
@fids << fid
|
1167
1190
|
else
|
1168
1191
|
raise "verifyFieldList: '#{name}' is not a valid field name"
|
@@ -1230,8 +1253,8 @@ class Client
|
|
1230
1253
|
queryOperator = ""
|
1231
1254
|
|
1232
1255
|
if @queryOperators.nil?
|
1233
|
-
@queryOperators =
|
1234
|
-
@queryOperatorFieldType =
|
1256
|
+
@queryOperators = {}
|
1257
|
+
@queryOperatorFieldType = {}
|
1235
1258
|
|
1236
1259
|
@queryOperators[ "CT" ] = [ "contains", "[]" ]
|
1237
1260
|
@queryOperators[ "XCT" ] = [ "does not contain", "![]" ]
|
@@ -1288,16 +1311,20 @@ class Client
|
|
1288
1311
|
|
1289
1312
|
# Get a field's base type using its name.
|
1290
1313
|
def lookupBaseFieldTypeByName( fieldName )
|
1314
|
+
type = ""
|
1291
1315
|
fid = lookupFieldIDByName( fieldName )
|
1292
1316
|
field = lookupField( fid ) if fid
|
1293
1317
|
type = field.attributes[ "base_type" ] if field
|
1318
|
+
type
|
1294
1319
|
end
|
1295
1320
|
|
1296
1321
|
# Get a field's type using its name.
|
1297
1322
|
def lookupFieldTypeByName( fieldName )
|
1323
|
+
type = ""
|
1298
1324
|
fid = lookupFieldIDByName( fieldName )
|
1299
1325
|
field = lookupField( fid ) if fid
|
1300
1326
|
type = field.attributes[ "field_type" ] if field
|
1327
|
+
type
|
1301
1328
|
end
|
1302
1329
|
|
1303
1330
|
# Returns the string required for emebedding CSV data in a request.
|
@@ -1438,7 +1465,7 @@ class Client
|
|
1438
1465
|
# Converts a string into an array, given a field separator.
|
1439
1466
|
# '"' followed by the field separator are treated the same way as just the field separator.
|
1440
1467
|
def splitString( string, fieldSeparator = "," )
|
1441
|
-
ra =
|
1468
|
+
ra = []
|
1442
1469
|
string.chomp!
|
1443
1470
|
if string.include?( "\"" )
|
1444
1471
|
a=string.split( "\"#{fieldSeparator}" )
|
@@ -1456,7 +1483,7 @@ class Client
|
|
1456
1483
|
# Returns the URL-encoded version of a non-printing character.
|
1457
1484
|
def escapeXML( char )
|
1458
1485
|
if @xmlEscapes.nil?
|
1459
|
-
@xmlEscapes =
|
1486
|
+
@xmlEscapes = {}
|
1460
1487
|
(0..255).each{ |i| @xmlEscapes[ i.chr ] = sprintf( "&#%03d;", i ) }
|
1461
1488
|
end
|
1462
1489
|
return @xmlEscapes[ char ] if @xmlEscapes[ char ]
|
@@ -1556,10 +1583,10 @@ class Client
|
|
1556
1583
|
if @events.include?( event )
|
1557
1584
|
if handler and handler.is_a?( EventHandler )
|
1558
1585
|
if @eventSubscribers.nil?
|
1559
|
-
@eventSubscribers =
|
1586
|
+
@eventSubscribers = {}
|
1560
1587
|
end
|
1561
1588
|
if not @eventSubscribers.include?( event )
|
1562
|
-
@eventSubscribers[ event ] =
|
1589
|
+
@eventSubscribers[ event ] = []
|
1563
1590
|
end
|
1564
1591
|
if not @eventSubscribers[ event ].include?( handler )
|
1565
1592
|
@eventSubscribers[ event ] << handler
|
@@ -1741,7 +1768,7 @@ class Client
|
|
1741
1768
|
# API_DeleteAppZip
|
1742
1769
|
def deleteAppZip( dbid )
|
1743
1770
|
@dbid = dbid
|
1744
|
-
sendRequest( :deleteAppZip
|
1771
|
+
sendRequest( :deleteAppZip )
|
1745
1772
|
return self if @chainAPIcalls
|
1746
1773
|
@responseCode
|
1747
1774
|
end
|
@@ -1864,7 +1891,7 @@ class Client
|
|
1864
1891
|
# API_ChangeRecordOwner
|
1865
1892
|
def _changeRecordOwner( rid, newowner ) changeRecordOwner( @dbid, rid, newowner ) end
|
1866
1893
|
|
1867
|
-
# API_ChangeUserRole
|
1894
|
+
# API_ChangeUserRole.
|
1868
1895
|
def changeUserRole( dbid, userid, roleid, newroleid )
|
1869
1896
|
@dbid, @userid, @roleid, @newroleid = dbid, userid, roleid, newroleid
|
1870
1897
|
|
@@ -2027,8 +2054,12 @@ class Client
|
|
2027
2054
|
|
2028
2055
|
return self if @chainAPIcalls
|
2029
2056
|
|
2030
|
-
if
|
2031
|
-
@records
|
2057
|
+
if block_given?
|
2058
|
+
if @records
|
2059
|
+
@records.each { |element| yield element }
|
2060
|
+
else
|
2061
|
+
yield nil
|
2062
|
+
end
|
2032
2063
|
else
|
2033
2064
|
@records
|
2034
2065
|
end
|
@@ -2071,7 +2102,7 @@ class Client
|
|
2071
2102
|
@numMatches
|
2072
2103
|
end
|
2073
2104
|
|
2074
|
-
#
|
2105
|
+
# API_DoQueryCount, using the active table id.
|
2075
2106
|
def _doQueryCount( query ) doQueryCount( @dbid, query ) end
|
2076
2107
|
|
2077
2108
|
# Download a file's contents from a file attachment field in QuickBase.
|
@@ -2082,7 +2113,7 @@ class Client
|
|
2082
2113
|
|
2083
2114
|
@downLoadFileURL = "http://#{@org}.#{@domain}.com/up/#{dbid}/a/r#{rid}/e#{fid}/v#{vid}"
|
2084
2115
|
|
2085
|
-
if @
|
2116
|
+
if @useSSL
|
2086
2117
|
@downLoadFileURL.gsub!( "http:", "https:" )
|
2087
2118
|
end
|
2088
2119
|
|
@@ -2097,8 +2128,14 @@ class Client
|
|
2097
2128
|
|
2098
2129
|
begin
|
2099
2130
|
|
2100
|
-
|
2101
|
-
|
2131
|
+
if USING_HTTPCLIENT
|
2132
|
+
response = @httpConnection.get( @downLoadFileURL, nil, @requestHeaders )
|
2133
|
+
@responseCode = response.status
|
2134
|
+
@fileContents = response.body.content if response.body
|
2135
|
+
else
|
2136
|
+
@responseCode, @fileContents = @httpConnection.get( @downLoadFileURL, @requestHeaders )
|
2137
|
+
end
|
2138
|
+
|
2102
2139
|
rescue Net::HTTPBadResponse => @lastError
|
2103
2140
|
rescue Net::HTTPHeaderSyntaxError => @lastError
|
2104
2141
|
rescue StandardError => @lastError
|
@@ -2362,6 +2399,7 @@ class Client
|
|
2362
2399
|
|
2363
2400
|
@dbid, @pageid, @pagename = dbid, pageid, pagename
|
2364
2401
|
|
2402
|
+
xmlRequestData = nil
|
2365
2403
|
if @pageid
|
2366
2404
|
xmlRequestData = toXML( :pageid, @pageid )
|
2367
2405
|
elsif @pagename
|
@@ -2683,7 +2721,7 @@ class Client
|
|
2683
2721
|
def importFromCSV( dbid, records_csv, clist, skipfirst = nil, msInUTC = nil )
|
2684
2722
|
|
2685
2723
|
@dbid, @records_csv, @clist, @skipfirst, @msInUTC = dbid, records_csv, clist, skipfirst, msInUTC
|
2686
|
-
@clist
|
2724
|
+
@clist ||= "0"
|
2687
2725
|
|
2688
2726
|
xmlRequestData = toXML( :records_csv, @records_csv )
|
2689
2727
|
xmlRequestData << toXML( :clist, @clist )
|
@@ -2726,8 +2764,12 @@ class Client
|
|
2726
2764
|
|
2727
2765
|
@pages = getResponseValue( :pages )
|
2728
2766
|
return self if @chainAPIcalls
|
2729
|
-
if
|
2730
|
-
@pages
|
2767
|
+
if block_given?
|
2768
|
+
if @pages
|
2769
|
+
@pages.each{ |element| yield element }
|
2770
|
+
else
|
2771
|
+
yield nil
|
2772
|
+
end
|
2731
2773
|
else
|
2732
2774
|
@pages
|
2733
2775
|
end
|
@@ -2841,7 +2883,7 @@ class Client
|
|
2841
2883
|
sendRequest( :setAppData )
|
2842
2884
|
|
2843
2885
|
return self if @chainAPIcalls
|
2844
|
-
|
2886
|
+
@appdata
|
2845
2887
|
end
|
2846
2888
|
|
2847
2889
|
# API_SetAppData, using the active table id.
|
@@ -3196,10 +3238,10 @@ class Client
|
|
3196
3238
|
|
3197
3239
|
getSchema(dbid)
|
3198
3240
|
|
3199
|
-
values =
|
3200
|
-
fieldIDs =
|
3241
|
+
values = {}
|
3242
|
+
fieldIDs = {}
|
3201
3243
|
if fieldNames and fieldNames.is_a?( String )
|
3202
|
-
values[ fieldNames ] =
|
3244
|
+
values[ fieldNames ] = []
|
3203
3245
|
fieldID = lookupFieldIDByName( fieldNames )
|
3204
3246
|
if fieldID
|
3205
3247
|
fieldIDs[ fieldNames ] = fieldID
|
@@ -3209,7 +3251,7 @@ class Client
|
|
3209
3251
|
elsif fieldNames and fieldNames.is_a?( Array )
|
3210
3252
|
fieldNames.each{ |name|
|
3211
3253
|
if name
|
3212
|
-
values[ name ] =
|
3254
|
+
values[ name ] = []
|
3213
3255
|
fieldID = lookupFieldIDByName( name )
|
3214
3256
|
if fieldID
|
3215
3257
|
fieldIDs[ fieldID ] = name
|
@@ -3220,7 +3262,7 @@ class Client
|
|
3220
3262
|
}
|
3221
3263
|
elsif fieldNames.nil?
|
3222
3264
|
getFieldNames(dbid).each{|name|
|
3223
|
-
values[ name ] =
|
3265
|
+
values[ name ] = []
|
3224
3266
|
fieldID = lookupFieldIDByName( name )
|
3225
3267
|
fieldIDs[ fieldID ] = name
|
3226
3268
|
}
|
@@ -3341,7 +3383,7 @@ class Client
|
|
3341
3383
|
numRecords = 0
|
3342
3384
|
numRecords = queryResults[fieldNames[0]].length if queryResults[fieldNames[0]]
|
3343
3385
|
(0..(numRecords-1)).each{|recNum|
|
3344
|
-
recordHash =
|
3386
|
+
recordHash = {}
|
3345
3387
|
fieldNames.each{|fieldName|
|
3346
3388
|
recordHash[fieldName]=queryResults[fieldName][recNum]
|
3347
3389
|
}
|
@@ -3356,8 +3398,8 @@ class Client
|
|
3356
3398
|
# Same as iterateRecords but with fields optionally filtered by Ruby regular expressions.
|
3357
3399
|
# e.g. iterateFilteredRecords( "dhnju5y7", [{"Name" => "[A-E].+}","Address"] ) { |values| puts values["Name"], values["Address"] }
|
3358
3400
|
def iterateFilteredRecords( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3359
|
-
fields =
|
3360
|
-
regexp =
|
3401
|
+
fields = []
|
3402
|
+
regexp = {}
|
3361
3403
|
fieldNames.each{|field|
|
3362
3404
|
if field.is_a?(Hash)
|
3363
3405
|
fields << field.keys[0]
|
@@ -3403,10 +3445,10 @@ class Client
|
|
3403
3445
|
if tablesAndFields and tablesAndFields.is_a?(Array)
|
3404
3446
|
|
3405
3447
|
# get all the records from QuickBase that we might need - fewer API calls is faster than processing extra data
|
3406
|
-
tables =
|
3407
|
-
numRecords =
|
3408
|
-
tableRecords =
|
3409
|
-
joinfield =
|
3448
|
+
tables = []
|
3449
|
+
numRecords = {}
|
3450
|
+
tableRecords = {}
|
3451
|
+
joinfield = {}
|
3410
3452
|
|
3411
3453
|
tablesAndFields.each{|tableAndFields|
|
3412
3454
|
if tableAndFields and tableAndFields.is_a?(Hash)
|
@@ -3445,10 +3487,10 @@ class Client
|
|
3445
3487
|
joinfieldValue = tableRecords[tables[0]][joinfield[tables[0]]][i]
|
3446
3488
|
|
3447
3489
|
# save the other tables' record indices of records containing the joinfield value
|
3448
|
-
tableIndices =
|
3490
|
+
tableIndices = []
|
3449
3491
|
|
3450
3492
|
(1..(numTables-1)).each{|tableNum|
|
3451
|
-
tableIndices[tableNum] =
|
3493
|
+
tableIndices[tableNum] = []
|
3452
3494
|
(0..(numRecords[tables[tableNum]]-1)).each{|j|
|
3453
3495
|
if joinfieldValue == tableRecords[tables[tableNum]][joinfield[tables[tableNum]]][j]
|
3454
3496
|
tableIndices[tableNum] << j
|
@@ -3464,14 +3506,14 @@ class Client
|
|
3464
3506
|
|
3465
3507
|
if buildJoinRecord
|
3466
3508
|
|
3467
|
-
joinRecord =
|
3509
|
+
joinRecord = {}
|
3468
3510
|
|
3469
3511
|
tableRecords[tables[0]].each_key{|field|
|
3470
3512
|
joinRecord[field] = tableRecords[tables[0]][field][i]
|
3471
3513
|
}
|
3472
3514
|
|
3473
3515
|
# nested loops for however many tables we have
|
3474
|
-
currentIndex =
|
3516
|
+
currentIndex = []
|
3475
3517
|
numTables.times{ currentIndex << 0 }
|
3476
3518
|
loop {
|
3477
3519
|
(1..(numTables-1)).each{|tableNum|
|
@@ -3521,7 +3563,7 @@ class Client
|
|
3521
3563
|
|
3522
3564
|
if tables and tables.is_a?(Array)
|
3523
3565
|
if fieldNames and fieldNames.is_a?(Array)
|
3524
|
-
tableRecords =
|
3566
|
+
tableRecords = []
|
3525
3567
|
tables.each{|table|
|
3526
3568
|
if table and table.is_a?(Hash) and table["dbid"]
|
3527
3569
|
tableRecords << getAllValuesForFields( table["dbid"],
|
@@ -3543,13 +3585,13 @@ class Client
|
|
3543
3585
|
else
|
3544
3586
|
raise "'tables' must be an Array of Hashes."
|
3545
3587
|
end
|
3546
|
-
usedRecords =
|
3588
|
+
usedRecords = {}
|
3547
3589
|
tableRecords.each{|queryResults|
|
3548
3590
|
if queryResults
|
3549
3591
|
numRecords = 0
|
3550
3592
|
numRecords = queryResults[fieldNames[0]].length if queryResults[fieldNames[0]]
|
3551
3593
|
(0..(numRecords-1)).each{|recNum|
|
3552
|
-
recordHash =
|
3594
|
+
recordHash = {}
|
3553
3595
|
fieldNames.each{|fieldName|
|
3554
3596
|
recordHash[fieldName]=queryResults[fieldName][recNum]
|
3555
3597
|
}
|
@@ -3587,11 +3629,11 @@ class Client
|
|
3587
3629
|
getSchema(dbid)
|
3588
3630
|
|
3589
3631
|
slist = ""
|
3590
|
-
summaryRecord =
|
3591
|
-
doesTotal =
|
3592
|
-
doesAverage =
|
3593
|
-
summaryField =
|
3594
|
-
fieldType =
|
3632
|
+
summaryRecord = {}
|
3633
|
+
doesTotal = {}
|
3634
|
+
doesAverage = {}
|
3635
|
+
summaryField = {}
|
3636
|
+
fieldType = {}
|
3595
3637
|
|
3596
3638
|
fieldNames.each{ |fieldName|
|
3597
3639
|
fieldType[fieldName] = lookupFieldTypeByName(fieldName)
|
@@ -3642,7 +3684,7 @@ class Client
|
|
3642
3684
|
yield summaryRecord
|
3643
3685
|
|
3644
3686
|
count=0
|
3645
|
-
summaryRecord =
|
3687
|
+
summaryRecord = {}
|
3646
3688
|
fieldNames.each{|fieldName|
|
3647
3689
|
if doesTotal[fieldName]
|
3648
3690
|
summaryRecord["#{fieldName}:Total"] = 0
|
@@ -3813,7 +3855,7 @@ class Client
|
|
3813
3855
|
# Find the lowest value for one or more fields in the records returned by a query.
|
3814
3856
|
# e.g. minimumsHash = min("dfdfafff",["Date Sent","Quantity","Part Name"])
|
3815
3857
|
def min( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3816
|
-
min =
|
3858
|
+
min = {}
|
3817
3859
|
hasValues = false
|
3818
3860
|
iterateRecords(dbid,fieldNames,query,qid,qname,clist,slist,fmt,options){|record|
|
3819
3861
|
fieldNames.each{|field|
|
@@ -3840,7 +3882,7 @@ class Client
|
|
3840
3882
|
# Find the highest value for one or more fields in the records returned by a query.
|
3841
3883
|
# e.g. maximumsHash = max("dfdfafff",["Date Sent","Quantity","Part Name"])
|
3842
3884
|
def max( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3843
|
-
max =
|
3885
|
+
max = {}
|
3844
3886
|
hasValues = false
|
3845
3887
|
iterateRecords(dbid,fieldNames,query,qid,qname,clist,slist,fmt,options){|record|
|
3846
3888
|
fieldNames.each{|field|
|
@@ -3867,7 +3909,7 @@ class Client
|
|
3867
3909
|
# Returns the number non-null values for one or more fields in the records returned by a query.
|
3868
3910
|
# e.g. countsHash = count("dfdfafff",["Date Sent","Quantity","Part Name"])
|
3869
3911
|
def count( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3870
|
-
count =
|
3912
|
+
count = {}
|
3871
3913
|
fieldNames.each{|field| count[field]=0 }
|
3872
3914
|
hasValues = false
|
3873
3915
|
iterateRecords(dbid,fieldNames,query,qid,qname,clist,slist,fmt,options){|record|
|
@@ -3885,7 +3927,7 @@ class Client
|
|
3885
3927
|
# Returns the sum of the values for one or more fields in the records returned by a query.
|
3886
3928
|
# e.g. sumsHash = sum("dfdfafff",["Date Sent","Quantity","Part Name"])
|
3887
3929
|
def sum( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3888
|
-
sum =
|
3930
|
+
sum = {}
|
3889
3931
|
hasValues = false
|
3890
3932
|
iterateRecords(dbid,fieldNames,query,qid,qname,clist,slist,fmt,options){|record|
|
3891
3933
|
fieldNames.each{|field|
|
@@ -3914,9 +3956,9 @@ class Client
|
|
3914
3956
|
# Returns the average of the values for one or more fields in the records returned by a query.
|
3915
3957
|
# e.g. averagesHash = average("dfdfafff",["Date Sent","Quantity","Part Name"])
|
3916
3958
|
def average( dbid, fieldNames, query = nil, qid = nil, qname = nil, clist = nil, slist = nil, fmt = "structured", options = nil )
|
3917
|
-
count =
|
3959
|
+
count = {}
|
3918
3960
|
fieldNames.each{|field| count[field]=0 }
|
3919
|
-
sum =
|
3961
|
+
sum = {}
|
3920
3962
|
iterateRecords(dbid,fieldNames,query,qid,qname,clist,slist,fmt,options){|record|
|
3921
3963
|
fieldNames.each{|field|
|
3922
3964
|
value = record[field]
|
@@ -3937,7 +3979,7 @@ class Client
|
|
3937
3979
|
end
|
3938
3980
|
}
|
3939
3981
|
}
|
3940
|
-
average =
|
3982
|
+
average = {}
|
3941
3983
|
hasValues = false
|
3942
3984
|
fieldNames.each{|field|
|
3943
3985
|
if sum[field] and count[field] > 0
|
@@ -4009,7 +4051,7 @@ class Client
|
|
4009
4051
|
end
|
4010
4052
|
field = lookupField( fid )
|
4011
4053
|
if field
|
4012
|
-
choices =
|
4054
|
+
choices = []
|
4013
4055
|
choicesProc = proc { |element|
|
4014
4056
|
if element.is_a?(REXML::Element)
|
4015
4057
|
if element.name == "choice" and element.has_text?
|
@@ -4028,7 +4070,7 @@ class Client
|
|
4028
4070
|
# Get an array of all the record IDs for a specified table.
|
4029
4071
|
# e.g. IDs = getAllRecordIDs( "dhnju5y7" ){ |id| puts "Record #{id}" }
|
4030
4072
|
def getAllRecordIDs( dbid )
|
4031
|
-
rids =
|
4073
|
+
rids = []
|
4032
4074
|
if dbid
|
4033
4075
|
getSchema( dbid )
|
4034
4076
|
next_record_id = getResponseElement( "table/original/next_record_id" )
|
@@ -4055,7 +4097,7 @@ class Client
|
|
4055
4097
|
# Returns a hash with the structure { "duplicated values" => [ rid, rid, ... ] }
|
4056
4098
|
def findDuplicateRecordIDs( fnames, fids, dbid = @dbid, ignoreCase = true )
|
4057
4099
|
verifyFieldList( fnames, fids, dbid )
|
4058
|
-
duplicates =
|
4100
|
+
duplicates = {}
|
4059
4101
|
if @fids
|
4060
4102
|
cslist = @fids.join( "." )
|
4061
4103
|
ridFields = lookupFieldsByType( "recordid" )
|
@@ -4063,11 +4105,11 @@ class Client
|
|
4063
4105
|
cslist << "."
|
4064
4106
|
recordidFid = ridFields.last.attributes["id"]
|
4065
4107
|
cslist << recordidFid
|
4066
|
-
valuesUsed =
|
4108
|
+
valuesUsed = {}
|
4067
4109
|
doQuery( @dbid, nil, nil, nil, cslist ) { |record|
|
4068
4110
|
next unless record.is_a?( REXML::Element) and record.name == "record"
|
4069
4111
|
recordID = ""
|
4070
|
-
recordValues =
|
4112
|
+
recordValues = []
|
4071
4113
|
record.each { |f|
|
4072
4114
|
if f.is_a?( REXML::Element) and f.name == "f" and f.has_text?
|
4073
4115
|
if recordidFid == f.attributes["id"]
|
@@ -4078,7 +4120,7 @@ class Client
|
|
4078
4120
|
end
|
4079
4121
|
}
|
4080
4122
|
if not valuesUsed[ recordValues ]
|
4081
|
-
valuesUsed[ recordValues ] =
|
4123
|
+
valuesUsed[ recordValues ] = []
|
4082
4124
|
end
|
4083
4125
|
valuesUsed[ recordValues ] << recordID
|
4084
4126
|
}
|
@@ -4108,7 +4150,7 @@ class Client
|
|
4108
4150
|
if options and not options.is_a?( Hash )
|
4109
4151
|
raise "deleteDuplicateRecords: 'options' parameter must be a Hash"
|
4110
4152
|
else
|
4111
|
-
options =
|
4153
|
+
options = {}
|
4112
4154
|
options[ "keeplastrecord" ] = true
|
4113
4155
|
options[ "ignoreCase" ] = true
|
4114
4156
|
end
|
@@ -4132,7 +4174,7 @@ class Client
|
|
4132
4174
|
end
|
4133
4175
|
end
|
4134
4176
|
}
|
4135
|
-
newRecordIDs =
|
4177
|
+
newRecordIDs = []
|
4136
4178
|
if @fvlist and @fvlist.length > 0
|
4137
4179
|
numCopies.times {
|
4138
4180
|
addRecord( dbid, @fvlist )
|
@@ -4186,7 +4228,7 @@ class Client
|
|
4186
4228
|
excel.Quit
|
4187
4229
|
|
4188
4230
|
csvData = ""
|
4189
|
-
targetFieldIDs =
|
4231
|
+
targetFieldIDs = []
|
4190
4232
|
|
4191
4233
|
if fieldNames and fieldNames.length > 0
|
4192
4234
|
fieldNames.each{ |fieldNameRow|
|
@@ -4261,7 +4303,7 @@ class Client
|
|
4261
4303
|
if dbid
|
4262
4304
|
getSchema( dbid )
|
4263
4305
|
|
4264
|
-
targetFieldIDs =
|
4306
|
+
targetFieldIDs = []
|
4265
4307
|
|
4266
4308
|
if targetFieldNames and targetFieldNames.is_a?( Array )
|
4267
4309
|
targetFieldNames.each{ |fieldName|
|
@@ -4271,8 +4313,8 @@ class Client
|
|
4271
4313
|
end
|
4272
4314
|
|
4273
4315
|
useAddRecord = false
|
4274
|
-
invalidLines =
|
4275
|
-
validLines =
|
4316
|
+
invalidLines = []
|
4317
|
+
validLines = []
|
4276
4318
|
|
4277
4319
|
linenum = 1
|
4278
4320
|
IO.foreach( filename ){ |line|
|
@@ -4437,6 +4479,18 @@ class Client
|
|
4437
4479
|
def _updateFile( filename, fileAttachmentFieldName )
|
4438
4480
|
updateFile( @dbid, @rid, filename, fileAttachmentFieldName )
|
4439
4481
|
end
|
4482
|
+
|
4483
|
+
# Remove the file from a File Attachment field in an existing record.
|
4484
|
+
# e.g. removeFileAttachment( "bdxxxibz4", "6", "Document" )
|
4485
|
+
def removeFileAttachment( dbid, rid , fileAttachmentFieldName )
|
4486
|
+
updateFile( dbid, rid, "delete", fileAttachmentFieldName )
|
4487
|
+
end
|
4488
|
+
|
4489
|
+
# Remove the file from a File Attachment field in an existing record in the active table
|
4490
|
+
# e.g. _removeFileAttachment( "6", "Document" )
|
4491
|
+
def _removeFileAttachment( rid , fileAttachmentFieldName )
|
4492
|
+
updateFile( @dbid, rid, "delete", fileAttachmentFieldName )
|
4493
|
+
end
|
4440
4494
|
|
4441
4495
|
# Translate a simple SQL SELECT statement to a QuickBase query and run it.
|
4442
4496
|
#
|
@@ -4457,14 +4511,14 @@ class Client
|
|
4457
4511
|
slist = nil
|
4458
4512
|
state = nil
|
4459
4513
|
dbname = ""
|
4460
|
-
columns =
|
4461
|
-
sortFields =
|
4514
|
+
columns = []
|
4515
|
+
sortFields = []
|
4462
4516
|
limit = nil
|
4463
4517
|
offset = nil
|
4464
4518
|
options = nil
|
4465
4519
|
getRecordCount = false
|
4466
4520
|
|
4467
|
-
queryFields =
|
4521
|
+
queryFields = []
|
4468
4522
|
query = "{'["
|
4469
4523
|
queryState = "getFieldName"
|
4470
4524
|
queryField = ""
|
@@ -4774,7 +4828,7 @@ class Client
|
|
4774
4828
|
|
4775
4829
|
end
|
4776
4830
|
|
4777
|
-
# Iterate @records XML and yield only
|
4831
|
+
# Iterate @records XML and yield only 'record' elements.
|
4778
4832
|
def eachRecord( records = @records )
|
4779
4833
|
if records and block_given?
|
4780
4834
|
records.each { |record|
|
@@ -4787,7 +4841,7 @@ class Client
|
|
4787
4841
|
nil
|
4788
4842
|
end
|
4789
4843
|
|
4790
|
-
# Iterate record XML and yield only
|
4844
|
+
# Iterate record XML and yield only 'f' elements.
|
4791
4845
|
def eachField( record = @record )
|
4792
4846
|
if record and block_given?
|
4793
4847
|
record.each{ |field|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 3
|
9
|
+
version: 1.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Gareth Lewis
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-07 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|