Andornot Consulting Inc.
Home Page
Home Page
 |  | 

Tuesday, August 05, 2008

DB/Text trace log format method

Further to my post of May 2007, where I described how to create a trace log in DB/Text and handle exceptions, here is a method that is handy for building more complex log statements.

   1: // construct formatted log statement
   2: function logFormat(val, arg1, arg2, arg3)
   3: {
   4:     var formattedVal = val;
   5:     if (arg1 != null)    {
   6:         formattedVal = formattedVal.replace("{0}", arg1);
   7:     }
   8:     if (arg2 != null) {
   9:         formattedVal = formattedVal.replace("{1}", arg2);
  10:     }
  11:     if (arg3 != null) {
  12:         formattedVal = formattedVal.replace("{2}", arg3);
  13:     }
  14:     log(formattedVal);
  15: }
  16:  
  17: // write log statement to form box
  18: function log(val)
  19: {
  20:     var box = Form.boxes("boxDebugLog");
  21:     if (box == null)
  22:         return;    
  23:     
  24:     if (box.content != '')
  25:     {
  26:         box.content += "\n";    
  27:     }    
  28:     box.content += val;
  29: }

Overview

logFormat takes a string value and up to 3 arguments, and replaces tokens found in the string value with those arguments. It's based on C#'s string.Format method, and others like it.

Example

It's easier to read (and write) a format string that contains tokens than a string concatenated together with plus signs.

   1: var count = 10;
   2: var name = "Peter";
   3: var duration = 30;
   4:  
   5: function LogTheOldWay()
   6: {
   7:     log(name + " was lashed with a wet noodle " + count + " times for " + duration + " seconds.");
   8: }
   9:  
  10: function LogTheNewWay()
  11: {    
  12:     logFormat("{0} was lashed with a wet noodle {1} times for {2} seconds.", name, count, duration);
  13: }

Update

My example above only proves how piddly my skills really are. There's a *much* better way of doing this that allows for n arguments instead of a maximum of three. I had forgotten that every javascript function has a local property called arguments that contains all parameters passed to the function as an array.

   1: function logFormat(val)
   2: {
   3:     var formattedVal = val;
   4:   for(i = 1; i < arguments.length; i++)
   5:   {
   6:     formattedVal = formattedVal.replace("{" + (i - 1) + "}", arguments[i]);
   7:   }
   8:   return formattedVal;
   9: }

Labels: ,

"Static" properties in javascript. Kinda.

The following gives me the ability to access javascript properties in a static kind of way. Not really, since the object has been instantiated, but at least I don't have to define a specific object, then call its constructor all the time to new it up and get at its properties.

   1: // declare globally
   2: var Artist = {
   3:     Fields: {
   4:         Id: "ID",
   5:         FullName: "Term",
   6:         FirstName: "First Name",
   7:         LastName: "Last Name",
   8:         Birth: "Date of Birth",
   9:         Death: "Date of Death"
  10:     }
  11: };

Overview

I like to store DB/Text field names in this way, as they become (pseudo) strongly-typed and the magic strings are stored in one place only, instead of scattered across my code like dandelion seeds.

It can be difficult to work in javascript after C# and other full-featured languages. This is the closest I could come to static properties or a struct. If I weren't scripting in DB/Text, a proprietary closed system, I would be using jQuery or mootools, which put the joy back in joyvascript.

Example

Now I can access field names statically as follows. Look ma, no constructors!

   1: function SomeMethod()
   2: {
   3:     var idFieldName = Artist.Fields.Id;
   4: }

Labels: ,

A highly reusable recordset script for DB/Text

This javascript function performs a search against a DB/Text database and returns a sorted array of dictionary objects with fieldnames as keys.

Parameters

Search(commandQuery, textbase, password, fieldsToReturn, sortFields)

commandQuery (string): A query in command syntax.
textbase (string): The name of the textbase to query.
password (string): A textbase password that allows querying.
fieldsToReturn (array): An array of field names whose values the query will retrieve from result records.
sortFields (array): An array of field names to sort the recordset by.

Dependencies

SortByField(recordset, sortFields): A method that sorts a recordset by the fields provided.

   1: // returns array of dictionaries with fieldsToReturn as keys
   2: function Search(commandQuery, textbase, password, fieldsToReturn, sortFields)
   3: {
   4:     var rs;
   5:     var output = [];
   6:     
   7:     rs = Application.newRecordset(textbase, Application.activeTextbase.path, password);
   8:     if (rs != null)
   9:     {
  10:         try {
  11:             rs.Open(commandQuery);
  12:             if (rs.Errors.Count > 0)
  13:             {
  14:                     // log error
  15:             }
  16:             else if (rs.RecordCount > 0)
  17:             {
  18:                 // sort the recordset
  19:                 rs = SortByField(rs, sortFields);
  20:                 
  21:                 rs.MoveFirst();
  22:                 while (!rs.EOF)
  23:                 {                    
  24:                     // create dictionary of field values, add to output list
  25:                     var record = new Object();
  26:                     for (var i = 0;i < fieldsToReturn.length;i++)
  27:                     {
  28:                         record[fieldsToReturn[i]] = rs.Fields(fieldsToReturn[i]).Value;
  29:                     }
  30:                     output[output.length] = record;
  31:                     
  32:                     rs.MoveNext();                    
  33:                 }
  34:             }    
  35:         }
  36:         catch (e) {
  37:             // log exception
  38:         }
  39:         finally 
  40:         {
  41:             rs.Close();    
  42:         }
  43:     }
  44:     return output;
  45: }
  46:  
  47: // returns recordset sorted by fields provided
  48: function SortByField(recordset, sortFields)
  49: {
  50:     var sort = recordset.NewSortDescriptor();
  51:     for (var i = 0;i < sortFields.length;i++)
  52:     {
  53:         if (i >= (sort.MaxSORTFIELDS - 1))
  54:         {
  55:             break;    
  56:         }
  57:         sort.sortFieldName(i) = sortFields[i];
  58:     }
  59:     recordset.Sort(sort);    
  60:     return recordset;
  61: }

