Tutorial: 30 LotusScript tips



Tutorial: 30 LotusScript tips | |

|[pic] |

|27 Mar 2007 | |

|[pic] |

Among the plethora of sessions to choose from at Lotusphere 2007, one of the most useful and interesting was "AD505 DevBlast -- 30 LotusScript tips," a presentation from Bill Buchan, CEO of HADSL, a company that develops best-practice tools for Lotus Notes and Domino. With Bill Buchan's permission, we've repackaged his presentation here for your education and enjoyment.

Part 1 introduces 10 basic LotusScript techniques that you should already be using if you've had any previous exposure to LotusScript. These beginner-level tips are also perfect for a Notes/Domino developer just getting started with LotusScript.

Part 2 explains LotusScript best practices you should employ as part of your everyday coding. These intermediate-level tips will help you polish your routine coding methods and secure your LotusScript code.

Part 3 offers advice to experienced developers who want to take their LotusScript skills to the next level. These advanced-level tips will challenge you to use more complex LotusScript commands to improve the functionality, performance and clarity of your LotusScript code.

[pic]

[pic]TUTORIAL: 30 LOTUSSCRIPT TIPS

[pic]

[pic] Home: Introduction

[pic] Part 1: 10 fundamental LotusScript tips

[pic] Part 2: 10 everyday LotusScript tips

[pic] Part 3: 10 advanced LotusScript tips

[pic] Part 4: More LotusScript learning resources

LOTUSSCRIPT

10 fundamental LotusScript tips

|[pic][pic]Option Declare |

|Templates and versions |

|Application lifecycles |

|How to code |

|"Measure twice, cut once" |

|Extending arrays the easy way |

|Use the list operator |

|Logging agents |

|Code structure |

|Hiding your code |

|  Fundamental LotusScript Tip #1. Option Declare |

| |

|Make sure you always use "Option Declare." It sounds obvious, but there are several determining factors as to why you should always use it. |

|Here are some of the reasons: |

|If you don't use "Option Declare," all variables are created at runtime as variants, and this makes fact checking redundant. |

|Data conversion will cost your company 10 times your performance. |

|All your errors will be runtime errors. |

|Also, always remember that you should test often and test early. Use the strength of the compiler to help you. |

|  Fundamental LotusScript Tip #2. Templates and versions |

|  |

| |

|When working with templates and versions in LotusScript in custom code, there are a few things you should always do: |

|Always create templates with new code, which may include databases with an NTF extension or databases with Master Template Name set. |

|Make sure you create a separate version for each copy you're working on. |

|And always keep your templates and versions in a central repository. |

|You might say these all sound like good reasons, but why should I use them? There are three major reasons: |

|Because it makes it much easier to roll back. |

|It helps with version control. |

|It makes it simpler to construct test cases with other versions. |

|  Fundamental LotusScript Tip #3. Application lifecycles |

|  |

| |

|When dealing with the lifecycles of your applications, there are a few best practices you should follow: |

|Always develop in a development sandbox -- a non-production environment where each developer keeps his or her working copy of the code base. |

|Always test in a user acceptance environment. |

|Pass the template to your Domino administrator to copy. |

|Here is why you should adhere to these rules: |

|It's a basic change-control system. |

|It is you doing your part as a development professional. |

|It shakes out hard-coded bugs, such as hard-coded server names, paths and replica IDs. |

|It allows you to run extensive testing without affecting your company's production. |

|  Fundamental LotusScript Tip #4. How to code |

| |

|Here are the fundamental how's and why's of how to code in LotusScript: |

|Always code for maintenance. |

|Only code for performance if it is required. Make sure you get your code working in general, before you get it working for speed. |

|Here is why you should follow these LotusScript how-to coding guidelines: |

|The highest cost in development is software maintenance. |

|You make it easier for the person maintaining the application. |

|You could be working on that application! |

|  Fundamental LotusScript Tip #5. "Measure twice, cut once" |

| |

|"Measure twice, cut one" may sound a bit cliche, but it is a good analogy when you are writing LotusScript code. Here are a couple ways it |

|applies. |

|You should always spend more time thinking about what you are coding and less time doing the actual coding. |

|You should always try to think of two different ways of solving the problem at hand. The best approach is usually a combination of the two, and|

|you should always think about your data model. |

|This works well because you are spending more time planning and less time doing actual labor. |

|  Fundamental LotusScript Tip #6. Extending arrays the easy way |

| |

|You should always try to use "ubound" to establish the size of your array. Below you will find an example of it in use. |

|Sub initialize() |

|Dim myArray() as String |

|redim my Array(0) |

