How to serialize an object to Inmagic Textworks userStore with JSON

by Peter Tyrrell Thursday, May 17, 2007 11:28 PM

The Inmagic DB/Textworks and CS/Textworks script object model includes a Store object which you can use to store strings. It's a simple dictionary object of key/value pairs where the key is a unique identifier for the corresponding value. There are two versions of the Store object: userStore and sessionStore. The first persists key/value pairs to the local machine, while sessionStore only sticks around so long as Textworks is open.

It's a lot like writing cookies. If you need to keep some values around to reuse that would otherwise disappear when the form unloads, you need to use the Store object.

set:

var myString = "foo";

Application.userStore.value("keyName") = myString;

 

get:

var myString = "";

myString = Application.userStore.value("keyName");

Unfortunately it only stores a string, like a cookie. If you like to work with objects, it doesn't seem right to store individual object properties as separate key/value pairs. Especially if you decide to change the object profile down the road, because you will have to rewrite code. It would be great to simply store an object's state and pick it back up again whenever.

Serialize the object

Serialization is the act of saving an object into a binary or text format. Aha! Text format - that's a string. We can save our object to a string, and persist it to the Store object. Then later we can get that string and deserialize back into an object.

What's the best way to store the string so we can easily turn it back into an object? We could roll our own format, but it's best to stick with something already known and tested and out there. So let's use JSON. JSON stands for JavaScript Object Notation and it's a text-based human-readable format for representing data structures, commonly for transmission over an HTTP connection. Basically it's AJAX without the X (Object Notation instead of XML).

JSON Example

In the following example, we define a Person object, instantiate it, set its properties, serialize it, store it, retrieve it, and deserialize back into an object.

function Person()

{

    this.Name;

    this.BirthDate;

}

 

// instantiate a Person

var person = new Person();

person.Name = "Joe Blow";

person.BirthDate = new Date("July 1, 1975 08:30:00");

 

// serialize the Person object

var strPerson = person.toJSONString();

/*

strPerson =

{"name":"Joe Blow","BirthDate":"1975-07-01T08:30:00"}

*/

 

// store the serialized Person

Application.userStore.value("mykey") = strPerson;

 

// retrieve the serialized Person

var s = Application.userStore.value("mykey");

 

// deserialize from string back into object

var o = s.parseJSON();

Pretty simple!

JSON Source Code

The prototype functions toJSONString() and parseJSON() are provided by an open source JSON parser and stringifier at http://www.json.org/json.js which can be compressed to less than 2000 characters and placed in the form script.

toJSONString() serializes strings, dates, arrays, booleans, numbers, nested versions of these... pretty much anything. parseJSON() is a more secure rewrite of javascript eval which limits itself to valid JSON notation only.

A Short Aside About Eval

If you're not already aware of how powerful eval is, consider that you can pass in a string and have it interpreted as code. You can say:

eval("var a = 1");

and thereafter have a variable, a, which equals 1. This is incredibly handy, but also a security concern, so you would never eval anything you didn't have complete trust in. You can directly eval a JSON string and magically get your object back, but parseJSON() is safer.

How to make a javascript trace log

by Peter Tyrrell Wednesday, May 16, 2007 2:19 AM

After I wrote my previous post on handling script errors, it occurred to me I had taken knowledge about about tracing and exceptions for granted. So, here is a description of how I add a trace log and exception handling to my scripts in DB/ or CS/Textworks.

Make an Inmagic DB/Textworks Trace Log

First, add a form box and two buttons to the form, named boxDebugLog, btnBugs, and btnClearLog.

Add code to handle the two buttons and and a log method, which writes a new line into the form box whenever called. The bugs button ensures there is no syntax error lurking in the code, which would halt any code from running on the form, and therefore would stop "no syntax bugs" from being written into the log (no news is bad news). The isLogging boolean variable can be turned on and off globally so that trace statements don't have to be ripped out of deployed code.

var isLogging = true;
function btnBugs_onClick()
{
  log("no syntax bugs");
}
 
function btnClearLog_onClick()
{
  Form.boxes("boxDebugLog").content = ''
}
 
function log(val)
{
  if (!isLogging)
  {
    return
  }
 
  var box = Form.boxes("boxDebugLog");
  if (box.content != '')
  {
    box.content += "\n"
  }  
  box.content += val;
}

Within the code, insert trace statements wherever you want to see the results of an operation.

log("GetRequestorsByQuery() " + commandQuery);

The result of the above would look like this in the Debug Log form box:

Extend the Trace Log to Handle Exceptions

The above is good for viewing the results of expected steps in a series of operations and can help you debug. Of course, when you work with code the real bugbear is the unexpected. And, further, there are operational errors (not bugs) which may crop up long after you're done building the code.

Inmagic DB/Textworks presents us with an obstacle in that any error encountered simply halts the process and does not in any way indicate there has been an error. To the average user this is perhaps a blessing (ignorance being bliss), but to the developer it is nothing but an impediment. Fortunately there is a way to free the errors from their airless dungeon so as to inform you of what is really going on.

The try/catch/finally statement attempts operations with instructions (the catch block) on what to do if an exception is encountered in the try block. The finally block tells what to do after try and catch have been dealt with, and is useful for closing objects or releasing resources that might otherwise have been stuck as a result of the exception encountered.

In our case, we can use try/catch to handle exceptions, and log the error messages to the trace log.

  var item = new catalogItem();

  var isInserted = false;

 

  try

  {

    isInserted = InsertCatalogRecord(item); 

  }

  catch (err)

  {

 

    log("Unable to add to catalog. " + err.name + ": " + err.message);

  }

