Part 2 - The Revolution will not be televised...
The second part of The Revolution is passing Lotuscript variable references between UI contexts.
Historically, if you were in a view, and you had some Lotuscript variables that you'd populated in the view, and you wanted to create a new document with some information about those variables, you'd write stuff to some data storage point. If you had, say, a NotesDatabase handle, you'd write a server/filepath to an environment variable or a profile document from the view. Then in the document, you'd look in that environment or profile, retrieve those values, and then use .getDatabase to reestablish the link to that database.
A slightly more advanced technique might be to create a document on the backend, write the server/filepath into fields on that doc, then surface the doc in the UI via a .editDocument call. Again, you'd reestablish the database handle in the document, typically on the PostOpen event for the form.
Now that The Revolution has come, I'll show you how you can simply create shared referenced variables between the view and the new document...
PS: It's not the technique that's simple. It's what you get as the result that becomes incredibly easy and convenient.
This technique is deceptively subtle. It combines remote binding of UI events with dynamic library loading to hack a cross-context pointer object. As such, it's difficult both to demonstrate and document, so bear with the somewhat obtuse examples I'll be providing here.
The first thing we need to do is figure out WHAT we're going to pass. In my example, I want to move a NotesDatabase handle around, and while I could probably do that directly, it's a bit easy to manage with a custom class. In that custom class, I have some properties of some Notes product objects, including a NotesDatabase object. (Don't pay much attention to the stuff outside the red circle... yes, I'm doing some work to make a crazy abstracted class out of this for massive reuse.)
Now, in my view, I include that library and instantiate one of my uniCache objects as variable "myCache."
I also set the database handle in the PostOpen event of the view.
Over in my form, I again include my library, and declare a global "myCache" again (as a variant this time -- you can't strong-type it from what I've been able to tell.)
Then in my action to create the new document, I remotely bind the QueryRecalc event to a local routine in my view.
Simple enough so far, right? Now comes the head exploding part...
In the view's code that's triggered on the QueryRecalc, we use dynamic library loading to load a library called ContextWrapper.
ContextWrapper is the simplest Lotuscript class in recorded history. But it also has a global instantiation of one of those wrapper objects. And that, of course, has a property that can hold whatever we want to put in it.
So, in our view code, we slam our myCache variable into that property. We can do that because the myCache is global to the view, and therefore it's available in the context of our Execute'd script. Here's that code again.
"Okay," you're thinking, "so what? Executed script runs in it's own temporary Lotuscript module that's unloaded when it's done. It says so right in the documentation!!"
"Ah," I reply, "stop thinking in terms of constraints. How temporary is a temporary module? Could it be consistent within a single event sequence? After all, we have two routines that are run by the SAME event, and Lotuscript is smart enough not to load a library multiple times in nested libraries called by the same event."
And it turns out, this works!
Now as cool as this is, it's even cooler than you probably think. It seems like I'd be making a copy of the myCache object, right? I might have set the values from the Execute'd code, but when that event unloads, then I've got what I got and that's it. It was only a temporary module in the Execute that allowed me to do this in the first place.
But that's not what happens.
It's a REFERENCE. The view and document now have a SHARED OBJECT. When I make changes in the document's context, they happen in the view's context, too. If I point that db handle to a different place, it's repointed for every context in which I've shared it.
To prove that, The Revolution demo ties some events together. The form's QueryRecalc event reports on the known current filepath & replicaID of my shared db handle. Then it changes the database to something else.
Did you notice earlier that my view had also bound the PostRecalc event of the document? So it echos the path & replicaID of db handle.
Now you can just sit there and press F9 in the document, and watch the view push in details about the db reference!


Comments
Posted by Keil Wilson At 10:10:14 AM On 07/23/2007 |
However, I'm working on some ideas to fix that. For example, if each new opening environment identified itself in the cache, then you could check the cache on the QueryClose of whatever your originator object was.
Posted by Nathan T. Freeman At 10:32:38 AM On 07/23/2007 |