AppleScript Book Afterthoughts

These are errata, desiderata, updata, and miscellaneous shmata connected with my AppleScript book.  Also, now that Leopard and Snow Leopard have been released, I’ve started feeding in some notice of AppleScript changes (there are not many).

Looking for errata from the first edition? They are here.

p. 15 and passim
In Snow Leopard, Script Editor has been renamed AppleScript Editor, and it now lives in /Applications/Utilities. It has some minor interface differences from earlier versions, but nothing that will cause any difficulty. Just supply the correct name mentally throughout the book and you’ll be fine!
Confusingly, bitness can make a difference on Snow Leopard. You should probably do a Get Info on AppleScript Editor in the Finder and check “32-bit”. Otherwise, some rather startling math errors can result. How you are supposed to enforce 32-bitness on AppleScript from other contexts is a mystery to me; this seems like a train wreck waiting to happen.
p. 18
Middle of the last paragraph before “Internally Scriptable Applications”, misspelling of “currenly” for “currently.”
p. 24
In Snow Leopard, the Script Menu is activated through AppleScript Editor.
p. 27
On AppleScript Studio in Snow Leopard, see note on p. 437. I have rewritten this example using ASOC (AppleScriptObjectiveC).
p. 64
In Leopard, when you cancel out of the dialog that asks you to locate a missing application, the script does not refuse to compile. Instead, compilation proceeds. (It might still fail because of a compilation error of some other kind, but at least it doesn’t stop merely because you canceled this dialog. Thus, the case where you cancel is now more like the case where you locate some other application.) This is a major change, perhaps one of the most significant changes in AppleScript’s general behavior since the dawn of AppleScript, making it much easier to deal with applications missing at compile time.
p. 66
See the note on p. 64; there is a parallel change here. In Leopard, when you cancel out of the dialog that asks you to locate a missing application, the script does not refuse to open. Instead, it opens but might not be compiled (and unresolved terminology is displayed in raw form). Again, this means that the case where you cancel is now more like the case where you locate some other application, and is a major improvement when dealing with a missing application.
p. 67
According to Apple’s release notes for Leopard, the problem in the “trap” paragraph here should now go away. A script will try to locate targeted applications every time the script runs or opens. Thus, a script should never get “stuck” on an incorrect application or the wrong copy of an application. I haven’t yet had occasion to test this myself.
p. 68
See the notes on p. 64 and p. 66; the problem with applets described here is also solved in Leopard. An applet simply runs, and if the applet refers to an application that can’t be found, there’s no problem unless the code referring to that application is actually executed. The workaround in Chapter 19 is therefore unnecessary in Leopard.
p. 84
See note on p. 226.
p. 88
Starting in Leopard, a single-line comment can be designated by #.
p. 118
The bug with persistence in Apple’s Script Menu documented on this page is said by Apple to be fixed in Mac OS X 10.4.6.
p. 123
There’s an extra space before the first word of the second code example.
p. 197
The version of the Finder has, of course, gone up since the book was written!
p. 197
The example with disk “main” should have been replaced with “feathers” for the second edition.
p. 202
In the comment in the next-to-last code example, the last character is the wrong font (should be bold).
p. 220
In Leopard, there are additional ways to specify an application; see the note on p. 387. Also, in Leopard, there is a new running property which states whether an application is running or not. Fetching the running, name, frontmost, id, or version property of an application in Leopard does not send an Apple event to that application and therefore, since the mere presence of a tell block targeting an application no longer launches that application, fetching one of these properties does not cause the application to launch if it is not running already.
p. 222
“AppleScript … is quite liberal in what it will accept [as a string value that can be interpreted as a date].” So liberal, in fact, that it might accept a single letter followed by punctuation followed by anything at all. For example, date "M. Neuburg" does not generate a runtime error; it generates a date, namely midnight of the first day of March of the current year. (Thanks to Ray Robertson of AppleScript Pro Sessions for pointing this out to me.)
p. 222
In Snow Leopard, the engine that parses strings into dates (in coercions and date specifiers) is replaced by ICU. This may cause some minor breakage of existing scripts, because the parser is more strict about the format of the string it’s expecting, but on the other hand it makes dates more powerful (they cover a much larger range, for instance, including non-Gregorian calendars). So, for instance, some of my example date specifiers such as date "2/25" no longer work.
p. 226
Starting in Leopard, Apple’s Script Editor (now called AppleScript Editor) has a preference allowing you to display tabs and line breaks as escaped characters in literal strings, so you can actually see what they are (as in Script Debugger).
pp. 226-31
In Leopard, the distinction between string and Unicode text goes away, and Unicode is completely integrated into AppleScript as the native text type (which is called “text”). The problem discussed on p. 229 (“you can’t type a non-MacRoman literal directly”) goes away; literal text is Unicode, and if it contains non-MacRoman characters, that’s fine. Exchange of data with files is largely unaffected; if you previously wrote out a string as a file without using “as” to specify the encoding, it was written as MacRoman, and if you now read that file as a string without using “as”, it is read as MacRoman and all is well; if, however, you previously wrote out a Unicode text as a file without using “as”, it was written as UTF-16, and you will need to specify “as Unicode text” when reading. It’s a pity that there is no simple way to read and write as UTF-8, as this is a more compact way of expressing Unicode, but you can still use the technique described at the top of p. 230.
A text object in Leopard has an id property; to learn how to use it, see the note on p. 374.
p. 227
The statement that “only the first item of the list is effective” is falsified by Snow Leopard, which for the first time in the history of the universe has made AppleScript behave the way its own documentation claims: the text item delimiters can now be a list of strings, and any element of that list will be used to split a string into text items.
p. 232
In Leopard, the first bullet point under “Alias” is no longer true: the item referred to by an alias using a literal pathname need not exist at compile time. If there’s going to be an error because the item referred to by the alias doesn’t exist, it won’t be encountered until runtime. This is a very welcome change. It was always possible to work around this behavior by assigning the pathname to a variable and using the variable to specify the alias (as in the second code example on p. 233), but this workaround is no longer necessary. It is still the case, however, that an alias must exist in order to refer to it at runtime, whereas a file need not; thus, for example, to save a new document in TextEdit, you still need to describe the pathname as a file, not an alias.
p. 233
In Leopard, a POSIX file object string specifier is no longer rewritten during compilation as a file specifier with the slashes changed to colons. However, it is still the case that a file object can’t be assigned to a variable whereas a POSIX file specifier can, and it is still the case that a POSIX file object is returned as an apparent file object, even though they are not at all the same thing. For example, the result of get POSIX file "/Users/mattleopard/Desktop/test.rtf" is still file "Hume:Users:mattleopard:Desktop:test.rtf". Fooey.
p. 249
It is no longer the case that a string other than "true" or "false" (and  "yes" or "no") can be coerced to a boolean, but I do not know when this changed.
p. 257
A better and fairly common example of how computers have trouble with numerics is a comparison like 1.8 + 0.1 = 1.9, which is false. Techniques for floating-point comparison are admirably summarized by Bruce Dawson.
p. 259
Under “mod”, it should say “number1 mod number2”, obviously. By the way, in Leopard, if number1 is an integer and number2 is a real with an integer value, you get a real. This is new behaviour; in Tiger, you got an integer. What I mean is, e.g., 5 mod 2.0 is 1 on Tiger but 1.0 on Leopard.
p. 272
The linefeed global property was introduced in Leopard.
p. 285
The behavior described in paragraph #1 of the “repeat with” explanation, where the start and end integers must be either an integer or a string or real representing an integer, was actually a bug in Tiger. The correct behavior is that these values should be coerced to an integer at runtime, and thus can be anything coercible to an integer. In Leopard, the behavior is correct.
p. 294
In Leopard, the puzzle solved in the last paragraph is no longer a puzzle - an applet behaves like a compiled script with respect to locating targeted applications - and this “using terms from” trick is no longer necessary.
p. 296
New tests suggest that in FileMaker Pro 8, transaction support is abandoned, and that it is now perhaps possible for a script to alter a database even while another script is operating on the same database in a with transaction block (in which case the transaction block is pointless). Thanks for Bill Cheeseman for pointing this out.
p. 313-4
Concerning the dialog asking where an application is, see the note on p. 64. In general, in Leopard the entire behavior of applications loading when a script is compiled has changed. In many cases (I have not yet determined the full extent of the change), merely compiling a script does not launch a targeted application. The application will launch only if the running script targets the application, and even then only if the running script actually needs to send an Apple event to the application. And even if the application is launched, it will not be activated; it launches invisibly, unless the script explicitly activates it.
p. 336
Starting in Leopard, the Script Editor (now called AppleScript Editor) has an option to show inherited properties directly; thus, for example, within the dictionary display of the “disk” class you can see the properties inherited from “container” and from “item”. You’ll need to switch on this preference (it is off by default), and if any dictionaries are open, you’ll need to close and reopen them to see the change in the display.
p. 369
The problem with path to me not working in Script Editor (now called AppleScript Editor) is said to be fixed starting in Leopard.
p. 374
In Leopard, where text is Unicode at last, the ASCII character and ASCII number scripting additions are effectively deprecated. Instead, use a text’s id property to get its numeric representation; the result is an integer or a list of integers representing the codepoints of the characters. Use “character id” or “string id” (not “text id”) with an integer or list of integers to generate a string from codepoint values. For example, string id {9786, 9785} generates a string consisting of a smiley face and a frowny face.
p. 384
A new version of DragThing fixes the speed problem to make DragThing’s execution speed for the test script comparable to that of Apple’s Script Menu.
p. 387
In Leopard, there is a new way to specify an application - by id. Thus, you would say tell application id "…". What goes after the id can be either a four-letter creator code or a bundle id (e.g. “com.apple.iTunes”). You can learn an application’s id through its id property. The id is a useful way to launch an app, since it does not require that the application’s name remain constant. Furthermore, an application reference by id will never generate the “Where is…?” dialog; if there is no such application, AppleScript will throw a runtime error, which you can catch in good order. Also, the bug mentioned on the top of p. 388 might be fixed in Leopard, but I have not tested to be sure.
p. 391
As promised in the middle of the page, the security hole of being able to send do shell script to a remote application has apparently been closed.
p. 399
Perhaps the words “and retrieved from” should be deleted, since the database file can be moved after it’s saved and so can be “retrieved” from anywhere.
p. 405
In Leopard, Automator includes a “recording” feature that generates GUI scripting code based on your actions with the mouse and keyboard. This can be a very good way to create a script that involves GUI scripting.
p. 409
I’m sorry, but this example, as written, no longer works, because the TidBITS search form has moved and the underlying HTML has completely changed. I have rewritten the example using AppleScriptObjectiveC and Ruby.
p. 412
In Leopard, osacompile is matched by a new command, osadecompile.
p. 412
In Leopard, you can now create an AppleScript Unix executable text file by starting with the shebang line #!/usr/bin/osascript. This works because # is now seen by AppleScript as starting a comment.
p. 415
See the note concerning p. 515, below.
p. 420
In Leopard, folder actions are “rearchitected,” but we are not told what the new architecture is. So we have to guess. My guess is as follows. It appears that System Events is no longer involved; System Events does not have to be running in order for folder actions to work. Instead, the system-level launchd mechanism is used. (If you have no idea what I’m talking about, this page will give you a clue.) If you create a folder action and set it up to operate, you’ll see that your ~/Library/LaunchAgents directory now contains two plist files. One of them simply makes sure that a scriptable background application called Folder Actions Dispatcher is running. (Folder Actions Dispatcher lives in CoreServices, and you can read its dictionary.) The other specifies what folder is being watched; this, I assume, requires no polling because the FSEvents mechanism is used. When the folder is modified, an osascript command runs, telling Folder Actions Dispatcher to tick. Now Folder Actions Dispatched does poll - once - to see what has happened to the folders it’s watching. If there has been an appropriate change, it tells another scriptable background application, AppleScript Runner, to run the appropriate folder action script. This mechanism seems ingenious, but it does not appear to be entirely robust; in my very first test, it misbehaved slightly: I had set up a folder to run a script when a file was added to that folder, and this worked, but when I removed the file from the folder, the script ran again, which was not supposed to happen. Still, I look forward to seeing how things shake out.
In Snow Leopard, Folder Actions Setup is moved to /System/Library/CoreServices, and the main way to access it is through a service (which appears as a contextual menu item in the Finder).
p. 421
Wherever the book talks about the application called Configure Folder Actions, it means Folder Actions Setup. This kind of mistake cannot be sheer accident; perhaps Configure Folder Actions was the name way back in Jaguar, then Apple later changed it and I failed to notice (since this error is present in both editions of the book). It beats me.
p. 437
Broadly speaking, AppleScript Studio has been the victim of increasing abandonment (or decreasing attention) from Apple since the day of its birth, until finally in Snow Leopard it is officially deprecated and “obsoleted” (i.e. replaced) by the AppleScriptObjC bridge (ASOC). (In fact, the Inspector palette used in Interface Builder to configure AppleScript Studio projects is missing; to restore it, say defaults write com.apple.InterfaceBuilder3 IBEnableAppleScriptStudioSupport -bool YES at the command line.) The AppleScriptObjC bridge gives you access to all of Cocoa using AppleScript as your basic language, kind of as if you were using AppleScript Studio with call method turned on all the time. It beats me why anyone would want to do this (but then, it used to beat me why anyone would use AppleScript Studio). But there’s no denying that having a full bridge like this is powerful. To demonstrate its power, I’ve rewritten the example developed here, using ASOC.
pp. 442-51
See the notes on p. 409 and p. 437. The example is very cute but it no longer actually works as written, plus AppleScript Studio is a dead letter; I have rewritten the example using ASOC and Ruby.
pp. 445ff
Please pretend that this and other screen shots of Xcode and Interface Builder are brought up to date for Leopard.
pp. 472ff
See the note on p. 442. This is example is no longer needed, because AppleScript Studio is obsoleted by ASOC (AppleScriptObjectiveC), where scriptability is much easier - basically, you just do Cocoa Scripting, as I described on pp. 459ff. My new SearchTidBITS example, rewritten in ASOC, demonstrates how simple it can be.
pp. 508-15
Leopard has introduced a new mechanism for sending Apple events using Objective-C, the Cocoa Scripting Bridge. It works very much like my description of the Perl mechanism in this appendix: ahead of time, the target application’s dictionary is parsed and Cocoa glue files are generated; subsequently, your Cocoa code can speak Objective-C to the glue classes, which do the heavy lifting of translating Objective-C into raw Apple events so you don’t have to. Thus, for instance, the Cocoa equivalent of the p. 508 example script would be… Holy cats, you can’t get it to work! Yes, believe it or not, after all this fanfare, Apple has come up with a framework that can’t even generate the Apple event equivalent of make new document. You can see me asking for confirmation of this fact here; Chris Nebel’s confirmation is here; and my response, which is the sort of thing I’d be saying in the book if I were writing it today, is here.
p. 509
Last sentence of last paragraph, “issue” should be “issues”, obviously.
p. 510
In the code at the top of the page, the phrase bytes:"all " is wrong because it is not platform-safe: it breaks on an Intel machine. The solution is to insert a preceding line:
DescType all = kAEAll;
and then change bytes:"all " to bytes:&all. (Thanks to Olof Hellman for explaining to me how to fix this.)
p. 515
Add (mentally) a new subsection to Appendix B, entitled Ruby. In October, 2006, Hamish Sanderson ported Appscript’s underpinnings to Ruby, my favorite language. The Ruby version is available here. Installation is simple (instructions appear in the README file). Here’s the Ruby code for generating our model events:
require 'appscript'
bb = AS.app 'TextEdit'
bb.make :new => :document
bb.documents[1].text.set 'Hello, world!'
I thought it would be really interesting to adapt the example from p. 415 to use Appscript. The original example shows how to call AppleScript from Ruby: having done some Ruby-like calculations involving Regexp and Hash and other good Ruby stuff, we construct a rather lengthy bit of text and then use osascript to compile and execute it as AppleScript. The Appscript version, however, just constructs and sends all the requisite Apple events directly. It’s wonderfully fast and is actually a lot easier to read than the AppleScript version. Plus at the time I wrote the original, my knowledge of Ruby was very primitive, so I’ve taken the opportunity to spruce up the opening as well. Here’s a new version:
#!/usr/bin/env ruby
require 'appscript'
h = Hash.new(0)
open(ARGV[0]).each do |line|
  line.scan(/\w+[-']*\w*/) do |w| # allow internal punctuation
    h[w.downcase] += 1
  end
end
h = h.sort { |x,y| y[1]<=>x[1] }.to_a[0..29].reverse
excel = AS.app('Microsoft Excel')
excel.activate
excel.make(:new => :workbook)
w1 = excel.worksheets[1]
h.each_with_index do |(k, v), i|
  i += 1 # AppleScript is usually 1-based
  w1.rows[i].cells[1].value.set(k)
  w1.rows[i].cells[2].value.set(v)
end
excel.ranges["A1:B30"].select
excel.make(:new => :chart_sheet, :at => excel.active_workbook.start)
c = excel.active_chart
c.chart_type.set(:bar_clustered)
c.has_title.set(1)
c.chart_title.caption.set("30 Most Frequent Words")
c.has_legend.set(0)
c.apply_data_labels(:type_ => :data_labels_show_label, :legend_key => 0)
[:category_axis, :series_axis, :value_axis].each do |axt|
  [:primary_axis, :secondary_axis].each do |ax|
    begin
      x = c.get_axis(:axis_type => axt, :which_axis => ax)
      x.has_major_gridlines.set(0)
      x.has_minor_gridlines.set(0)
      x.has_title.set(0)
      c.set_has_axis(:axis_type => axt, :axis_group => ax, :axis_exists => 0)
    rescue
      # puts $! # debugging, make sure exceptions are as expected
    end
  end
end
For more information, please see the online article and the online book I wrote about rb-appscript.

Back to Matt Neuburg’s Home Page

This page prepared August 15, 2012 by Matt Neuburg, phd = matt at tidbits dot com, using RubyFrontier. RubyFrontier is a port, written in the Ruby language, of the Web-site-creation features of UserLand Frontier. Works just like Frontier, but written in Ruby!
Download RubyFrontier from GitHub.