Class: QuickBase::CommandLineClient

Inherits:
Client
  • Object
show all
Defined in:
lib/QuickBaseCommandLineClient.rb

Overview

This implements an extensible command line interface to QuickBase.

Use setCommand() and setCommandAlias() to extend or modify the interface.

Call run() to use the command line interactively.

Call run( filename ) to run the commands in a file.

Commands entered during an interactive session can be recorded to a file.

In addition to the @commands loaded in initialize(), any public method
from class Client can be used as a command, and any line of Ruby code
can be executed.

Run a command line client session interactively, or using the commands in a file. e.g.:
* ruby QuickBaseCommandLineClient.rb run
* ruby QuickBaseCommandLineClient.rb run dailyTasks.qbc
* ruby -e "require 'quickbasecommandlineclient';QuickBase::CommandLineClient.new.run"

Direct Known Subclasses

WebClient

Defined Under Namespace

Classes: Command

Instance Attribute Summary collapse

Attributes inherited from Client

#HTML, #access, #accessid, #accountLimit, #accountUsage, #action, #admin, #adminOnly, #ancestorappid, #app, #appdata, #appdbid, #applicationLimit, #applicationUsage, #apptoken, #authenticationXML, #cacheSchemas, #cachedSchemas, #chdbids, #choice, #clist, #create, #createapptoken, #createdTime, #databases, #dbdesc, #dbid, #dbidForRequestURL, #dbname, #delete, #disprec, #domain, #downLoadFileURL, #email, #encoding, #errcode, #errdetail, #errtext, #escapeBR, #eventSubscribers, #excludeparents, #externalAuth, #fform, #fid, #fids, #field, #fieldTypeLabelMap, #fieldValue, #field_data, #field_data_list, #fields, #fileContents, #fileUploadToken, #firstName, #fmt, #fname, #fnames, #fvlist, #hours, #httpConnection, #id, #ignoreCR, #ignoreError, #ignoreLF, #ignoreTAB, #includeancestors, #jht, #jsa, #keepData, #key_fid, #label, #lastAccessTime, #lastError, #lastModifiedTime, #lastName, #lastPaymentDate, #lastRecModTime, #logger, #login, #mgrID, #mgrName, #mode, #modify, #name, #newappname, #newdbdesc, #newdbid, #newdbname, #newowner, #numMatches, #numRecords, #num_fields, #num_records, #num_recs_added, #num_recs_deleted, #num_recs_input, #num_recs_updated, #numadded, #numremoved, #oldestancestorappid, #options, #org, #page, #pagebody, #pageid, #pagename, #pagetype, #password, #permissions, #printRequestsAndResponses, #properties, #qarancestorappid, #qbhost, #qdbapi, #qid, #qname, #queries, #query, #rdr, #record, #records, #records_csv, #requestHeaders, #requestNextAllowedTime, #requestSucceeded, #requestTime, #requestURL, #requestXML, #responseElement, #responseElementText, #responseElements, #responseXML, #responseXMLdoc, #rid, #rids, #role, #roleid, #rolename, #saveviews, #screenName, #serverDatabases, #serverGroups, #serverStatus, #serverUpdays, #serverUptime, #serverUsers, #serverVersion, #showAppData, #skipfirst, #slist, #standardRequestHeaders, #status, #stopOnError, #table, #tables, #ticket, #type, #udata, #uname, #update_id, #user, #userid, #username, #users, #validFieldProperties, #validFieldTypes, #value, #variables, #varname, #version, #vid, #view, #withembeddedtables, #xsl

Instance Method Summary collapse

Methods inherited from Client

