22.5. Context
The context or environment from which a script is executed can make a huge difference to its speed. This is in large measure because Apple events are expensive. In particular, what makes them expensive is the context switch involved in communicating between one application and another. Thus it is typically
(But also, there are some contexts that are
Here's a test script:
set x to (get current date)
repeat 500 times
tell application "iTunes"
get name of it
end tell
end repeat
set y to (get current date)
set z to (y - x)
tell application "Finder"
activate
display dialog z
end tell
Table 22-1 shows some rough timings on my machine for running that script from within various contexts. Table 22-1. Timings for the same script executed in different contexts
Many external factors affect speed , so no absolute lessons can be drawn from a simple, unscientific test such as this; but clearly it can be worthwhile to consider the context in which a script will run when evaluating its speed. |
Chapter 23. Scriptable Applications
AppleScript's chief purpose is to let you communicate with scriptable applications. How you target a scriptable application using AppleScript depends on whether the application is
local
(run on the same computer and by the same
This book won't teach you how to script any particular application (see "The Scope of This Book" in the Preface). If the application comes with documentation or examples showing how to script it, start with that. For certain applications, there may be third-party books or web pages devoted to the topic of scripting it. The application will in any case have a dictionary (see Chapter 20). |
23.1. Targeting Scriptable Applications
To
target
a scriptable application is to aim Apple events at it, like arrows. The principal linguistic device for targeting an application in AppleScript is the tell block containing an application
A reference to an object
(See "Missing External Referents" in Chapter 3; "Target" in Chapter 11; Chapter 12; "Application" in Chapter 13; "Tell" and "Using Terms From" in Chapter 19; and "Resolution of Terminology" in Chapter 20.) 23.1.1. Local Applications
A
local
application is an application on the same computer and under the same
Specifying an application's name can be trickier than you might suppose. Back in the pre-Mac OS X days, typing the name of an application could be a maddening and
With the coming of Mac OS X, things got even
Script editor applications now
Specifying a full pathname should not usually be necessary, but can sometimes be useful; for example, it could be a way to distinguish two versions of the same application on your machine . Unfortunately, this trick doesn't work if one version is already running. For example, I have FileMaker Pro version 7 and FileMaker Pro version 5.5. This script is
tell application "gromit:Users:matt2: 23.1.2. Remote ApplicationsA remote application is an application running on a different computer from the script, or on the same computer but under a different user already logged in (through Fast User Switching ). Communication is performed over IP (not AppleTalk , as in the past); this has the advantage that it works over the Internet. Thanks to Bonjour (formerly called Rendezvous), a machine on the local network can be specified by name. On the target computer, Remote Apple Events must first be turned on; this can be done in the Sharing preference pane.
To target a remote application, you have to specify the machine on which it is running. To do so, you use an eppc URL . You can learn a lot more about the format of this URLindeed, you can easily create such a URL, suitable for targeting a remote applicationthrough the
choose remote application
command (Chapter 21). For example, if I use
choose remote application
to select the Finder on my iBook in the
application "Finder" of machine "eppc://duck.local/?uid=501&pid=179"
The application specifier is followed by a machine specifier that uses an eppc URL with my iBook's Bonjour name as the first
So, here are some ways of targeting the Finder on my iBook: tell application "Finder" of machine "eppc://duck.local" -- puts up the username/password dialog get name of every window end tell tell application "Finder" of machine "eppc://mattneub:teehee@duck.local" -- avoids the username/password dialog get name of every window end tell set s to "eppc://mattneub:teehee@duck.local/Finder" -- incorporates machine specifier into application specifier tell application s get name of window 1 end tell set s to "eppc://mattneub:teehee@192.168.0.4/Finder" -- uses IP number instead of Bonjour name tell application s get name of window 1 end tell set s to "eppc://mattneub:teehee@duck.local/?uid=501&pid=179" -- uses pid instead of application name tell application s get name of window 1 end tell If you use a variable rather than a literal to specify the target, a terms block referring to a local application may be needed to resolve terminology at compile time (see "Using Terms From" in Chapter 19). It wasn't needed in the previous examples, because name and window are defined in AppleScript itself (see "The 'aeut' Resource" in Chapter 20). If possible, it's probably a good idea to use a terms block in any case, because it is much faster to get the dictionary from a local copy of an application than to get it across a network, and besides, you probably don't want to bother forming the connection to the remote machine every time you compile the script. So, for example:
set s to "eppc://mattneub:teehee@duck.local/Finder?uid=501" using terms from application "Finder" tell application s get name of every disk --
{"OmniumGatherum", "Network", "SecretSharer", "Puma"}
end tell end using terms from
Some scripting addition commands you call while targeting a remote application are run on the remote machine. For example: tell application "Finder" of machine "eppc://duck.local" say "Watson, come here, I want you." end tell Starting in Tiger, however, scripting addition commands that put up a user interface are disabled from operating remotely; this makes sense because there might be no one at the remote machine to dismiss a dialog: tell application "Finder" of machine "eppc://duck.local" display dialog "Watson, come here, I want you." -- error: Finder got an error: No user interaction allowed end tell The scripting addition commands store script and run script are disabled, probably as a security measure (but load script works). Passing object references back and forth between machines can work, because the reference is usually endowed with a machine specifier as well as an application specifier. But aliases can't usually be handed between one machine and another, because they are resolved on the wrong machine; instead, try to obtain a pathname string. You cannot target a remote machine merely; you must target some application on that machine. This raises the question of how to launch a remote application in order to target it. You cannot simply target the application by name as a way of launching it, because a literal application specifier will be resolved on the local machine. A remote application must thus already be running before you target it. Accordingly, you must always start with some application you know is running; likely possibilities are Finder and Dock. Then you can worry about whether the application you want to target is running. Here's how to find out:
set s to "eppc://mattneub:teehee@duck.local/Finder?uid=501" using terms from application "Finder" tell application s get name of every process --
{"
In theory you shouldn't be able to talk like that, because the Finder no longer handles the process classSystem Events does (see "System Events," later in this chapter). However, the process class is grandfathered into the Finder, and as a bonus, it is implemented by routing the Apple event to System Events, so not only do you get the answer, you also cause System Events to launch, which is good because you might want to target it. This still doesn't answer the more general question of how to launch an application remotely. If you know its full pathname, then you can just tell the Finder to open it: tell application "Finder" of machine "eppc://duck.local" open item "OmniumGatherum:Applications:Utilities:Terminal.app" end tell
Otherwise, the official solution is to ask the Finder to open the application file using its ID , which can be a
set m to "eppc://mattneub:teehee@duck.local" tell application "Finder" of machine m using terms from application "Finder" open application file id "com.
(It is also possible to launch an application remotely using
do shell script
and
If multiple users are logged into the remote computer
set L to {} repeat with i from 501 to 520 set m to "eppc://mattneub:teehee@duck.local/?uid=" & (i as string) using terms from application "Finder" try tell application "Finder" of machine m get (desktop as string) set end of L to i end tell end try end using terms from end repeat L --
{501, 502}
Once we know the uid numbers of the logged-in users, we can target the applications run by a specific user. Observe that the username and password is still the username and password for the machine as a whole, not the user:
on test(m) tell application "Finder" of machine m set n to (get name of window 1) end tell display dialog n end test test("eppc://mattneub:teehee@duck.local/?uid=501") --
mattneub
test("eppc://mattneub:teehee@duck.local/?uid=502") --
guest
The identical syntax allows you to target a different user logged into the local machine. In other words, if a machine has multiple users logged in, you can run a script under one user and target an application under another user. You can describe the present machine as localhost . Don't forget to turn on Remote Apple Events on your own machine! tell application "Finder" to get name of window 1 -- mattneub set m to "eppc://mattneub:teehee@localhost/?uid=502" tell application "Finder" of machine m get name of window 1 -- mrclean end tell 23.1.3. XML-RPC and SOAP
XML-RPC and SOAP are
web services
. This means they are ways for one computer (the client) to query or command another computer (the server) over the Internet. The server defines certain commands to which it is prepared to respond. The client somehow knows what these are, and issues one of the commands with appropriate parameters; it also
XML-RPC and SOAP do not
The web server here need not in fact be elsewhere on the Internet. It could be on your local network, even on the same machine. It isn't difficult to set up your own web server to function as an XML-RPC or SOAP server ; there are standard modules for doing this with Perl or PHP, for example. This means you can use AppleScript to communicate with a Perl or PHP script. I know someone who does this as a way of storing certain email messages in a MySQL database . PHP has a good interface to MySQL, so it makes a good intermediary. His email client is scriptable; when he receives an email message that he wants to store, he runs a script that captures the information from the email message and sends a SOAP message containing it to the web server running on the same machine; the web server processes the SOAP message through PHP, which stores the email message in the MySQL database. (For a similar architecture, with AppleScript speaking to Perl instead of PHP, see http://developer.apple.com/internet/applescript/applescripttoperl.html.) AppleScript targets XML-RPC and SOAP services through support built into the Apple Event Manager. Your AppleScript command is translated into an Apple event, which in turn is translated into XML. The XML is shoved into the post argument of an HTTP request, and the request is sent across the Internet. (Your script waits synchronously for a reply.) When the reply comes back, the XML is extracted from the reply and interpreted as AppleScript data (usually a record)and that's the result of your command. You specify the server in a tell block, but instead of saying the name of an application, you provide a URL string. Within the tell block, you use one of two commands: either the call xmlrpc command or the call soap command. (These terms are resolved only when the target is an http URL; otherwise, they are illegal.) The target can be expressed in this form: tell application "http://www.xxx.com/someApplication" or you can say this, which decompiles to the previous syntax: tell application "someApplication" of machine "http://www.xxx.com" The syntax for call xmlrpc is as follows:
call xmlrpc {method name:
methodName
, parameters:
parameterList
}
The syntax for call soap is as follows:
call soap {method name:
methodName
,
method namespace uri: uri ,
parameters: parameterRecord ,
SOAPAction: action }
You can omit an item of the parameter record (such as
method namespace uri
or
parameters
) if it isn't
Now let's test these commands. There's a copy of UserLand Frontier on the Internet that is intended for users to test with XML-RPC and SOAP
tell application "http://superhonker.userland.com/rpc2" call xmlrpc
{method name:"examples.getStateName",
parameters:30} --
"New Jersey"
end tell
Frontier is also a SOAP server. We can call the SOAP equivalent of the same verb using AppleScript, as follows: tell application "http://superhonker.userland.com" call soap
{method name:"getStateName",
SOAPAction:"/examples",
parameters:{statenum:30}} --
"New Jersey"
end tell
If you happen to have a copy of Frontier or Radio UserLand, you can test all this on your own machine, without using the Internet. These programs run the very same server on port 8080. So with Frontier or Radio UserLand running, you can substitute "http://localhost:8080/rpc2" and "http://localhost:8080" as the application URLs for the tell block.
If you tell AppleScript to look for a dictionary in an application that is a web URL, AppleScript won't actually look there, but will assume that this application is a SOAP or XML-RPC server. We can use this as a trick to treat the target as a variable when doing a SOAP call over the Internet. This example, based on a script distributed by Apple, shows how to write a general SOAP-calling handler. The handler
generalSOAP
contains no hard-coded information at all, except the application URL named in the terms blockbut this URL is a fake, merely to
on generalSOAP(u, m, s, a, p) using terms from application "http://www.apple.com/placebo" tell application u return call soap
{method name:m,
method namespace uri:s,
parameters:p,
SOAPAction:a} end tell end using terms from end generalSOAP generalSOAP("http://services.xmethods.net:80/soap",
"getQuote", "urn:xmethods-delayed-quotes",
"", {Symbol:"AAPL"}) --
74.93
For another example of call soap , see "Combining Specialties" in Chapter 1. In general, call xmlrpc and call soap are not difficult to use, but you'll have to study the documentation for the service you're trying to call, and it may take a little trial and error to get the parameters just right.
|