Blogs

  • Browse Blogs
  • My Blog
  • My Updates

Tags Help

  • View as cloud  | list

Similar Blogs

photo

Beyond The Ye...

246 Entries |  Peter Presnell
Updated 
RatingsRatings 13     CommentsComments 426
photo

TexasSwede

109 Entries |  Karl-Henry Martinsso...
Updated 
No RatingsRatings 0     CommentsComments 93
photo

Erik Brooks

34 Entries |  Erik Brooks
Updated 
RatingsRatings 7     CommentsComments 83
photo

Lotus Nut

111 Entries |  Chris Whisonant
Updated 
RatingsRatings 23     CommentsComments 157
photo

Patrick Picar...

62 Entries |  Patrick Picard
Updated 
RatingsRatings 2     CommentsComments 111

Bookmarks

Yellow is the New Blog

Blog Authors:  Tim Tripcony  

All entries tagged with lotusscript

Re: So Programming Language Really Does Matter

Tim Tripcony  |     |  Tags:  lotusscript java ssjs programming  |  Comments (0)

In response to: So Programming Language Really Does Matter

@Henning, in my opinion, IBM is absolutely running an experiment, but a rather clever one. The JSF specification is over 8 years old, and SSJS has been around even longer (Rhino, for example, was orginally created in 1997, and LiveWire was released in 1996), so the underlying technology isn't exactly new. On the other hand, in many ways, XPages have allowed Domino to leapfrog most of the web development platforms that are currently trendy... a phenomenon I honestly never thought I'd see.

I found this video about SSJS on the YUI Theater site intriguing:

http://developer.yahoo.com/yui/theater/video.php?v=isaac-ssjs

It's a short clip (at least compared to most on that site), but the implications are clear: platforms implementing SSJS are still considered leading edge, but there's strong momentum behind this, and over time, having a rock solid implementation is definitely going to be a strong selling point for any platform.

In case you're curious, Wikipedia has a listing of all known implementations of SSJS:
http://en.wikipedia.org/wiki/SSJS

...and yes, there's a glaring omission. :(

I was wrong: plus is better than ampersand