Overview

The main advantage of this function is its agnosticism towards the fields it is to retrieve. It relies on the fact that objects in javascript inherently behave as associative arrays, e.g.

var myObject = new Object();
myObject["myKey"] = "some value";

For each record in the query's result set, an object is created with fieldsToReturn as keys, and the function's return type is simply an array of these objects. The fact that I am using these objects solely for their key/value behaviour leads me to refer to them as dictionaries. They are not dictionaries in the sense of specialized collections as with other languages outside of javascript.

Example

The following is a simplified method that I would never use in production code. It grabs search text from a box, creates a command query, then searches the Catalog. The result set will be sorted by Title, and will contain values from Title and Author fields. It then shows an alert message with the title from the first result in the set.

Note that field values are retrieved by using field names as keys on the objects in the results array.

   1: function btnSearch_onClick()
   2: {
   3:     var searchText = Form.boxes("boxSearchText").content;
   4:     var query = "find (Title ct {0}) or (Author ct {0})".replace("{0}", searchText);
   5:     var results = Search(query, "Catalog", "password", ["Title", "Author"], ["Title"]);
   6:     
   7:     var msg = "First result Title is '{0}'".replace("{0}", results[0]["Title"];
   8:     Application.message(msg);
   9: }

Labels: ,

Monday, July 07, 2008

Inmagic DB/TextWorks and Vista Virtualization

This post is a follow-up to Peter's post Inmagic WebPublisher PRO and Vista Virtualization, but specifically pertaining to DB/TextWorks. 

I recently had a client who created and added a bunch of records to a new textbase located in a folder under Program Files on her Vista-installed laptop.  When it came time to move the textbase off her laptop onto another computer she zipped up all the textbase files, copied the zip file to her thumb drive and prepared to unzip the files onto the other machine.  With files unzipped on the other machine (an older XP-installed desktop computer), she enthusiastically went to show off her newly created and populated textbase. 

Much to her surprise DB/TextWorks produced a disheartening message indicating that the textbase was empty.  How could this be, she had 170 records in the textbase when it was on her laptop and she had copied every textbase file from the correct folder on her laptop.  I got involved and we looked this way and that way - on both machines - and there was nothing that we could see that could indicate that the textbase could possibly be empty (the .btx, .dbt and .occ files were rather large).

The ah ha moment came when Kathy suggested that Peter had posted something about this on our Blog - back in March 2008 (see above link).  I had skipped over that posting because it had referenced WebPublisher, but in fact it told me exactly what I needed to know.  To change the text and code slightly from what Peter posted:

For instance, say DB/TextWorks indicates that a textbase is located at:

%Program Files%\Inmagic\Textbases

Vista detects that DB/TextWorks does not have permission to save to that location and copies the file to:

%Users%\<your account>\AppData\Local\VirtualStore\Program Files\Inmagic\Textbases

Subsequent read/write operations access the VirtualStore version of the file, even though DB/TextWorks will still think it is accessing the Program Files directory. Poor deluded DB/TextWorks.

It took about 1 minute to find the database files in the VirtualStore location - thank you Peter! - however, there was more, or in this case less, to come!  Two of the essential files were missing.  They were the .tba file and the .sdo file.  Why those two? Who knows?  However, combining the new found files (.acf, .btx, .dbo, .dbr, .ixl, .log, and .occ)  from the VirtualStore location with the .tba and the .sdo file from the presumed location – under Program files, allowed us to complete the textbase and transfer it to another location. 

The lesson learned here:

  • On a Vista machine DO NOT install textbases under Program files. 

And a Note: 

  • this problem only occurs with Vista's UAC (user account control) turned on.

Labels: ,

Friday, June 15, 2007

How to install Inmagic DB/Textworks on Vista

Right-click on the db/textworks installer. Choose "run as administrator". You may be prompted to proceed and/or enter administrative credentials, depending on your Vista setup. The installer will start installing and should work fine.

If you do not run as administrator, you will get an error like this:

Error 1925. You do not have sufficient privileges to complete this installation for all users of the machine. Log on as administrator & retry installation.

You may be confused by this message if you are logged in as an administrator. Welcome to Vista's User Account Control (UAC).

With UAC, you may have logged in as an admin, but you are not running as one. Instead you are prompted to elevate your permissions whenever you attempt an administrator-level operation. It so happens that Inmagic has not updated db/textworks to handle this scenario and give you fair warning. The error message it gives upon failure is at least completely accurate in its diagnosis, but leaves it up to you to manually elevate your permissions.

Here are some other Inmagic-on-Vista related posts on the Andornot Developer Blog:

Labels: , ,