DekGenius.com
[ Team LiB ] Previous Section Next Section

12.5 With

A with block is used to modify Apple events sent within its code to target applications, specifying certain external attributes of those Apple events. Two types of with block are currently defined: a timeout block and a transaction block.

12.5.1 Timeout

Recall from Section 4.1 that during interapplication communications, the sender of an Apple event may attach to that Apple event a specification of how long it is willing to wait for a reply. This is the Apple event's timeout period. If the target does not reply within the specified timeout period, for whatever reason (the operation might be too lengthy, the target application might be otherwise engaged, and so forth), the System stops waiting for a reply and reports to the sender that the Apple event timed out. This report arrives as an error; your script can handle this error and proceed (see Section 12.7, later in this chapter).

This entire mechanism is valuable because, among other things, it saves the sender from hanging indefinitely while waiting for the target to reply; if the target takes too long to reply, the sender is able to proceed nonetheless. Of course, the sender must then do without any reply from the target; but the point is that a script can be written to take account of a problem of this kind, and reporting the problem to the user and proceeding or terminating in good order is certainly preferable to hanging or appearing to hang while waiting for a reply that is taking a long time to arrive and that may, indeed, never come.

All Apple events sent to target applications have a default timeout value of one minute. This is a good compromise between waiting sufficiently long for lengthy operations to complete and waiting so long (or not having any timeout at all) that a script can hang or appear to hang. If this value is acceptable to you, you don't need a timeout block to change it.

To change the timeout value temporarily using a timeout block, use this syntax:

with timeout of integer second[s]
        -- code
end timeout

This affects only code within the block; afterwards, Apple events revert to the default timeout value. To wait indefinitely, use an extremely large integer.

To illustrate, we'll command the Finder to perform an operation so long that without a timeout specification it probably wouldn't have time to reply—we'll ask it to cycle down the entire hierarchy looking for a certain kind of file:

with timeout of 100000 seconds
        tell application "Finder"
                get every application file of entire contents ¬
                        of disk 1 where its creator type is "aplt"
        end tell
end timeout

If we don't provide a timeout block, this code will time out before the Finder is finished, and we'll get an error: "Finder got an error: AppleEvent timed out." Even if the Apple event times out, the Finder will still be cycling down the entire hierarchy, and it will keep doing so until it finishes. So don't run that example unless you're not planning on using the Finder for a while.

12.5.2 Transaction

A problem that can arise with interapplication communications is that a target application is promiscuous. While you're being a sender and talking to a target application, some other sender can come along and talk to it as well. If this happens in the middle of a series of Apple events from you, it can alter the state of the target application, messing up what you're trying to accomplish.

The Apple event solution to this is the transaction. A transaction is a kind of permission slip allowing you to unify multiple commands. You start by asking for this permission slip, and the target application returns a transaction ID of some sort. You then continue sending the target application Apple events, showing it the transaction ID every time. When you're done, you tell the target that you're done (showing it the transaction ID, of course), and that transaction comes to an end. Not every scriptable application implements transactions (would that they did); a commonly used application that does is FileMaker Pro.

The target application itself is responsible for deciding how to implement the notion of a transaction. All you care about is that state should be conserved throughout the multiple commands of a single transaction. FileMaker's approach is to implement a transaction as monopolization: once you've asked for the permission slip and obtained the transaction ID, FileMaker will simply refuse to respond to any Apple event that does not show the transaction ID, until you tell it the transaction is over, at which point it returns to its normal state of promiscuity.

The way to obtain, show, and release the transaction ID is by wrapping your transactional communications in a transaction block, which looks like this:

with transaction
        -- code
end transaction

All the actual business of dealing with the transaction ID is handled transparently for you. The with transaction line causes an Apple event to be sent to the current target asking for the transaction ID. Then all the application-targeted Apple events inside the block are accompanied by this transaction ID. Finally, the end transaction line sends the current target one last Apple event accompanied by the transaction ID, telling it to leave transaction mode.

In this example, we monopolize FileMaker Pro long enough to create a Find request and perform it:

tell application "FileMaker Pro"
        with transaction
                tell database 1
                        show every record
                        set f to create new request
                        set cell "lastname" of f to "neuburg"
                        find
                end tell
        end transaction
end tell

There is one important thing to notice about that code: the transaction block is inside the tell block. It is essential to structure your code this way; the application with which you want to carry on a transaction must be the target when the with transaction line is encountered, so that AppleScript knows where to send that first Apple event asking for the transaction ID. Unfortunately, this means we run smack dab into a bizarre terminology problem. It turns out that FileMaker Pro's dictionary also implements the opening and closing transactional Apple events as the commands begin transaction and end transaction. This means that when you say end transaction inside a tell block addressed to FileMaker Pro, it is seen as FileMaker's end transaction command, not as the end of the transaction block. The script then won't compile. The workaround, which is terribly annoying, is to delete the word transaction from the end transaction line every time you are about to compile the script.

You might worry about what happens if something goes wrong in the middle of the transaction block. What if we say something that generates an error? We'll never reach the end of the transaction block, and that means we'll leave FileMaker Pro in a transaction state, refusing to respond to Apple events. You're perfectly right to worry about this; you certainly don't want to leave FileMaker Pro in transaction mode. If FileMaker Pro were to get into such a state, you couldn't even quit it, because the Quit menu item is implemented with an Apple event—and FileMaker Pro won't listen to that Apple event, because it doesn't supply the transaction ID! It turns out, though, that AppleScript solves this problem transparently. If an error is encountered during a transaction block, AppleScript sends the target the Apple event that ends the transaction. I suspect that the transaction block is wrapped in a sort of invisible try block. In any case, it's really all very nicely implemented.

    [ Team LiB ] Previous Section Next Section