|call myExtend (myArray, "Hello Sailor"") |

|end sub |

| |

|function myExtend(S() as String, ns as String) as integer |

|if (S(ubound(S)) "") then redim preserve S(ubound(S)+1) |

|S (ubound(S)) = ns |

|extend = true |

|end function |

|If you set up your arrays this way, you won't need to keep a separate index of the array size; it also automatically trims the code. However, |

|there is a minor drawback to implementing this. It will slow down large arrays, so you'll need to define the "empty" value. Otherwise, it works|

|great. |

|  Fundamental LotusScript Tip #7. Use the list operator |

| |

|When you use the list operator, it stores a value with a unique lookup key. It's a good idea to use the list operator because: |

|It's easy and fast |

|It's built right into LotusScript (as long as you're using version 4.5 or higher) |

|Here is some example code: |

|Dim WR list as String |

|WR("Peter") = "Perfect" |

|WR("Penelope") = "Pitstop" |

| |

|Print "Peter's last name is: " +WR("Peter") |

|if not isElement(WR("Dick")) then print "Dick isn't racing!" |

|forall thisRacer in WR |

|Print listtag(thisracer) + " " + thisRacer |

|end forall |

| |

|  Fundamental LotusScript Tip #8. Logging agents |

| |

|Another good tip is to log your LotusScript agents, especially if they are scheduled agents. It's also a good idea if you have a broad range of|

|clients. If you do, you need to log both your client and scheduled agents runtime status. |

|If you don't log your agents, chances are that your applications will break. Logging your agents will also help to let you know when your |

|LotusScript agents do break. It's also a good timing metric for performance testing. |

|Show caution when logging your agents though -- you don't want to make the logging so slow that it affects application performance! |

|  Fundamental LotusScript Tip #9. Code structure |

| |

|When dealing with the structure of your LotusScript code, it is usually good to keep it as short as possible. Smaller code sequences are easier|

|to deal with and maintain and are easily reusable. A good rule to go by is if your code is more than a screen's worth, you should determine if |

|it is possible to break it up. |

|Some tips when determining your LotusScript code structure: |

|Try to keep your functions/procedures/classes at a manageable size. |

|Think before decomposing problems. |

|Don't over comment -- explain your "why." |

|Try to use non-intuitive algorithms. |

|  Fundamental LotusScript Tip #10. Hiding your code |

| |

|When we say we're "hiding our code," we're basically saying that we're decomposing it so consumers can't see it. You don't want anyone to be |

|able to view it, not even yourself. |

|When you hide your code, you are inferring a logical interface with clear naming. As such, your users and customers can write to the interface |

|but not the code itself. This is good practice because it simplifies the coding experience. You can easily hide your code by using |

|"private/public" methods in your classes, script libraries, etc. |

10 everyday LotusScript tips

[pic]

|Use error trapping |

|Use defensive coding |

|Protect your code |

|Use NotesDateTime instead of strings |

|Use DXL as Transport |

|Use wizard interface in your Notes client |

|Consuming Web services |

|Use classes |

|Use the Evaluate command |

|Use "trusted servers" |

|  Everyday LotusScript Tip #1. Use error trapping |

|  |

| |

|Error trapping tests a particular condition when running a program. If the program runs into an error, it will then execute a corresponding |

|routine to fix the error. This should always be done. Error handling is not a joke -- it should always be handled in a serious manner and |

|should always be mandatory. |

|There are two ways to go about this: |

|Use a single error handler at the top of your LotusScript code. It is simple to implement, but at times difficult to keep in context. |

|Implement your error handler at the function level. It's a little bit more work, but it's also much more granular. |

|  Everyday LotusScript Tip #2. Use defensive coding |

|  |

| |

|While it may seem a bit paranoid, you should practice using defensive LotusScript coding -- it will save you time in the long run. Always |

|assume the worst and check all inputs on every function. It doesn't usually affect performance, but it is a good failsafe. |

|Here is a good example of defensive LotusScript coding in a typical function. |

|Function mytest (p1as String, p2 as String) as integer |

|mytest = false |

|if p1 = "" then exit function |

|if p2 = "" then exit function |

|. . . |

|' Now actually do something! |

|. . . . |

|mytest = true |

|end function |

| |

|  Everyday LotusScript Tip #3. Protect your code |

|  |

| |

|When creating commercial applications, it's a very good idea to hide your code. But, you may be asking how. There are actually two ways you can|

|go about it: |

|Create a template and click on "hide design." This is easy to do, but it may end up allowing form customization. |

|You could also remove your LotusScript source code from your script libraries. Use the NotesNoteCollection command to find your script design |