Tim Tripcony  |     |  Tags:  operators concatenation lotusscript  |  Comments (2)
A deeply ingrained habit of mine is using ampersand to concatenate strings in LotusScript (when I'm not bypassing operators entirely, such as using NotesStream or StringBuffer to optimize massive concatenations) - so deeply ingrained that when NTF asked me today why I don't use plus instead, for a moment I couldn't remember my original reason for switching... but after mulling it over briefly I remembered that I hate type coercion with a passion. It bugs me because it tries to be too smart and often fails. Just like extended syntax. For example:

"100" + "200" = "100200"
This may seem obvious: we're concatenating 2 strings, so it takes the value of one and appends the other. But...
100 + "200" = 300
The LotusScript interpreter decides that, since it's starting with a numeric value, you're obviously trying to add another numeric value. Since in this case the string can be converted to a number, it does so and adds the operands instead of concatenating them as strings. So, logically:
"100" + "200" = ...300?
That's right: plus is primarily an addition operator, so if there's any possible way it can remain one, it will. In fact:
100 + "two hundred" = type mismatch - at compile time if it knows the latter is a string, at runtime if it's a variant... reason #372 why variants should be used sparingly and with caution. Speaking of which, I think it was Nathan who once told me, "Variant should have been called Deviant... 'cause it'll accept any object". Yeah, I know, ew. But you gotta admit, that's darned funny.

So how did he convince me plus is better than ampersand despite my pedantic aversion to coercion?
  1. (Author's note: this was actually a third realization, which came after the discussion.) In LotusScript string concatenation, some coercion is unavoidable. Since ampersand's only valid use is as a concatenation operator, any scalar entering the operation is treated as a string whether you wanted it to be or not. So unless everything's a string to begin with, it's coercing anyway, it's just safer. But if we're subscribing to a "flexible input, strict output" philosophy, we're already making sure we know what data type we're dealing with and handling it appropriately, so ampersand doesn't really gain us much... it's only when we're being lazy about this that ampersand saves us from returning unexpected results.
  2. Whenever possible, it's helpful to use conventions that are optional in one language but required in another. In Formula, but - more importantly, over the long haul - also in JavaScript and Java, we don't have a choice: we have to concatenate with plus; might as well do it in LotusScript too so that we're spending more time thinking about how to implement the actual functionality at hand and less time trying to remember what operators to use in the current language... same reason I always surround my conditional expressions in parentheses: I know LotusScript doesn't force me to, but JavaScript and Java do, so why erode a good habit just because LotusScript would allow me to be lazier than that?
  3. In XML, an ampersand must be entity-encoded; a plus can be left alone. This was the "you had me at hello" argument. For the foreseeable future, any convention that allows my code to be handled more easily within an XML context is likely to appeal to me, even if it forces me to abandon a long-held alternate approach.
Anything you can think of that I'm overlooking? If not, I'm switching to plus effective immediately. That is, as soon as I get back to writing LotusScript... it's been nothin' but Java for me all week.

Quick tip: shorthand date adjustment in LotusScrip...

Tim Tripcony  |     |  Tags:  lotusscript  |  Comments (2)
How many times (this week) have you needed an object representing a past or future date in LotusScript? Pretty standard stuff... you instantiate a NotesDateTime with Now, then call an adjustment method to correct the date:

Dim datTomorrow As New NotesDateTime(Cstr(Now))
Call datTomorrow.AdjustDay(1)


And it's only two lines of code... but there's a way to do it in one by performing the adjustment immediately in the instantiation. LotusScript actually treats each date as a Double. The integer portion is the date (the number of days since 12/30/1899) and the fraction indicates the percentage of the day. Hence, if you convert Now to a Double instead of a String it'll return something like 39665.7083333333 ( Cdat that and Cstr it, and you'll get "8/5/2008 5:00:00 PM" ). As a result, you can add/substract the number of days you want to adjust, convert the result to a scalar date, and then a String... then pass that into the NotesDateTime constructor and by the time the object is initialized it already has the desired value:

Dim datTomorrow As New NotesDateTime(Cstr(Cdat(Cdbl(Now) + 1))) 'Same time tomorrow
Dim datOneWeekLater As New NotesDateTime(Cstr(Cdat(Cdbl(Now) + 7))) 'Same time next week
Dim datLaterToday As New NotesDateTime(Cstr(Cdat(Cdbl(Now) + (1/24)))) 'Come back in an hour


Enjoy.

One last thing (for now) about event binding: dele...

Tim Tripcony  |     |  Tags:  lotusscript  |  Comments (0)
Thanks for all the feedback on my recent ramblings regarding LotusScript event binding. Just one more post on this and I'll leave it alone for a while.

Both Thomas Bahn and Devin Olson chimed in on the last article in this series, and Peter Presnell posted a great article about compensating for weaknesses in LotusScript's OOP model, all of which prompted some useful mods to the framework. If you've already downloaded it, you may want to snag a fresh copy.

Thomas suggested that, instead of calling each bound Sub recursively with stack trace checking to prevent infinite recursion in cases where the event handler has not been overridden in the derived class... just bind the event to a delegate function that, in turn, calls the actual event handler. This results in a bit more code within the framework classes, but since the structure is such that you won't need to modify those classes again until new events are added in future versions of Notes, I'm perfectly fine with that as a tradeoff for avoiding recursion altogether. Don't worry, we'll find another excuse to use Nathan's code locking approach.

Devin's comments and Peter's article both provided insight into forcing (at runtime, at least) methods and classes to be treated as abstract, and, as such, overridden and derived, respectively. That was a bit of a mouthful, so perhaps it would be simpler to just show you the most basic of the EventBinder child classes in its current form:

Public Class TimerEventBinder As EventBinder
    
    Public Sub New (Source As NotesTimer)
        Dim classname As String
        
        Let className = Typename(Me)
        If (className = "TIMEREVENTBINDER") Then
            Error ERR_ABSTRACT_INSTANTIATION, MSG_ABSTRACT_INSTANTIATION & classname
            Exit Sub
        End If
        Set Me.context = Source
    End Sub
    
    Private Function bindEvent (Byval eventName As String, Source As Variant)
        Select Case Lcase(eventName)
        Case "alarm":
            On Event Alarm From Source Call delegate_Alarm
        Case Else:
            Error ERR_UNSUPPORTED_EVENT, MSG_UNSUPPORTED_EVENT & Ucase(eventName) & " in class " & Typename(Me)
        End Select
    End Function
    
    Private Sub delegate_Alarm ( Source As NotesTimer )
        Call Me.Alarm(Source)
    End Sub
    
    Private Sub Alarm( Source As NotesTimer )
        If (OPTION_FORCE_METHOD_OVERRIDE) Then
            Error ERR_ABSTRACT_METHOD, MSG_ABSTRACT_METHOD & Typename(Me)
        End If
    End Sub
    
End Class


The end result is a class that cannot be instantiated; to use its functionality, you must create a class that inherits from it. Similarly, in order to attach to the Alarm event, you must override the Alarm Sub with the same method signature (although there's a Const in the framework library that you can change to False to allow silent failure, but if you're not overriding the method, your event will do nothing, so you probably want an error thrown to notify you of the omission). Finally, another error is thrown if you try to attach to an event that doesn't even exist.

All in all, I think this has become a pretty solid framework, allowing you to bind UI events within an OOP context in a straightforward manner. Let me know if you have any additional suggestions for how this should be structured or examples of where it would be particularly useful.

Workaround for LotusScript event binding

Tim Tripcony  |     |  Tags:  lotusscript  |  Comments (0)
As a followup to last night's post, after receiving some helpful advice from both Peter Presnell and the Batman to my Robin, I found a workaround. I think it's crazy sexy cool.

To very briefly recap, here's the problem:
  • Under normal class inheritance circumstances, calling a class method will execute the code at the youngest level (i.e. child class vs. parent) where a method definition can be found.
  • When a class method binds an event to another method in the same class, it's binding the definition of that method within the same class. So if a class inherits from another that is binding events and does not override the method doing the binding, the event will be bound to the parent's copy of the target method, even if the child class overrides the bound method.
And here's the workaround:

Within the previously abstract definition of the event handler, we issue a call to itself, but with two subtle additions:
  1. We use the Me keyword to identify the method. Designer Help implies that this forces the method call to refer to a current class member, but that's not entirely accurate: it actually forces a reference to a current object member. In other words, even though the method being bound to the event is in a parent class, when it's executed it still knows it's inside the scope of an object typed to a child class, so calling Me.whatever executes the same method as defined in that object's class, not its parent class.
  2. We do a stack trace check to make sure that we don't cause infinite recursion. You see, if the bound method isn't overridden in the child class, Me.whatever only exists in the parent class, so it would keep calling itself over and over... stack overflow, Notes go boom.
For example:

Private Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)
    If Not(Me.isRecursive(Fulltrim(Split(Lsi_info(14), Chr(10))))) Then
        Call Me.inviewEdit(Source, Requesttype, Colprogname, Columnvalue, Continue)
    End If