#_addField, #_addRecord, #_addReplaceDBPage, #_addUserToRole, #_changePermission, #_changeRecordOwner, #_changeUserRole, #_cloneDatabase, #_deleteDatabase, #_deleteField, #_deleteFieldName, #_deleteRecord, #_doQuery, #_doQueryCount, #_doQueryHash, #_doQueryName, #_downLoadFile, #_editRecord, #_fieldAddChoices, #_fieldNameAddChoices, #_fieldNameRemoveChoices, #_fieldRemoveChoices, #_genAddRecordForm, #_genResultsTable, #_getAncestorInfo, #_getAppDTMInfo, #_getBillingStatus, #_getDBInfo, #_getDBPage, #_getDBvar, #_getEntitlementValues, #_getFileAttachmentUsage, #_getNumRecords, #_getRecordAsHTML, #_getRecordInfo, #_getRoleInfo, #_getSchema, #_getUserRole, #_importFromCSV, #_importFromExcel, #_listDBPages, #_printChildElements, #_provisionUser, #_purgeRecords, #_removeUserFromRole, #_renameApp, #_runImport, #_sendInvitation, #_setActiveRecord, #_setAppData, #_setDBvar, #_setFieldProperties, #_setKeyField, #_updateFile, #_uploadFile, #_userRoles, #addField, #addFieldValuePair, #addOrEditRecord, #addRecord, #addReplaceDBPage, #addUserToRole, #alias_methods, #applyDeviationToRecords, #applyPercentToRecords, #authenticate, #average, #chainAPIcallsBlock, #changePermission, #changeRecordOwner, #changeRecords, #changeUserRole, #clearFieldValuePairList, #clientMethods, #cloneDatabase, #copyRecord, #count, #createDatabase, #createTable, #dateToMS, #debugHTTPConnection, #decodeXML, #deleteDatabase, #deleteDuplicateRecords, #deleteField, #deleteRecord, #deleteRecords, #deviation, #doQuery, #doQueryCount, #doSQLInsert, #doSQLQuery, #doSQLUpdate, #downLoadFile, #eachField, #eachRecord, #editRecord, #editRecords, #encodeXML, #encodingStrings, #escapeXML, #fieldAddChoices, #fieldRemoveChoices, #fieldTypeForLabel, #findDBByname, #findDuplicateRecordIDs, #findElementByAttributeValue, #findElementsByAttributeName, #findElementsByAttributeValue, #formatChdbidName, #formatCurrency, #formatDate, #formatDuration, #formatFieldValue, #formatImportCSV, #formatPercent, #formatTimeOfDay, #genAddRecordForm, #genResultsTable, #getAllRecordIDs, #getAllValuesForFields, #getAllValuesForFieldsAsArray, #getAllValuesForFieldsAsJSON, #getAllValuesForFieldsAsPrettyJSON, #getAncestorInfo, #getAppDTMInfo, #getApplicationVariable, #getApplicationVariables, #getAttributeString, #getAuthenticationXMLforRequest, #getBillingStatus, #getColumnListForQuery, #getDBInfo, #getDBPage, #getDBPagesAsArray, #getDBforRequestURL, #getDBvar, #getEntitlementValues, #getErrorInfoFromResponse, #getFieldChoices, #getFieldDataPrintableValue, #getFieldDataValue, #getFieldIDs, #getFieldNames, #getFileAttachmentUsage, #getFileUploadToken, #getFilteredRecords, #getJoinRecords, #getNumRecords, #getNumTables, #getOneTimeTicket, #getQueryRequestXML, #getRecord, #getRecordAsHTML, #getRecordInfo, #getReportNames, #getResponseElement, #getResponseElements, #getResponsePathValue, #getResponsePathValues, #getResponseValue, #getRoleInfo, #getSchema, #getServerStatus, #getSortListForQuery, #getSummaryRecords, #getTableIDs, #getTableName, #getTableNames, #getUnionRecords, #getUserInfo, #getUserRole, #grantedDBs, #importCSVFile, #importFromCSV, #importFromExcel, #importSVFile, #importTSVFile, init, #isAverageField?, #isBuiltInField?, #isHTMLRequest?, #isRecordidField?, #isTotalField?, #isValidFieldProperty?, #isValidFieldType?, #iterateDBPages, #iterateFilteredRecords, #iterateJoinRecords, #iterateRecordInfos, #iterateRecords, #iterateSummaryRecords, #iterateUnionRecords, #listDBPages, #logToFile, #lookupBaseFieldTypeByName, #lookupChdbid, #lookupField, #lookupFieldData, #lookupFieldIDByName, #lookupFieldName, #lookupFieldNameFromID, #lookupFieldPropertyByName, #lookupFieldType, #lookupFieldTypeByName, #lookupFieldsByType, #lookupQuery, #lookupQueryByName, #lookupRecord, #makeSVFile, #max, #method_missing, #min, #obStatus, #onChangedDbid, #parseResponseXML, #percent, #prependAPI?, #printChildElements, #printLastError, #printRequest, #printResponse, #processChildElements, processDatabase, #processRESTFieldNameOrRecordKeyRequest, #processRESTRequest, #processResponse, #provisionUser, #purgeRecords, #removeUserFromRole, #renameApp, #replaceFieldValuePair, #resetErrorInfo, #resetfid, #resetrid, #runImport, #sendInvitation, #sendRequest, #setActiveRecord, #setActiveTable, #setAppData, #setDBvar, #setFieldProperties, #setFieldValue, #setFieldValues, #setHTTPConnection, #setHTTPConnectionAndqbhost, #setKeyField, #setLogger, #setqbhost, #signOut, #splitString, #subscribe, #sum, #toXML, #toggleTraceInfo, #updateFile, #uploadFile, #userRoles, #verifyFieldList, #verifyQueryOperator