|document. Then replace the "$ScriptLib" with a String -- "Hello." This is not the easiest way to go about this process, but your other design |

|elements can be modified as well. (Do not do this on your development copy!) |

|  Everyday LotusScript Tip #4. Use NotesDateTime instead of strings |

| |

|You should never store date/time values as strings. It is always good practice to use NotesDateTime structures instead and save them. |

|You might say, sure, but why? Well, you never know how the client will interpret dates. Is it dd/mm/yyyy or mm/dd/yyyy? It also means that |

|views will be able to sort on dates. |

|Trust me, this is a good tip to practice. This issue comes up more often than you might think. |

|  Everyday LotusScript Tip #5. Use DXL as Transport |

|  |

| |

|A good reason to consider using DXL as Transport stems from a situation where a customer wants to easily send back "log" documents. When this |

|happens, you can use a LotusScript agent to: |

|Pick up all selected documents. |

|Create a memo with a rich-text field. |

|Use DXL to store the documents in the rich-text field. |

|At the receiving end, this will: |

|Unpack the mail message to a DXL stream. |

|Construct new documents to store the data. |

|This way, you are transferring data without replication. Below is a sample code of this being implemented. |

|Dim sSession As New NotesSession |

|Dim dbThis As notesDatabase |

|Set dbThis = sSession.CurrentDatabase |

|Dim dc As NotesDocumentCollection |

|Set dc = dbThis.UnprocessedDocuments |

|If (dc Is Nothing) Then exit sub |

|If (dc.count < 1) Then exit sub |

|Dim doc As NotesDocument |

|Set doc = dc.GetFirstDocument |

|While (Not doc Is Nothing) |

|Dim de As NotesDXLExporter |

|Set de = sSession.CreateDXLExporter() |

|Call de.setInput(doc) |

|Dim dxl As String |

|dxl = de.Export |

|' continued overleaf.. |

|Dim dbMail As |

|New NotesDatabase("", "") |

|Call dbMail.OpenMail() |

|Dim docM As NotesDocument |

|Set docM = dbMail.CreateDocument |

|Call docM.ReplaceItemValue |

|("Form", "Memo") |

|Call docM.ReplaceItemValue |

|("Recipients", "logs@") |

|Call docM.ReplaceItemValue |

|("SendTo", "logs@") |

|Call docM.ReplaceItemValue |

|("Subject", "Log Documents") |

|Dim rt As New NotesRichTextItem |

|(docM, "Body") |

|Call rt.AppendText(dxl) |

|Call docM.Send(False) |

|Set docM = Nothing |

|Set de = Nothing |

|Set doc = dc.GetNextDocument(doc) |

|Wend |

| |

|  Everyday LotusScript Tip #6. Use a wizard interface in your Notes client |

| |

|When using a wizard interface with your Lotus Notes client, there are a few steps you should follow: |

|Create a form with a tabbed table. |

|Set the tabs to "1," "2," "3," etc. |

|Select "Switch Rows Programmatically." |

|Set the "name" field to the name of the table; for example: "RequestTable." |

|Create a variable on the form with $Name; for example: "$RequestTable." |

|Have your "forward" and "back" buttons increment/decrement the variable. |

|  Everyday LotusScript Tip #7. Consuming Web services |

|  |

| |

|There are two different ways you can go about consuming Web services. The first is quick and to accomplish it, you should follow these steps: |

|Install Microsoft SOAP on client machines. |

|Write LotusScript to create a Microsoft SOAP object. This is a good option because it is quick and handy when it comes to testing. |

|Unfortunately, it is platform-specific, requires dynamic link libraries on clients, and there is no timeout. |

|Below is some code that illustrates how to create the Microsoft SOAP object. |

|Dim Client As Variant |

|Set Client = CreateObject("MSSOAP.SoapClient") |

|'Initialize connection to the Web Service |

|Call Client.mssoapinit |

|("") |

|'Call our simple GetEmailAddress |

|function provided by Web service |

|Dim result As String |

|result = Client.getJoke() |

|'output result to message box |

|Messagebox result, 48, "Get Joke" |

|The other approach is a little different. It's big and robust and uses Stubby. Just point it at a Web service and it produces the code for you.|

| |

|Some good points about it are that it is multi-platform, scalable and there are no dynamic link libraries. However, it does require you to use |

|more than four lines of code. |

| |

|Everyday LotusScript Tip #8. Use classes |

|When developing with LotusScript, it is always a good idea to use classes. Here are some reasons why: |

|Classes help to bundle data and code in one place. |

|They decompose problems into "objects." |

|They help to write smaller, more focused code. |

|They help define and implement the internal data model. |

