Dynamically Including Script Libraries
This tip has come in handy for a couple of years in several cases, but for different reasons. With many companies now working (or soon to be working) in a mixed R5/ND6 environment (or even a mixed ND6/6.5 environment), you may want to take advantage of new features but not break agents for the older releases. For example, several new LotusScript classes were introduced in 6 and it would be nice to upgrade an agent for the people using the 6 client. But how do you do it without causing errors for your 5 clients? One way is to define all your variables using the Variant data type. But then you lose the association with what the data type truly is, and (down the road when all your clients have upgraded) you're wasting memory by having too much storage allocated for each variable. Also, sometimes when doing things this way Notes will give you a "type mismatch" error message because the variant is not the true expected data type.
What we do at Breaking Par Consulting, Inc is to dynamically include a script library. That's right. At run time, a different script library is included in the code. So, we can have an R5 script library and then a new ND6 script library. The ND6 script library defines everything using the correct classes (you get the type-ahead advantage that you wouldn't get if they were variants, too). And since your R5 clients bring in the R5 script library instead of the ND6 script library at run-time, they don't get any errors.
How do you do this?
It's actually pretty easy. You use the built-in LotusScript statement Execute to call some script to run. The script brings in the script library and calls a function or subroutine in the script library. Your biggest worry is getting any variables you need into the subroutine. What we do is define a couple of global variables in the agent (the ones that the script library will need) by putting them into the (Declarations) area of the agent. We don't bother with things like a session or a workspace, because those are pretty quick to get within the subroutine itself. Getting a handle the current database (or a remote database) takes a little longer, so we usually define that as global.
So, you now have a couple of global variables set up. Now it's time to write the script that includes the script library and makes the subroutine call. We'll demonstrate with some code:
Dim script As String
script = {Option Public} & Chr$(10) & {Option Declare} & Chr$(10) & {Use "}
If session.NotesBuildVersion >= 182 Then
script = script & {My ND6 Script Library}
Else
script = script & {My R5 Script Library}
End If
script = script & {"} & Chr$(10) & {Sub Initialize}
script = script & Chr$(10) & {Call myFunction} & Chr$(10) & {End Sub}
Execute(script)
The script builds up an entire "agent" if you will. It has the Initialize sub and everything. That's necessary because the "Use" statement must go into the (Options) part of the script for it to be brought in correctly. So we're not just executing a couple of lines of script, we executing a couple of "Objects" as you would see in the left frame when creating a LotusScript agent.
Based on the user's build number (greater than or equal to 182 is ND6) a different script library is included. Then a fixed subroutine is called. So, the same subroutine needs to be in both the script libraries. But we could have easily expanded the If blocks and called different things in the different versions.
So, there you have it. The correct script library based on the user's version will be included at run time and a subroutine will be run against whatever script library was included. Note that if you wanted to do a function, the return value must also be a global variable or you won't have access to it after the inner script is finished.
What else can this be used for?
Our biggest use for dynamically including script libraries is in report generation. Let's say you have 20 reports that the users can generate whenever they want. Having 20 different agents is really a pain. But we have 1 agent for building a report. At the start of the agent, we prompt the user for the report they want to generate. We use familiar report names so it's easy for them to choose. Then, once the report is chosen, we convert that to a script library name, dynamically include that script library and then run the report.
Doing things that way ends up being much more efficient than including all 20 script libraries in the main agent to begin with and calling a different subroutine based on the report to generate. The main reason it's more efficient is because all those script libraries don't have to come across the network when the agent is first loaded. This means a quicker response initially for the user (they can quickly choose the report they want to run). There will be a little hit when the script library is dynamically included, but it is (in the user's mind) part of the report generation and the download time is a little more accepted. Also, we are bringing in 1 script library instead of 20, so the overall time to generate the report should be less.