Constructor Details

#initializeCommandLineClient

Set up some basic commands and their abbreviations



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/QuickBaseCommandLineClient.rb', line 67

def initialize
   super

   # the main purpose of commands and aliases loaded here is reduce the
   # steps, typing and clicking needed to get simple things done in QuickBase

   setCommand( "signin",   "Sign into QuickBase",                     "@ticket.nil?", [ "username", "password" ], [ :authenticate ] )

   setCommand( "create",   "Create an application",                   "@ticket", [ "application name", "description" ], [ :createDatabase, :onChangedDbid ] )
   setCommand( "copy",     "Copy an application, with/out data",      "@ticket and @dbid", [ "name", "desc", "keep data?" ], [ :_cloneDatabase, :onChangedDbid ] )
   setCommand( "open",     "Open an application",                     "@ticket", [ "application name" ], [ :findDBByname, :onChangedDbid ] )
   setCommand( "use",      "Use a table from the current application","@ticket and @dbid and @chdbids", [ "table name" ], [ :lookupChdbid, :onChangedDbid ] )

   setCommand( "select",   "Select records using a query name",       "@ticket and @dbid", [ "query name" ], [ :_doQueryName, :resetrid ] )
   setCommand( "setrecord",   "Set the active record",                "@ticket and @dbid", [ "record number" ], [ :_setActiveRecord ] )

   setCommand( "changerecords", "Conditionally set field value",      "@ticket and @dbid and @fields", [ "field", "value", "testfld", "test", "testval" ], [ :changeRecords ], "Are you sure?" )
   setCommand( "deleterecords", "Conditionally delete records",       "@ticket and @dbid and @fields", [ "test field", "test", "test value" ], [ :deleteRecords ], "Are you sure?" )
   setCommand( "deleteallrecords", "Delete all records",              "@ticket and @dbid", nil, [ :deleteRecords, :resetrid ], "Are you sure?" )

   setCommand( "addrecord","Add a record to the active table",        "@ticket and @dbid", nil, [ :_addRecord, :_getRecordInfo ] )
   setCommand( "setfield", "Set a field value in the active record",  "@ticket and @dbid and @rid", [ "field name", "value"], [ :setFieldValue ] )

   setCommand( "addfield", "Add a field to the active table",         "@ticket and @dbid", [ "field name" , "field type" ], [ :_addField ] )
   setCommand( "addfieldchoices", "Add value choices for a field",    "@ticket and @dbid", [ "field name" , "[choice1,choice2]" ], [ :_fieldNameAddChoices ] )

   setCommand( "importfile","Import records from a CSV file",         "@ticket and @dbid", [ "file name" ], [ :importCSVFile, :resetrid ] )
   setCommand( "importexcelfile","Import records from Excel",         "@ticket and @dbid", [ "Excel(.xls) file name", "last import column" ], [ :_importFromExcel, :resetrid ] )
   setCommand( "exportfile","Export records to a CSV file",           "@ticket and @dbid", [ "file name" ], [ :makeSVFile ] )

   setCommand( "uploadfile", "Upload a file into a new record",       "@ticket and @dbid", [ "file name", "file attachment field name" ], [:_uploadFile]  )
   setCommand( "updatefile", "Update the file attachment in a record","@ticket and @dbid and @rid", [ "file name", "file attachment field name" ], [:_updateFile]  )

   setCommand( "deletefield", "Delete a field from the active table", "@ticket and @dbid", [ "field name" ], [:_deleteFieldName], "Are you sure?" )
   setCommand( "deletetable", "Delete the active table",              "@ticket and @dbid", nil, [ :_deleteDatabase, :resetrid ], "Are you sure?" )

   setCommand( "print",    "Prints the results of the last command",  "@ticket", nil, [ :_printChildElements ] )
   setCommand( "listapps", "Lists the applications you can access",   "@ticket", nil, [ :grantedDBs, :_printChildElements ] )

   setCommand( "uselog", "Logs requests and responses to a file",     "true",  [ "log file" ], [ :logToFile ] )

   setCommand( "signout",  "Sign out of QuickBase",                   "@ticket", nil, [ :signOut ] )

   @aliases = { "si" => "signin", "la" => "listapps", "o" => "open",
                "p" => "print", "so" => "signout", "af" => "addfield",
                "sel" => "select", "ar" => "addrecord", "sf" => "setfield",
                "sr" => "setrecord", "q" => "quit", "if" => "importfile",
                "ef" => "exportfile", "ulf" => "uploadfile", "udf" => "updatefile",
                "r" => "run", "ul" => "uselog"  }

