Section 8.6. Compiled Script Files as Script Objects


8.6. Compiled Script Files as Script Objects

A script can read a compiled script file and incorporate its contents as a script object. Similarly, a script can save a script object out to disk as a compiled script file. You might use this mechanism as a means of implementing file-level persistence, or to build a separate library of commonly needed code that all scripts can share.

The mechanism depends upon three verbs. They're not part of AppleScript proper, but are implemented in a scripting addition (Chapter 3) that's standard on all machines.

load script

Syntax

 load script aliasOrFile 

Description

Returns the top-level script of the compiled script file or applet aliasOrFile as a script object.

Example

 set myScript to load script alias "myDisk:myFile.scpt" 

run script

Syntax

 run script aliasOrFile [with parameters list] 

Description

Tells the top-level script of the compiled script file, applet, or text file aliasOrFile to run (compiling first if necessary), optionally handing it the list as the parameters for its explicit run handler, and returns the result.

Example

 run script alias "myDisk:myFile.scpt" 

store script

Syntax

 store script scriptObject [in aliasOrFile [replacing yes|no]] 

Description

Saves scriptObject to disk as a compiled script file or applet. Returns no value. If no further parameters are supplied, presents a Save File dialog; if the user cancels, a runtime error is raised. If aliasOrFile is supplied, the Save File dialog is suppressed, but if the file exists already, presents a dialog asking how to proceed; if the user cancels, a runtime error is raised. If replacing is supplied, this dialog is suppressed; if yes, the file is just saved, and if no, an error is raised if the file exists. The filename extension determines the format of the resulting file: .scpt (or nothing) for a compiled script file, .scptd for a script bundle, .app for an applet. (The applet will be an applet bundle unless a nonbundle applet already exists.)

Example

 store script sayHello in file "myDisk:myFile.scpt" replacing yes 

(On aliases and file specifiers and the differences between them, see Chapter 13. The verb run script, instead of a file, can take a string, and it then functions as a kind of second level of evaluation; see Chapter 19.)

When you save a script object with store script, the lines delimiting the script object definition block (if any) are not included. This fact makes sense, since those lines were never part of the actual script object to begin with. So, for example:

 script sayHello     display dialog "Hello" end script store script sayHello in file "myDisk:myFile.scpt" replacing yes 

What is saved in myFile.scpt is the single line:

 display dialog "Hello"

8.6.1. Data Storage

Recall from earlier in this chapter that top-level script object entities are persistent, but that at the level of a file on disk, this persistence is unreliable, because it depends upon the environment where the script runs. For example, Entourage's Script menu doesn't save a compiled script file back to disk after execution, so top-level entities don't persist between executions. We can get around this uncertainty by storing needed data ourselves in a separate compiled script file. A script object that we save with store script is saved along with the current values of its top-level entities. Thus we can guarantee file-level persistence by saving a script object as a compiled script file separately from our main script.

If you load a script as a script object with the load script command, and top-level entity values within this script object change, and you wish to write these changes back to disk, it is up to you do so explicitly, with store script.

The run script command does not save its script, so any changes in the script's top-level entity values do not persist.


Here's an example where we display the user's favorite color. This information will be stored persistently in a file on the desktop called myPrefs.scpt. (The path to command is discussed in Chapter 21.) First we try to load this file. If we succeed, fine; if we fail, we ask for the user's favorite color and store it in myPrefs.scpt. Either way, we now know and can display the user's favorite color. If the user doesn't move myPrefs.scpt, the next time the script runs there will be no need to ask for the user's favorite color; we will already know it.

 set thePath to (path to desktop as string) & "myPrefs.scpt" script myPrefs     property favoriteColor : "" end script try     set myPrefs to load script file thePath on error     set favoriteColor of myPrefs to text returned of ¬         (display dialog "Favorite Color:" default answer ¬             "" buttons {"OK"} default button "OK")     store script myPrefs in file thePath replacing yes end try display dialog "Your favorite color is " & favoriteColor of myPrefs 

You might object that a file sitting on the user's desktop is a silly place to store our data. You're perfectly right; it's only an example! How might we solve this problem in real life? We need a place to put the data where the user won't see it or object to it. One solution, if it is supported by the environment where the script will run, would be to make this script a script bundle and store the secondary script inside the bundle. A script bundle looks like a file, so the secondary script is out of sight inside it. The first line of the script could then be something like this:

 set thePath to (path to me as string) & "Contents:Resources:myPrefs.scpt" 

