Using Terms From

A terms block has the following structure:

using terms from application
    -- code containing terms to be resolved
end using terms from

A terms block dictates which application's dictionary AppleScript should get the enclosed terminology from, without actually targeting that application. Terminology is resolved at compile time; therefore the application must be a literal application specifier (otherwise there's a compile-time error, "Can't make some data into the expected type"). A terms block is important only at compile time (and decompile time); it is effectively ignored at runtime.

A question immediately arises of what happens when a tell block and a terms block are nested inside one another. The short answer is that the innermost block takes precedence. Here's an example of how to screw things up:

tell application "Finder"
    using terms from application "Microsoft Entourage"
        get name of folder 1 -- error: Finder got an error: Can't get name of folder 1
    end using terms from
end tell

The problem there is that you're forcing AppleScript to resolve folder by means of Entourage's dictionary. Entourage defines folder, but using a different four-letter code from the Finder. You're still targeting the Finder, though, so when you do, you're talking to it in Entourage's language, which the Finder doesn't understand.

But if an innermost tell block would not permit AppleScript to resolve terminology, a terms block surrounding it may do so. This will compile just fine:

using terms from application "Finder"
    tell application someVariable
        get name of folder 1
    end tell
end using terms from

And it's completely equivalent to nesting the blocks the other way around:

tell application someVariable
    using terms from application "Finder"
        get name of folder 1
    end using terms from
end tell

Those examples illustrate how a terms block is typically used—it lets you target an application in a tell block without saying explicitly what application it is. Here, the application's name is expressed as a variable, someVariable; AppleScript has no idea what this will be at runtime, so it follows the instructions of the terms block and thus is able at compile time to obtain a dictionary that resolves the enclosed terminology. Successful compilation, of course, does not guarantee that the code will run. Perhaps someVariable will specify an application that understands the Finder's notion of what a folder is; perhaps not. Basically you're telling AppleScript to suspend judgment and just believe that the Finder's notion of a folder will work here.

Why would you want to do something like that? One reason would be to allow compilation of a script targeting a remote application (see "Remote Applications" in Chapter 23). You have seen that AppleScript must be able to resolve all terminology at compile time, and that normally it attempts to do this by using the dictionary of the targeted application. When an application is on another computer, this might not be possible at the time the script is compiled: the remote machine might be unavailable, the remote application might not be running, or the script might specify the target machine dynamically . A terms block lets you get past these hurdles by specifying a local source for the terminology you're using.

In this example, I'll talk to my iBook in the next room. I've turned on Remote Apple Events in the iBook's Sharing preferences. The script specifies a machine dynamically. It compiles on my iMac because I use a terms block to get the Finder's terminology from the local Finder, the Finder on my iMac. It runs because I specify a valid machine name in the dialog (and because I know the username and password for that machine):

set whatMachine to text returned of ¬
    (display dialog "Machine to connect to:" default answer "eppc://")
-- I enter eppc://duck.local and hit OK; the password dialog
appears and I fill it out correctly
tell application "Finder" of machine whatMachine
    using terms from application "Finder"
        get name of disk 1 -- "OmniumGatherum"
    end using terms from
end tell

This next example shows how applications can be targeted dynamically through references. There is no tell block anywhere in the script! The handler getThing acts as a general dispatcher; it dereferences whatever reference it is handed, and reports back the result. It has no notion of what application this involves, or even that it involves an application. AppleScript must be able to resolve all terminology at compile time, so we form our references with terms blocks.

using terms from application "Microsoft Entourage"
    set n1 to a reference to name of folder 1 of application "Microsoft Entourage"
end using terms from
using terms from application "Finder"
    set n2 to a reference to name of folder 1 of application "Finder"
end using terms from
on getThing(R)
    return (get contents of R)
end getThing
getThing(n1) -- "Inbox"
getThing(n2) --"Mannie"

A terms block can also be used to solve the puzzle of how an applet can be made to behave like a compiled script file when a targeted application might be missing. (See "Missing External Referents" in Chapter 3.) Normally, an applet attempts to locate all targeted applications at launch time. A compiled script file, on the other hand, postpones this attempt until the script is running and code targeting an application is actually encountered; if such code is never encountered, the question never arises. The problem is to get an applet to behave that way—to launch and start running even if, somewhere in its code, it targets an application that might not be present. The trick here is to refer to the target application in its tell block by means of a variable, wrapping the whole thing in a terms block so that compilation will succeed:

using terms from application "someAppThatMightBeMissing"
    set s to "someAppThatMightBeMissing"
    tell application s
        doYourThing
    end tell
end using terms from

If this code is actually encountered during the course of execution, then the application had better be present or there will be a runtime error. But the point is that at least the applet will launch! Other code might decide whether to execute this code, based on the presence of the application or some other criteria, or this code could be enclosed in a try block that will catch the error and proceed in some other way (see "Errors," later in this chapter).