|They aid reusability. |

|Classes have a good design methodology, which leads to Java. But everyone is not used to them and it may take time to sink in. Below you will |

|see some code that implements classes. |

|Class Person |

|private nName as NotesName |

|private strUNID as String |

|sub new(strNewName as string, strNewUNID asString) |

|me.nnName = new NotesName(strNewName) |

|me.strUNID = strNewUNID |

|end sub |

|public function getName as String |

|if (me.nnName is nothing) then exit function |

|getName = nnName.Canonical |

|end function |

|public function getUNID as String |

|getUNID = strUNID |

|end function |

|end class |

| |

|  Everyday LotusScript Tip #9. Use the Evaluate command |

| |

|The Evaluate command allows you to run @Functions within LotusScript. It is sometimes quicker and easier, as it allows you to use your favorite|

|function in certain situations. |

|An example of it might be: |

|evaluate(|@unique|) |

|Don't overuse it though. Loads of LotusScript functions mimic @functions. |

|  Everyday LotusScript Tip #10. Use "trusted servers" |

|  |

| |

|It is good practice to use trusted servers because scheduled agents cannot normally open databases on other servers. |

|The "trusted servers" field in a Lotus Domino R6 server document's security section allows servers to trust other servers. By doing this, it |

|allows you to centralize "collection" agents. You also simplify your architecture and limit the number of agents you use. However, it does rely|

|on a fast, reliable network infrastructure. |

|As a final note, make sure to never trust servers in another domain. |

10 advanced LotusScript tips

[pic]

|Understand binding |

|Code for performance |

|Use lists and classes |

|Use class inheritance |

|Use platform-specific LotusScript code with classes |

|Use version-specific LotusScript code with classes |

|Use LSI_Info()/GetThreadInfo |

|Use the execute command |

|Use advanced logging |

|Mixing Java and LotusScript |

|Advanced LotusScript Tip #1. Understand binding |

|There are two types of binding: early binding and late binding. |

|Early binding is set by the compiler and works well because it uses type checking, works quickly and is easy to use. An example of early |

|binding might be: |

|Dim S as String |

|Late binding is set at runtime. It is very flexible, but doesn't use type checking. Unfortunately, the performance isn't as good as early |

|binding and you might run into some runtime errors. |

|Dim V as variant |

|Dim S as new NotesSession |

|set V = S.CurrentDatabase |

|print V.getTitle() |

| |

|Advanced LotusScript Tip #2. Code for performance |

|When you're coding for performance, always remember that expensive operations include opening Lotus Notes databases, and views and documents |

|with lots of fields. So, when you're collecting data, remember to cache views wherever possible and use NotesViewEntry instead of opening |

|documents. |

|As an example, let's say you have a Lotus Notes database with 100,000 documents in it. This would take seven hours to actually open every |

|document in the Lotus Notes database, if you don't code for performance and use views. If you do code for performance, it will only take you 60|

|minutes to open these using NotesView and only 12 minutes if you use NotesViewEntry! |

|Advanced LotusScript Tip #3. Use lists and classes |

|It's good practice to use LotusScript lists and classes because classes bind complex data and operations. Lists can look these up quickly in |

|memory. For a quick example, here's how we might extend our Person class: |

|dim People list as Person |

|dim PeopleByUNID list as Person |

|Dim P as new Person |

|("Joe Bloggs/ACME", "010101010201020") |

|.... |

|set People(P.getName) = P |

|set PeopleByUNID(P.getUNID) = P |

|if (isElement(People("Joe Bloggs/ACME"))) then _ |

|Print "Joe's UNID is: " + |

|People("Joe Bloggs/ACME").getUNID |

|if (isElement(PeopleByUNID("010101010201020"))) then _ |

|Print "UNID '010101010201020' is: " + _ |

|PeopleByUNID("010101010201020").getName |

| |

|Advanced LotusScript Tip #4. Use class inheritance |

|Class inheritance allows us to "Extend" classes to add functionality. For example: |

|class StaffPerson as Person |

|private strStaffID as String |

|sub new(strNewPerson |

|as String, strNewUNID as String) |

|end sub |

|public function setStaffNumber(newNo as String) |

|strStaffID = newNo |

|end function |

|public function getStaffNumber as String |

|getStaffNumber = me.strStaffID |

|end function |

|end class |

| |

|Advanced LotusScript Tip #5. Use platform-specific code with classes |

| |

|Dim s as new NotesSession |

|Dim mem as variant |

|select case s.platform |

|case "Windows/32" |

|set mem = new getMemW32() |

|case "AIX" |

|set mem = new getMemAIX() |