Note that the err variable is an Error object, and so has predictable properties to call upon, name being the exception type and message being the short description. (More about exception types.Error is a built-in javascript object. I should mention that it is normally good practice to isolate specific exception types with multiple catch blocks, but I think that's overkill for Inmagic scripting.

You can also generate custom exceptions using the throw statement.

throw new Error("something went wrong");

throw "something went wrong";

Both statements are valid, because javascript is extremely lenient about what you can throw. I prefer to throw an Error object instead of a string, to be consistent with other exception types that might come up. Then I can ask for err.name and err.message without having to first check and see if the exception is actually a string instead.

So the throw statement allows you to "throw" (i.e. raise) an error whenever you like. Why would you want to, if the whole point of this exercise is to eliminate errors from the script? The reason becomes clear when you are working with operations whose scope is beyond your immediate control. In the scenario above where try/catch is demonstrated, we are adding a new record to a catalog. It's possible the catalog might not be there, or might reject a new record for some reason, or a field name changed since we wrote the script. If any of these happens, or anything else we can't now predict, we can still catch the problem and deal with it in a predictable way so that the script fails gracefully and passes on vital information before it expires.

If you wanted to force the Inmagic recordset object to throw any error it encountered, you could do that, for instance (and I do).

      if (rs.Errors.Count > 0)

      {

        throw new Error(rs.Errors(0).Description);

      }

It takes a bit of an adjustment at first to deliberately throw an error, or to let an error bubble up to another, calling function and deal with it there, but it is a powerful and freeing concept once you get it.

Tags:

Handling Inmagic Textworks javascript errors

by Peter Tyrrell Tuesday, May 15, 2007 2:40 PM

The scripting environment within Inmagic DB/Textworks (or CS/Textworks) is extremely primitive. Controls you want to interact with have to be declared in an environment separate from the form designer and it's hard to write code longer than 100 lines because there's no tab character or ctrl-a select all, let alone syntax highlighting or intellisense. It's very easy to lose your way if your script does anything beyond "hello world."

The most difficult thing to deal with is that any javascript error encountered does not actually throw an error. Things just progress... until they don't. No error thrown, no feedback, no nothing. It makes debugging a nightmare. What I have done until recently is insert a million-and-one trace statements that write to a trace box over on the side of the form, which at least tells me how far progress went and gives me a clue as to the general area things went wrong.

But I've recently realized I *can* throw (and handle) an error, if I wrap logical blocks of code in try/catch/finally statements. Can't believe it took me so long to cotton on to this. Now I can get detailed info about unanticipated exceptions as well as the anticipated ones. It makes my life better until I can boot up Visual Studio and bask once again in its warm developer-friendly glow.

Play "spot da error":

Yeah, "rs" is an undeclared variable. That's what happens when you refactor manually. Without try/catch this little problem becomes extremely elusive.

UPDATE: The picture has syntax highlighting because I do all scripting in UltraEdit. (Also features code folding, a jump-to-function list, etc.)

MIX 07 session videos for all

by Administrator Wednesday, May 09, 2007 5:46 PM

I didn't go to MIX 07, Microsoft's conference for Web designers, developers and decision makers, but I am shamelessly sucking down the conference session videos. I've watched about 3 hours so far, and already had some great eye-openers, particularly about LINQ and Visual Studio "Orcas". (Not Silverlight so much yet.)

Here's the feed to the session videos - well worth a look! There's bound to be at least something to get you excited: http://sessions.visitmix.com/rss/mix07_rss.xml.

Here's Anders Hejlsberg talking about LINQ, the one I liked so much: how LINQ (Language INtegrated Query) unifies data operations on disparate data formats (relational tables, XML, in-memory objects).

Tags:

Re-partitioning a dual boot Windows Vista and XP system

by Peter Tyrrell Tuesday, May 08, 2007 2:54 PM

If you're into adventure on the high seas, extreme blood sports, or are simply a masochist looking for your next hit of sweet, sweet pain, look no further. Have I got an activity for you.

Ladies and gentlemen, I direct your attention to this hard disk. On the C:\ partition, I give you: Windows XP. On the V:\ partition: Windows Vista.

"Hm. V:\ too small, and C:\ very big. I will shrink C:\ and allocate extra space to V:\," says I. Jauntily. I should have paid attention to the flash of green lightning and the ominous thunder-rumble that occurred when I said that. But I did not, and I stand before you now a sadder, but infinitely wiser man.

Here's what I did, minus the cursing and backtracking, minus the time spent on Google and various unhelpful Microsoft KB articles.

  1. Back up everything.
  2. Get GParted (Gnome Partition Editor) LiveCD, an open source partition manager. Burn to disc. Boot to said disc.
  3. Shrink C:\. Extend V:\. Mix, stir, let stand 3 hours.
  4. Reboot to Windows Vista install disc. Select "Repair this computer". (It repairs. Quickly.)
  5. Reboot, this time to boot manager and to installed version of Vista on V:\. Chkdsk isn't at all sure what the hee-haw you've just been doing and wants to check the disk. Let 'er rip.
  6. Log in to Vista. Ta-da. But what about XP? Yeah, it's hooped. (When you try to boot to XP, it says "\ntldr is missing." XP System Recovery Console can't find a Windows XP install, can't map the C:\ partition, can't find its own buttocks with two hands and a flashlight.)
  7. While in Vista, download VistaBootPro. It's free. Install. Run.
  8. From Manage BCD OS Entries, select Windows XP, or rather, "Earlier Version of Windows". Rename that entry to "Windows XP" while you're at it. Select "Change Boot Drive." Select C: from the dropdown. Apply Updates.
  9. Reboot to boot manager and to installed version of XP on C:\. Success!

The above has been a highly condensed version of my experience today. It worked on my machine. Your mileage may vary.

 

Tags: Vista

Month List