End Sub


The isRecursive method is actually defined at the very top level:

Private Function isRecursive (stackTrace As Variant) As Boolean
    Dim stackCount As Integer        
    
    Let stackCount = Ubound(stackTrace)
    If (stackCount > 0) Then
        Let Me.isRecursive = (stackTrace(stackCount) = stackTrace(stackCount - 1))
    End If
End Function


The end result is a framework in which you can create a derived class that overrides only the events you wish to bind, then attaches to those events, either from within the class itself or via calls to its inherited attachEvent method:

Public Class ExampleDocumentBinder As DocumentEventBinder
    
    Public Sub New (Source As NotesUIDocument)
        Call Me.attachEvent("Querysave", Source)
    End Sub
    
    Private Sub Querysave(Source As Notesuidocument, Continue As Variant)
        Dim newEdit As String
        Dim editType As String
        
        If (Source.IsNewDoc) Then
            Let editType = "created"
        Else
            Let editType = "modified"
        End If
        Let newEdit = Cstr(Now) & " - Document " & editType & " by " & Me.getSession().commonUserName
        Call Source.Document.ReplaceItemValue("AuditTrail", Fulltrim(Split(newEdit & Chr(13) &_
        Join(Source.Document.GetItemValue("AuditTrail"), Chr(13)), Chr(13))))
    End Sub
    