|case else |

|Print "Platform not supported" |

|set mem = nothing |

|end case |

|if (not mem is nothing) then |

|call mem.printMemory() |

| |

|Class getMem |

|function getMem() as long |

|getMem = 0 |

|end function |

|sub printMemory |

|print me.getMem() |

|end sub |

|end class |

| |

|Class getMemW32 as getMem |

|function getMem() as long |

|getMem = getWindowsMemory() |

|end function |

|end class |

| |

|Class getMemAIX as getMem |

|function getMem() as long |

|getMem = getAIXMemory() |

|end function |

|end class |

| |

|Advanced LotusScript Tip #6. Use version-specific code with classes |

| |

|Dim s as new NotesSession |

|dim vCU as variant |

|select case s.version |

|case 5 |

|set vCU = new createUser() |

|case 6 |

|set vCU = new createUserv6() |

|case else |

|Print "Version not supported" |

|set vCU = nothing |

|end case |

|if (not vCU is nothing) then call vCU.CreateUser(....) |

| |

|Class createUser |

|function createUser(...) as integer |

|.... |

|end function |

|end class |

| |

|Class createUserv6 as createUser |

|function createUser(...) as integer |

|.... |

|end function |

|end class |

| |

|Advanced LotusScript Tip #7. Use LSI_Info()/GetThreadInfo |

|You can use the LSI_INFO() command to get some runtime information. Be aware though that this information is superceded by the GetThreadInfo |

|command. |

| |

|If you use GetThreadInfo(11), that will return you the calling class. If you use GetThreadInfo(10), that will return you the function name. And|

|these are just the beginning. |

|Through error trapping, we can track where we came from. We don't have to pass lists of parameters to error trapping code. It also prevents |

|coding errors through using the copy and paste method. Here is an example of this in use, preceded by the calling code: |

|' calling code... |

|ExitFunction: |

|exit function |

|errorhandler: |

|Call RaiseError() |

|resume exitFunction |

|end function |

|Function RaiseError() |

|Dim thisType As String |

|Dim es as String |

|thisType = Typename(Me) |

|' Not a class, use the calling module instead |

|If (thisType = "") Then thisType = Getthreadinfo(11) |

|es = thisType & "::" & Getthreadinfo(10) & ": " |

|If (Err = 0) Then |

|es = es + "Manually raised an error" |

|Else |

|es = es + "Run time error: (" + Trim(Str(Err)) + ") " + Error$ + " at |

|line: "+ Trim(Str(Erl)) |

|End If |

|Print es |

|end function |

|Advanced LotusScript Tip #8. Use the execute command |

|By using the execute command, you can run LotusScript from a string. Doing this accommodates version/platform differences at runtime. Here's an|

|example: |

|Dim executeString as String |

|executeString = | |

|print "Hello world" |

|dim s as new NotesSession |

|dim db as NotesDatabase |

|set db = s.currentDatabase |

|print "Current Database name is: " + db.Title |

|| |

|execute (executeString) |

| |

|Advanced LotusScript Tip #9. Use advanced logging |

|By using the OpenNTF "OpenLog" solution, you can make simple LotusScript library additions to your code, provide "called from," "error," and |

|"line number" functionality. Our system now works on error trap and displays all objects in memory. |

|Advanced LotusScript Tip #10. Mixing Java and LotusScript |

|By mixing Java and LotusScript together, you can really get the most out of each scripting language. The trick is to use each language to its |

|strengths. For example, Java is good for Web service, network I/O, and multithreaded operations. LotusScript is the traditional Lotus Notes |

|development language and works in the user interface. |

|Mixing the two languages together is easy -- just call an agent, passing a Lotus Notes document. |

|You should also know that this works both ways, as you can call Java from LotusScript. This is called LS2J. An example is below: |

|// Create a Script Library of type "Java" called xlib |

|// containing the following function: |

|public class calculator { |

|public int add(int a, int b) { return a + b; } |

|public int div(int a, int b) { return a / b; } |

|public int mul(int a, int b) { return a * b; } |

|public int sub(int a, int b) { return a - b; } |

|} |

|Option Public |

|Use "xlib" |

|Uselsx "*javacon" |

|Sub Initialize |

|Dim mySession As JavaSession |

|Dim myClass As JavaClass, |

|calculator As JavaObject, a,b,c As Integer |

|Set mySession = New JavaSession() |

|Set myClass = mySession.GetClass("calculator") |

|Set calculator = myClass.CreateObject() |

|a = 10 |

|b = 5 |

|c = calculator.mul(a,b) |

|MessageBox "a * b = " & c |

|End Sub |

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download