end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class QuickBase::Client

Instance Attribute Details

#aliasesObject (readonly)

Returns the value of attribute aliases.



38
39
40
# File 'lib/QuickBaseCommandLineClient.rb', line 38

def aliases
  @aliases
end

#availableCommandsObject (readonly)

Returns the value of attribute availableCommands.



38
39
40
# File 'lib/QuickBaseCommandLineClient.rb', line 38

def availableCommands
  @availableCommands
end

#commandsObject (readonly)

Returns the value of attribute commands.



38
39
40
# File 'lib/QuickBaseCommandLineClient.rb', line 38

def commands
  @commands
end

#usageObject (readonly)

Returns the value of attribute usage.



38
39
40
# File 'lib/QuickBaseCommandLineClient.rb', line 38

def usage
  @usage
end

Instance Method Details

#cmdString(command, include_desc = true) ⇒ Object

Make a string to display a command to the user



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/QuickBaseCommandLineClient.rb', line 141

def cmdString( command, include_desc = true )

   ret = ""

   cmd = @commands[command]
   if cmd

      cmdalias = ""
      if @aliases and @aliases.has_value?( cmd.name )
         @aliases.each{ |k,v| cmdalias << "#{k}," if v == cmd.name }
         cmdalias[-1] = ""
         cmdalias = "(#{cmdalias})"
      end

      if cmd.args
         if include_desc
            ret = "#{cmd.name}#{cmdalias} '#{cmd.args.join( ',' )}': #{cmd.desc}"
         else
            ret = "#{cmd.name}#{cmdalias} '#{cmd.args.join( ',' )}'"
         end
      else
         if include_desc
            ret = "#{cmd.name}#{cmdalias} : #{cmd.desc}"
         else
            ret = "#{cmd.name}#{cmdalias}"
         end
      end
   end
   ret
end

#evalAvailableCommandsObject

Get a list of commands that are valid now



133
134
135
136
137
138
# File 'lib/QuickBaseCommandLineClient.rb', line 133

def evalAvailableCommands
   @availableCommands = Array.new
   @commands.each{ |name,cmd|
       @availableCommands << name if eval( cmd.prerequisite )
     }
end

#prompt(promptString) ⇒ Object

Prompt the user if a command requires an ‘Are you sure?’ type of response



187
188
189
190
191
192
193
194
195
# File 'lib/QuickBaseCommandLineClient.rb', line 187

def prompt( promptString )
   if promptString
      print "#{promptString} (Press 'y' if Yes): "
      yn = gets
      yn.chop!
      return false if yn != "y"
   end
   true
end

#run(filename = nil) ⇒ Object

The main command entry and processing loop



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/QuickBaseCommandLineClient.rb', line 198

