In 1992-93, the founders of AppleScript had to decide where the language should live. They could have made AppleScript the internal language of a single application, like HyperCard’s HyperTalk; the user would then compile and run AppleScript code entirely from within this one application. But this approach was unacceptable. Rather, they wanted AppleScript to be available everywhere. Thus the language would have to be part of the System. In creating a place within the System to put it, they generalized this place to be somewhere that not only AppleScript but any scripting language could live in. The resulting structure is the Open Scripting Architecture (OSA).
Under the OSA, a scripting language is implemented by a something called a component. (Components were not invented specially for the OSA; they existed already in connection with QuickTime.) Think of a component as a piece of self-contained functionality made available at system level so that any program can hook up to it and use it. One thing that’s special about components is that they can be installed and uninstalled dynamically. So an OSA-savvy program doesn’t necessarily think in terms of any particular scripting language; it asks the System—in particular, the Component Manager—what scripting languages are presently installed, and if it wants to use one, the Component Manager provides access to it.
Since components are installed
dynamically, this installation must actually take place while the
computer is running. AppleScript is installed as the computer starts
up and simply left in place, so that it’s always
available. You may recall that under Mac OS 9 there was an extension
called AppleScript (in the Extensions folder of the System Folder).
Its job was to install AppleScript as a component under the OSA as
the computer started up. On Mac OS X, the same function is performed
by AppleScript.component
, which is in
/System/Library/Components
; this type of file is
called a component file.
A nice consequence of this architecture is that Apple can easily release upgrades to AppleScript, and the user can easily install them, with no effect on any other part of the System. AppleScript itself has a version number, which refers to the version number of the component that is installed to implement it; you can find out what this is by running the following one-word script in the Script Editor:
version
At the time of this writing, the result is "1.9.2
“.
Components are special in another way, too: when a program is given access to a component by the Component Manager, it gets, in effect, its own private copy of that component, with its own persistent storage. This means, among other things, that multiple programs can use a component without interfering with one another. This has important consequences for how the AppleScript language behaves, and we’ll come back to it.
The Open Scripting Architecture is meant to accommodate scripting languages in general; but AppleScript is the only one supplied by Apple. In fact, AppleScript is designated the default scripting component, the one that is used when no particular scripting component is specified. Still, there can be other scripting languages. So where are they?
There have never been many other OSA scripting languages, perhaps because developers have not felt much need to supply them. I know of just four:
UserLand Frontier (under Mac OS 9 and before) installed its internal scripting language, UserTalk, as an OSA component dynamically whenever Frontier was running.
CE Software’s QuicKeys (under Mac OS 9 and before) installed its scripting language, QuicKeys Script, as an OSA component at startup, by means of an extension.
Late Night Software’s JavaScriptOSA installs JavaScript as a system-wide scripting language at startup, by means of a component file.
Late Night Software’s Script Debugger installs a debuggable version of AppleScript, called AppleScript Debugger, at startup, by means of a component file.
JavaScriptOSA is free, so you might like to look into it. A cool feature of it is that it adds classes to the JavaScript language that allow Apple events to be expressed. Thus it can be used where you would use AppleScript, and for the same purposes. JavaScript has some nice linguistic features (such as powerful string handling and object orientation), so it makes an interesting alternative to AppleScript. To learn more, see http://latenightsw.com/freeware/JavaScriptOSA/index.html.
If you were to download JavaScriptOSA and copy it to
/Library/Components
and then log out and log in,
JavaScript would be present as a scripting language on your machine.
You would observe this in the Script Editor, where
“JavaScript” would appear in the
scripting languages popup at the top of the window. You would be able
to write, compile, and run scripts written in JavaScript from within
the Script Editor. This illustrates the dynamic and generalized
nature of the Open Scripting Architecture.
There are two approaches that a program can take when it wants to gain access to a scripting component. An OSA-savvy program like the Script Editor wants to be able to access any scripting component at all, indiscriminately. For this purpose, the OSA supplies a special component called the Generic Scripting Component (GSC). The program asks the Component Manager to let it talk to the GSC, and after that the GSC routes communications between the program and the appropriate scripting component such as AppleScript. Alternatively, a program might ask the Component Manager for direct access to one particular scripting component; such a program would not implement OSA scripting in a general way, but rather would be accessing just that one scripting language. Either way, once a program is in communication with the appropriate scripting component, the program can do scripting in that scripting language.
Now comes the really interesting part. The program itself doesn’t do any of the work, and doesn’t need to have any knowledge of the scripting language; that’s the job of the component. For example, earlier we said, in the Terminal:
$ osascript -e 'tell app "Finder" to get disks'
The phrase 'tell
app
"Finder
" to
get
disks
' is an AppleScript
expression; and when we gave this command in the Terminal, it was
obeyed—references to all mounted volumes were displayed in the
Terminal. But the Terminal doesn’t know AppleScript.
The shell, to which we’re talking in the Terminal,
doesn’t know AppleScript. And the
osascript
program, which we call from the shell,
doesn’t know AppleScript either. So who does know
it? The AppleScript scripting component, of course.
Figure 4-2 diagrams a typical chain of events by which a program turns text into a runnable script, runs it, and is able to display the result, under the OSA.
The program asks the Component Manager to put it in touch with the scripting component.
The program obtains some text and hands it to the scripting component with instructions to compile and run it. If any of the expressions in this script are equivalents of Apple events, those Apple events will be generated and sent, and we will then be in the world of Figure 4-1.
The program asks the scripting component for the result as text; the scripting component complies.
Figure 4-2 is how a script-editing application such as the Script Editor works. The Script Editor does not know any AppleScript. It merely serves as a front end to the AppleScript scripting component, where all the work of compiling and running scripts actually takes place. (There are some additional details having to do with how scripts are compiled, saved, and decompiled, and we will get to them in the course of this chapter.)
Step 3 in the discussion of Figure 4-2 contains a very remarkable statement: the program “asks the scripting component for the result.” The startling implication is that the scripting component has a memory! We say that components maintain state. In fact, the component to which a program gets a connection is like an instance in the world of object-oriented programming: state is maintained individually for each connection. This is one of the things that makes components special.
Here’s an analogy to help you visualize what’s going on. Imagine the AppleScript scripting component as a kind of little universe, a universe where the knowledge of AppleScript resides. And imagine that this universe can make copies of itself. When a program asks the Component Manager for access to the AppleScript scripting component, as at the top of Figure 4-2, it isn’t simply given a telephone line to the one master copy of the AppleScript universe sitting in the System; instead, it’s as if the Component Manager makes a copy of the AppleScript universe and gives the program a telephone line to that copy. The program now has its own private AppleScript universe. This private copy of the AppleScript universe is technically an instance of the AppleScript scripting component.
The AppleScript scripting component instance can maintain state without getting confused at some global level, because it isn’t operating at a global level. It’s operating at a local level—local to the program that summoned it. Suppose we have two different programs, each of which gets a connection to the AppleScript scripting component and asks it to compile and execute a script. The AppleScript component does not get all flustered and say, “Oh, gosh, two different programs are trying to get me to do two different things at once!” Rather, there are in effect at that moment two different AppleScript component instances, the one that the first program is talking to and the one that the second program is talking to. Each program asks its own instance of the AppleScript component to compile and execute its script, and there is no conflict or confusion at all.
So each instance of the AppleScript scripting component maintains state. In terms of our analogy, each AppleScript universe remembers what goes on in it. Thus a program is able to return again and again to its same AppleScript universe and refer back to things that happened earlier. Thus, in the middle section of Figure 4-2, let’s introduce a pause. Let’s say the program hands the text to the AppleScript component and asks it just to compile it. The AppleScript component succeeds, reports this success, and stops. Now let some time pass. Then the program comes back to the AppleScript component and says: “Say, remember that script I had you compile a little while ago? Now I’d like you to run it.” The AppleScript component can do this. The program does not hand the compiled version of the script over to the AppleScript component to be run; the program doesn’t have the compiled version of the script. The AppleScript component still has it. When it compiles a script, the AppleScript component remembers the compiled version. Thus, when the program comes back and asks that the compiled script be run, there is no overhead of handing across a lot of compiled code, and the AppleScript component is ready to rock and roll (as a computer scientist would say) with no further ado.
The internal memory of an AppleScript scripting component instance will not persist forever. The lifetime of one of these instances can be no longer than that of the application that summoned it. When that application quits, any of these little component universes that it may have created must also fade away, and all the stuff that a component instance has been remembering trickles away like the air escaping from a deflating balloon.
So if a program asks the AppleScript scripting component to compile a script and then wants the compiled version to persist somehow after the program quits, it must take special steps to obtain the compiled version from the AppleScript component and save it in some way. This in fact is just what a program such as the Script Editor does when you save a compiled script. I’ll talk more about that later in this chapter.
Get AppleScript: The Definitive Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.