End Class


When calling attachEvent, you can pass a single event name, a comma-delimited list of events, or an Array of event names. The second parameter is the object whose event you're binding; if you pass Nothing, it will use the object initially passed to the constructor. The entire framework (along with a few examples) is available for download.

By the way, you might have noticed that this workaround uses the infamous Lsi_info. This undocumented function is almost universally considered unsafe, but is generally acceptable to use in an unthreaded context... so you wouldn't want to use this technique in web agents that could be running simultaneously. But as you've no doubt also noticed, event binding is entirely specific to Notes UI contexts (with the exception of NotesXMLProcessor descendants... and I think we'll leave tackling SAX parser event binding for another day), so I'm not too nervous about using Lsi_info for this niche purpose. About an hour ago, Nathan mentioned he's been playing with CodeLock as a way to make this even safer, so you might be seeing further revision to this model post haste (On Event PostHaste From... tee hee, just kidding).

Possible bug in LotusScript event binding

Tim Tripcony  |     |  Tags:  lotusscript  |  Comments (0)
Crack open a tasty beverage and commandeer a comfortable chair... this is going to be a long one.

In preparation for another Yellowcast episode, I've been playing with remote event binding in LotusScript. It's something I find myself using more and more frequently, but I decided I wanted to build a mini-framework to standardize how I approach it, as well as giving me something to provide to my loyal readers for instructional purposes if not ongoing usage. In the process, I've stumbled upon what appears to be a bug in how LotusScript handles overridden methods in derived classes when event binding is involved.

Perhaps I should back up a bit first. What is remote event binding? Simply put, it allows you to designate custom methods to be called when a LotusScript UI object's event is triggered. You're already familiar with this, though you might not realize it: in Domino Designer Help, check out the documentation for the NotesUIDocument class. Scroll past the Properties and Methods, and you'll see a list of familiar events. To briefly belabor the obvious, the reason they're so familiar is because they map directly to (a subset of) the events on the Objects tab when you're editing a form element in Designer. Every time you've added code to the Querysave Sub in a form, you've locally bound that event. When a user tries to save a document based on that form, the Querysave event of an anonymous NotesUIDocument instance is triggered, so the code you entered is executed. But this is actually just a convenient shortcut for binding to that event. There are a couple other ways you can achieve the same result using the oft-overlooked On Event statement.

The easiest is to define a new top-level function within the same element. For example, if you create a new Sub (say, myQuerySave) from within the global declarations of the form, you can bind that Sub to the Querysave event:

Sub Postopen(Source As Notesuidocument)
    On Event Querysave From Source Call myQuerySave
End Sub


In this case we're taking advantage of a local event binding (namely, the Postopen event) to get an automatic handle on the aforementioned anonymous NotesUIDocument and bind its Querysave event to our custom Sub. What does that gain you? Well... absolutely nothing, to be honest. You could have just entered the contents of the new Sub into the existing Querysave sub, and the results would have been the same. But this gives us an obvious illustration of how event binding actually works: when an event is triggered, every bound routine is called, and passed a predetermined set of parameters... which means each bound routine must have the exact same method signature (nitpick: technically the variable names can be changed but the sequence of data types must directly correspond). This is crucial to remember for later, so tuck that away for now while we delve into a slightly more advanced approach.

Imagine you've defined a custom class that may or may not be instantiated while a document is open. Perhaps it's a class defining the document's workflow, determining its current state, who needs to approve it based on who submitted it and a specified dollar amount, etc. Because you've moved beyond elementary development (pardon the assumption... if you've bothered to read this far, it's likely a safe one), you no longer use the standard input validation events, preferring instead to perform single-message validation in the Querysave or Querysend. After all, what user wants to get smacked in the face with a separate Msgbox for every nonconforming field each time something triggers a document refresh? Not to mention that typical input validation formulas completely preclude a notion of "Save As Draft", requiring the user to completely (and correctly) populate a document in order to save any of it. I once inherited a purchase requisition application that was losing the company money simply because its interface was obnoxious. Seriously. Users would try to create a requisition, and before the form had even loaded, "Smack! You forgot to enter a dollar amount!"... then when exiting any field (that's right, "automatically refresh fields" was enabled):
"Smack! You forgot to enter a vendor name!"
"Smack! You forgot to enter a shipping address!"

(These weren't the exact error messages, of course, just a rough approximation of user perception.)

Apparently (and unsurprisingly) people would get so annoyed that they'd give up, intending to try again later, but by the time they mustered up the courage to face the application again, the promotional discount period for the item they wanted to purchase had already expired, so we'd have to pay 10 or 15% more than they'd originally planned. Have I ever mentioned how much I hate standard input validations? Epic hate.

Uh... where was I? Oh yeah, event binding. So you've got a class that is only instantiated if the user is trying to actually submit the document for approval, and you want to trigger the field validation right before the workflow status is changed, because up until that point, who cares if they haven't filled out the required fields? So you put the validation code in a separate method of that class (because each method should only do one thing and do it well)... from anywhere within that class (including the constructor), you can bind the class method to the event.

Public Sub New(Source As Notesuidocument)
    On Event Querysave From Source Call validateDocument
End Sub


Why is this any better? A couple reasons. First, the code is never executed unless the class is instantiated, so you don't have to do any checking within the event handler itself to make sure it even needs to do anything. Second, and more importantly, the event handler has access to everything inside the class. You don't have to maintain a bunch of global variables or pass a bunch of local variables to the validation routine in order for it to know how to do its thing. It lives inside an object that's (hopefully) already storing what that routine needs to know and/or can hand off control to other methods of the same object, which in turn share the same capacity. Finally, if you had an entirely different type of class - one that you've instantiated numerous instances of... which you presumably wouldn't, in this example - each can bind a method to the same event, allowing you to define the functionality in a single location but take advantage of in each instance, potentially in different ways depending on what is unique to each instance.

One more implication to consider before we move on: because all bound event handlers are called with the same parameters, each by reference (as opposed to by value), any changes to a parameter passed to each handler is visible to any other handlers. For example, imagine we want to add another handler to the above example:

Public Sub New(Source As Notesuidocument)
    On Event Querysave From Source Call validateDocument
    On Event Querysave From Source Call updateHistory
End Sub


Not only are any changes made to the NotesUIDocument within validateDocument still intact when updateHistory is called, any events that pass a Continue parameter check its value between handlers and suspend all outstanding handlers if it is now False. In other words, in the new example, if validateDocument toggles Continue to False, updateHistory is never called, nor is the intrinsic handler (writing the document to disk). To expand this just a tiny bit more:

Public Sub New(Source As Notesuidocument)
    On Event Querysave From Source Call validateDocument
    On Event Querysave From Source Call updateHistory
    On Event Postsave From Source Call notifyNextApprover
End Sub


Just as you don't have to verify within updateHistory that validation was successful - because that routine wouldn't have been triggered if validation failed - you don't have to verify that the history was correctly updated before sending the approval request, because if it hadn't been, the intrinsic Querysave handler is skipped, which means the Postsave event never fires (NOTE: this assumes you're toggling Continue to False in your error handler block in each event handler). Your code is simplified, because you're allowing Notes to manage your event flow for you. The only downside is that anyone who has to maintain your code later will be confused by the absence of custom flow handling if they don't understand how automatic all of this is and will probably add in their own unnecessary conditional processing.

Okay, now let's take this even further. All of the above could be accomplished by defining the class directly in the form's declarations, but it's more likely that, for purposes of maintainability (per my earlier assumption), you defined it in a script library that's loaded by the form via a Use statement. Hence, the document knows about that code and can leverage all its yummy bindiness. Believe it or not, you can still bind to an object's events even if the design element that creates the object knows nothing about the code binding to its events. If you open a document via NotesUIWorkspace.editDocument, for example, you now have a NotesUIDocument handle on the document being opened, and can bind methods defined (or loaded) within the context from which the document was opened to the document's events (this memory scope overlap was a key factor in Nathan's Revolution). This could allow you, for example, to define standard audit trail logging in a centrally inherited script library and bind that behavior to every form in every application... without ever loading that script library in any of those forms. You wouldn't want to though, because then your audit trail logging only occurs if the document is opened in a way that allows the event(s) to be correctly bound (so you'd have to bind to the Queryopendocument event in every view to cancel the standard open and instead open the very document they were trying to open, but via a handle you can bind to... and, of course, doclinks bypass all of this). But flip that around: what if you want an event to be handled differently based on how the user opened the document? That's where this really comes in handy. Instead of using a form formula to load a completely different form, you can bind entirely different event handlers to the document depending on whether they opened the document via a doclink, an agent, a button, or simply double-clicked it in a view. I'll let your imagination run wild about all the possibilities this opens up.

So what's the problem? Well, as you might expect, all of this comes with some caveats in addition to those already described or alluded to. For one, it's extremely sensitive about memory scope. In fact, it works best if the bound event handler is declared globally... which is why it's best to bury this inside a class, so you can consolidate everything the handler needs to do and know inside the same object, instead of relying on a glut of global variables and top-level functions. But that's fine... we should be doing that for the bulk of our code anyway. What worries me is that I also seem to have found a flaw in class inheritance. Here's your opportunity to tell me what I'm doing wrong (okay, yet another opportunity). Consider the following, from a dummy script library:

Public Class A
    
    Public Function attachEvent
        Call Me.bindEvent()
    End Function
    
    Public Function bindEvent
        'abstract function, will be overridden in a descendant
    End Function
    
End Class

Public Class B As A
    
    Public Function bindEvent
        Call Me.doEvent
    End Function
    
    Public Function doEvent
        'abstract function, will be overridden in a descendant
    End Function
    
End Class


Another library that loads the above library contains the following:

Public Class C As B
    
    Public Function doEvent
        Msgbox "Consider it done"
    End Function
    
End Class


Load that library into an agent, button, etc. and initialize a C instance (oops, poor naming choice... no, we're not messing with API calls here), then call attachEvent. B inherits from A, so it gets its attachEvent method, and overrides its bindEvent method. C inherits from B, so it gets the inherited attachEvent and overridden attachEvent, and overrides doEvent. As you would expect, you'll get a Msgbox. No problem. But when you introduce event binding into the mix:

Public Class EventBinder

    Public Function attachEvent (Byval eventName As String, Source As Variant)
        If (Source Is Nothing) Then
            Set Source = Me.getContext
        End If
        Call Me.bindEvent (eventName, Source)
    End Function
    
    Private Function bindEvent (Byval eventName As String, Source As Variant)
        'abstract function, will be overridden in a descendant
    End Function

End Class

Public Class ViewEventBinder As EventBinder

    Private Function bindEvent (Byval eventName As String, Source As Variant)
        Select Case Lcase(eventName)
        Case "inviewedit":
            On Event Inviewedit From Source Call Inviewedit
        Case "queryopendocument":
            On Event Queryopendocument From Source Call Queryopendocument
        End Select
    End Function

    Private Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)
        'abstract function, will be overridden in a descendant
    End Sub

End Class

Public Class ExampleViewBinder As ViewEventBinder

    Private Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)
        Dim docTarget As NotesDocument        
        Dim columnId As Integer    
        
        If (Requesttype = 3) Then
            Set docTarget = Me.getCurrentDatabase().getDocumentById(Source.CaretNoteID)
            Forall columnName In Colprogname
                If Not (Left(columnName,1) = "$") Then
                    Call docTarget.ReplaceItemValue(columnName, Columnvalue(columnId))
                End If
                Let columnId = columnId + 1
            End Forall
            Call docTarget.Save(True, True, True)
        End If
    End Sub

End Class


So, if I instantiate an ExampleViewBinder and call attachEvent("inviewedit", uidoc), it should bind Inviewedit to the same event for the uidoc handle... which it does, but it binds the empty Sub defined in ViewEventBinder, not the overridden method in ExampleViewBinder. So when I trigger the event with debugging enabled, I see it calling the abstract function, not its overridden definition. In theory, when attachEvent is called, it should be calling ExampleViewBinder's inherited definition for that method; likewise, when that method calls bindEvent, it should be calling the inherited definition, which should bind to the overridden Inviewedit Sub. But it doesn't. So, if any of you are still here after all this blather, please tell me what I'm missing.

0.01 nanoseconds

Tim Tripcony  |     |  Tags:  lotusscript  |  Comments (0)
Ever since Peter von Stöckel posted about using NotesDatabase.GetProfileDocCollection to quickly create an empty NotesDocumentCollection, I've been meaning to benchmark it against a database with hundreds of thousands of documents, but keep forgetting to. Earlier tonight I had a reason for needing an empty collection, was reminded of the benchmarks Nick Wall provided on Peter's post (tested on a Domino 6.5.3 server), and realized that my web server log would be an ideal candidate to see if the speeds are similar in the Notes 8 client... at the time, it contained 541,269 documents. Here's how it turned out:

 db.Search
({@False}, Nothing, 0)
db.Search
({@False}, datTomorrow, 0)
db.GetProfileDocCollection
( "EmptyCollection" )
13.0740.1910.04
23.2950.2010
33.1040.2010
43.1250.20
53.0540.2010
63.2050.190.01
73.0840.20.01
83.1150.20
93.0940.20
103.2250.20
Average
(in seconds)
3.13750.19840.006
Per document
(in nanoseconds)
5.97650.36650.011


Still scales pretty well. Let me know if you've done any benchmarking against a huge database that also contains a significant number of profiles... my DomLog only contains one.

PercentRem.com

Tim Tripcony  |     |  Tags:  wiki lotusscript  |  Comments (0)
I just bogarted registered PercentRem.com. At the moment it's just a default instance of DominoWiki (although I did update the logo), but my current plan is to dedicate that wiki entirely to LotusScript. The language definitely has its limitations, but it's amazing what it can do when properly coaxed. My hope is to make this a living, breathing, centralized repository of tips, tricks, pitfalls, righteous hacks, and so forth. Let me know if you have any thoughts, ideas, suggestions, etc. Due to Ben's excellent versioning engine, I'ma just leave it wide open for the time being, so feel free to drop in any content that you feel would be a good fit. Over time I hope it will coalesce into a semi-structured reference on what the language truly can and can't do.

Cheap date

Tim Tripcony  |     |  Tags:  lotusscript domino  |  Comments (0)
I just had an epiphany... or maybe just an apostrophe. I'm in the middle of some code that does some date matching (determines which document to process based on date information posted from a web application). I don't have control over the date format posted to the agent, and currently the format is [Month abbreviation][unpadded day number][day number suffix] - i.e., Apr15th. I already knew that passing "April 15th" to the constructor of a NotesDateTime object would create an instance representing [04/15/2008 12:00:00]. But did you know that "Apr15th" will too? As it turns out, it actually ignores the suffix: "Apr15bogus", for example, creates a date with the same properties. Always good to find these things out before I resort to string parsing...

Skip to main content link. Accesskey S
IBM Lotus Connections Help Tools About

Tags

A tag is a keyword that is used to categorize an entry. To view the entries with a particular tag, click a tag name or enter a tag in the box.
The tag cloud indicates the frequency of tag use. Popular tags appear darkest. The slider control adjusts how many tags are displayed in the tag cloud.