The FooDoo Lounge

Handler Info - Care & Feeding

Index

Introduction

"Handler" is a generic term used to describe a block of code that performs a particular function - say writing something out to a file or rounding & truncating a number. Handlers are also known as "sub-routines", "functions" and "methods", amongst other names.

The use of handlers is roughly equivalent to the application or osax commands called in scripts, except that you define them yourself. Once they are tested and confirmed to work, it is possible to forget about the implementation details: Having written a tool to do a particular job, it is necessary only to keep track of the name of the handler, rather than the code that does all the work.

There are two basic reasons for using handlers. Firstly, they make it possible to avoid unnecessary repetition - a certain job may need to be done more than once in a script but there is no point writing the same code repeatedly - and secondly, handlers allow the script to be broken up into chunks that are much easier to write, test and maintain. The advantages second point in particular should not be underestimated when working on any but the simplest of scripts.

When to use Handlers

There is no hard and fast answer to this question for AppleScripters. The nature of the language is such that many people who have no other programming experience use it and whilst handlers are a basic concept for programmers, they're not necessarily within the sights of people writing workflow automation scripts.

If you write simple to moderately complex scripts and find that you can write, test and maintain them easily, then you probably have no need to use them. You may still benefit from their use though.

If you:

... then handlers will definitely benefit you.

I believe that the use of handlers encourages better practice and allows for more efficient code that is much easier to manage in every respect. Whilst the benefits of the modularised approach that handlers encourage multiply over time, they might take a little while to get used to and will almost certainly slow the script writing process initially.

Writing Handlers

Handlers usually (though not always) take one or more pieces of data, do something to or with the input and usually (though not always) return a result to the caller.

Starting with inline code, let's look at how we might write a string to a new text file. Two pieces of data are required for this: a full path to the file, such as "Mac HD:desktop folder:test.txt" and some text to write. We'll create these and store them in variables:

set testFilePath to (path to desktop as string) & "test.txt"-- generate a full path string for the file we're about to create and store it in a variable
set testText to "Hello Breakfast!"

Now, using the information contained in the Standard Additions dictionary, we generate the basic code required to automagically create the file (if it doesn't already exist) and write the text to it:

set ofaNum to open for access file testFilePath with write permission
write testText to ofaNum starting at eof
close access ofaNum

As long as the data in our variables is valid, this will write the text to the file specified. If anything goes wrong, the script will return an error and the file may be left open, so it's best to wrap it in a try/on error block and make sure the file is really closed before continuing:

try
    set ofaNum to open for access file testFilePath with write permission
    write testText to ofaNum starting at eof
    close access ofaNum
    return testFilePath -- return the full path, as an indication of success (optional)
on error errString number errNum
    try -- in case it is closed
        close access file testFilePath -- use the full path, in case the ofa number is invalid
    end try
    error errString number errNum -- propagate the error, so we know it happened
end try

We now have a fully functional code block that can be counted on to either work or clean up after itself if something goes wrong. The next step is to wrap it into a handler declaration. As detailed in the AppleScript Language Guide, (pg 289-299 of the pdf version) AS handlers come in two types - positional parameter and labelled parameter. The most commonly used is the positional parameter type and an example declaration of our above code would look like something like this:

on writeAppend(testFilePath, testText)
    -- code goes here
end writeAppend

The same thing in a labelled parameter style might look like this:

on writeAppend at testFilePath from testText
    -- code goes here
end writeAppend

There is no functional difference these two declarations. Labelled parameter type handlers exist in AppleScript simply to improve readability and only certain keywords can be used. Whichever type is used, the declarations must match the calls. See the AppleScript Language Guide (page 291 of the pdf version) for the list of keywords.

Using Handlers

Once the code is inside the handler we can call it, passing the information to it and creating a variable (optional in this case) to hold the result it returns, in a single line:

set writeResult to writeAppend at testFilePath from testText
--> "Mac HD:desktop folder:test.txt"

See writeWithAppend (located here) for another, functionally identical, version of this handler.

It is worth noting that any variables defined in a handler (including those in the declaration) are local - they have no meaning outside the handler and are destroyed once the routine is exited.

Handlers can be thought of as building blocks, which can be combined to produce higher level functions. This example calls the handler above and one other and adds information to the string before it is written out to the file:

to writeLog at logFilePath from logText
    set dateString to isoDate from (current date) -- get an abbreviated date string
    set writeString to dateString & ": " & logText & return -- create a new string containing the date stamp and log text, ending it with a return
    return writeAppend at logFilePath from writeString-- write it out to the log file
end writeLog

Handlers can contain anything that would otherwise be used inline, so we could check for the existence of the log file before writing and generate an opening line if required. This uses another handler and writes a log entry accordingly:

to writeLog at logFilePath from logText
    if (itemExists against logFilePath) then
        set writeString to "" -- declare an empty string variable
    else
        set writeString to "Start Log " -- declare an opening line for the log
    end if
    set dateString to isoDate from (current date) -- get an abbreviated date string
    set writeString to writeString & dateString & ": " & logText & return -- create a new string containing the date stamp and log text, ending it with a return
    return writeAppend at logFilePath from writeString-- write it out to the log file
end writeLog

The result of this handler being called when the file doesn't already exist, would be a file with text in it looking something like this:

Start Log 2003-05-15: kwomble security check - OK

If the file had been created on the previous day by the same routine, it might look something like this after the second call:

Start Log 2003-05-14: kwomble security check - OK
2003-05-15: Warning - the kwomble has been quaffed by rampant cretones

Further Reading

The definitive reference for information about anything to do with AppleScript language constructs is the AppleScript Language Guide. This particular topic is covered in Chapter 8, starting on page 279 of the pdf version. If you are running OS X Jaguar (10.2) or earlier and have the Developer Tools installed in their default location, you will find the pdf here. Panther (10.3) users should find it here. An html version of the ASLG here in Jaguar, or here in Panther.

You may also wish to take a look at this article on using libraries, which are collections of handlers that can be reused in any number of scripts.

 

The FooDoo Lounge is Copyright © Richard Morton 2002-2005

|| url: http://www.foodoo.sunreal.com.au/info/handlers.html
|| created: 25-May-04, 11:27 PM; updated: 25-May-04, 11:27 PM
|| size: 35459 bytes