def run( filename = nil )

   begin

      if filename
         file = File.new( filename )
      else
         showUsage
      end

      recordedCommandsFile = nil

      loop {

         resetErrorInfo

         if filename.nil?
            showAvailableCommands

            print "Enter a command: "
            inputLine = gets

         else
            evalAvailableCommands
            inputLine = file.gets
            break if inputLine.nil?
         end

         if inputLine.index( '\t' )
            separator = "\t"
         elsif inputLine.index( "," )
            separator = ","
         else
            separator = " "
         end

         args = inputLine.split
         inputcommand = args[0]

         if filename.nil? and recordedCommandsFile and inputcommand != "record"
            recordedCommandsFile.write( inputLine )
         end

         input = splitString( inputLine, separator )

         if input and input.length > 0

            (0..input.length-1).each{ |i| input[i].strip!;input[i].gsub!( "\"", "") }

            if @aliases and @aliases.include?( inputcommand )
               command = @aliases[ inputcommand ]
            else
               command = inputcommand
            end

            break if command == "quit"

            if command == "usage"
               showUsage
            elsif command == "record"
               if input.length == 2
                  recordedCommandsFile.close if recordedCommandsFile
                  recordedCommandsFile = File.open( input[ 1 ], "w" )
                  puts "Unable to open file '#{input[ 1 ]}' for writing." if recordedCommandsFile.nil?
               elsif input.length < 2
                  puts "Command file name is missing. Enter 'record filename'"
               end
            elsif command == "run"
               if input.length == 2 and FileTest.readable?( input[ 1 ] )
                  run( input[ 1 ] )
               elsif input.length < 2
                  puts "Command file name is missing. Enter 'run filename'"
               else
                  puts "'#{input[1]}' is not a readable file"
               end
            elsif command == "ruby"
               begin
                  inputLine.sub!( "ruby", "" )
                  puts inputLine
                  eval( inputLine )
               rescue StandardError => e
                  puts "Error: #{e}"
               end
            elsif command == "?"
               showAvailableCommands
               puts "API commands:\n#{clientMethods().sort().join(', ')}\n"
            elsif @commands and @commands.include?( command ) and @availableCommands.include?( command )

               cmd = @commands[ command ]
               input.shift

               if cmd.args and cmd.args.length == 1
                  inputLine.sub!( inputcommand, "" )
                  inputLine.strip!
                  input = [ inputLine ]
               end

               if prompt( cmd.prompt )
                  begin
                     if cmd.args and input.length == cmd.args.length
                        case input.length
                          when 1
                            puts "#{cmd.code[0]}( #{input[0]} )"
                            send( cmd.code[0], input[0] )
                          when 2
                            puts "#{cmd.code[0]}( #{input[0]}, #{input[1]} )"
                            send( cmd.code[0], input[0], input[1] )
                          when 3
                            puts "#{cmd.code[0]}( #{input[0]}, #{input[1]}, #{input[2]} )"
                            send( cmd.code[0], input[0], input[1], input[2] )
                          when 4
                            puts "#{cmd.code[0]}( #{input[0]}, #{input[1]}, #{input[2]} , #{input[3]})"
                            send( cmd.code[0], input[0], input[1], input[2], input[3] )
                          when 5
                            puts "#{cmd.code[0]}( #{input[0]}, #{input[1]}, #{input[2]} , #{input[3]}, #{input[4]})"
                            send( cmd.code[0], input[0], input[1], input[2], input[3], input[4] )
                        end

                        (1..cmd.code.length-1).each{ |c|
                            puts "#{cmd.code[c]}"
                            send( cmd.code[c] )
                        }

                     elsif cmd.args.nil?
                        cmd.code.each{ |c|
                           puts "#{c}"
                           send( c )
                        }
                     else
                        puts "Information missing: #{cmdString( command, false )}"
                     end
                  rescue StandardError => e
                     puts "Error: #{e}"
                  end
               end

            elsif clientMethods().include?( command )
               puts inputLine
               begin
                  eval( inputLine )
               rescue StandardError => e
                  puts "Error: #{e}"
               end
            else
               puts "Invalid command '#{inputcommand}'"
            end

            if (!@requestSucceeded.nil?) and @requestSucceeded == false
               puts "\n#{@lastError}"
            end

            if filename.nil?
               print "\nPress Enter to continue..."
               gets
            end

         end

      }

   rescue StandardError => e
      puts "Error: #{e}"
   ensure
      signOut if @ticket and filename.nil?
      recordedCommandsFile.close if recordedCommandsFile
   end
end

#setCommand(name, desc, prerequisite, args, code, prompt = nil) ⇒ Object

Add a command to the list of available commands



120
121
122
123
124
# File 'lib/QuickBaseCommandLineClient.rb', line 120

def setCommand( name, desc, prerequisite, args, code, prompt = nil )
   @commands = Hash.new if @commands.nil?
   cmd = Command.new( name, desc, prerequisite, args, code, prompt )
   @commands[ name ] = cmd
end

#setCommandAlias(anAlias, cmd) ⇒ Object

Set the alias, or abbreviation, for a command



127
128
129
130
# File 'lib/QuickBaseCommandLineClient.rb', line 127

def setCommandAlias( anAlias, cmd )
   @aliases = Hash.new if @aliases.nil?
   @aliases[ anAlias ] = cmd
end

#showAvailableCommandsObject

Display the commands currently available



173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/QuickBaseCommandLineClient.rb', line 173

def showAvailableCommands
   evalAvailableCommands
   puts "\nCommands available:"
   puts
   puts " quit(q): End this command session"
   puts " usage: Show how to use this program"
   puts " ruby 'rubycode': run a line of ruby language code"
   puts " run(r) 'filename': run the commands in a file"
   puts " record 'filename': records your commands in a file"
   @availableCommands.sort().each { |cmd| puts " #{cmdString( cmd )}" }
   puts "\n"
end

#showUsageObject

Display a message telling the user what to do.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/QuickBaseCommandLineClient.rb', line 49

def showUsage
   if @usage.nil?
      @usage = <<USAGE

 Enter a command from the list of available commands.
 The list of commands may change after each command.
 Type TABs, commas, or spaces between parts of a command.
 Use double-quotes if your commands contain TABs, commas or spaces.

 e.g. addfield "my text field" text

USAGE
   end

   puts @usage
end