Unfortunately, a script runner environment that doesn't implement persistence might not know about script bundles either. Perhaps a better place might be the user's Preferences folder:

 set thePath to (path to preferences as string) & "myPrefs.scpt" 

If you open the file myPrefs.scpt in a script editor application, you may be surprised to find that it doesn't actually seem to contain your favorite color:

 property favoriteColor : "" 

Don't worry! The decompiled version of the script, which is what you're seeing, shows the original bytecode, not the persistent data stored internally with the script. But the persistent data is there. (The only way I know of to get a look at the persistent data stored internally with a script is to use Script Debugger. Script Editor doesn't show it, and destroys it if you open and run the script directly.)

8.6.2. Library

A compiled script file may be used to hold commonly needed routines. A running script can access the contents of the script file using load script. The script file's top-level entities, including its run handler, are then available to the running script that loads it. A compiled script file used in this way is called a library .

For example, AppleScript has no native command to remove one item from a list (that is, to return a list without its nth item). Let's write one. Ooooh, we've already done it; see "LISP-likeness" in Chapter 4. Save that script (the AppleScript version, not the LISP version); for now, let's save it as library.scpt on the desktop. Here's how to use the library:

 -- load the library... set f to (path to desktop as string) & "library.scpt" set lib to load script alias f -- . . . and use it set L to {"Mannie", "Moe", "Jack"} set L2 to lib's remvix(2, L) L2 -- {"Mannie", "Jack"} 

What are the advantages and disadvantages of using a library ? An obvious advantage is that it makes code reusable and maintainable. Here, remvix is a useful handler. We don't want to have to keep copying and pasting it into different scripts. If its code lives in a library, it becomes accessible to any script we write. Furthermore, if we improve remvix in its library file, those improvements are accessible to any script; a script that already calls remvix by way of load script acquires any new improvements the next time it runs. Finally, a library may consist of many interrelated handlers that call each other (and may even share values by way of top-level properties). When you load the library, you load that entire functionality and the handlers work properly without your having to worry about the complexities of dependency.

A disadvantage is that a library reduces portability. We cannot just copy a script that calls remvix to another machine, or send it to a friend, because it depends on another file, library.scpt, and refers to it by a pathname that won't work on any other machine.

With Script Debugger, a trick for working around this problem is to load any library files as part of your script property initialization:

 -- load the library... property f : (path to desktop as string) & "library.scpt" property lib : load script alias f -- . . . and use it set L to {"Mannie", "Moe", "Jack"} set L2 to lib's remvix(2, L) L2 -- {"Mannie", "Jack"} 

This works because, as explained earlier in this chapter, Script Debugger saves top-level entities into the compiled script file. So when you run this script and then save it as a compiled script file with Script Debugger, the property lib is saved in its initialized state, meaning that it contains the script object loaded from disk. The resulting compiled script file thus contains the library that it uses, and will run correctly in a script runner on another machine. In fact, this compiled script file can even be opened and executed using Script Debugger on another machine.

However, if you open this script with Script Editor, the script is ruined, because Script Editor strips out the saved top-level entities when it opens a file. Furthermore, if you edit this script on another machine, the script is ruined. At that point, the values of the script properties will be thrown away, AppleScript will try to reinitialize them, the load script command will fail because the file it refers to doesn't exist, and the script will no longer compile or run. Thus, this trick is probably most appropriate when you can afford to distribute a script as run-only.

(Actually, Script Debugger helps you further in this situation as well. It lets you "flatten" a script so that it incorporates all library files on which it depends, and so has no load script dependencies.)

An interesting attempt to rationalize the library mechanism is the freeware utility Loader . The idea is to try to make it as easy to install and take advantage of libraries, even if they involve mutual dependencies, as in languages like Perl and Python. I haven't tried this myself, but it looks intriguing.




AppleScript. The Definitive Guide
AppleScript: The Definitive Guide, 2nd Edition
ISBN: 0596102119
EAN: 2147483647
Year: 2006
Pages: 267
Authors: Matt Neuburg

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net