You’re looking at a draft of a chapter from a work in progress, tentatively titled Scripting Mac Applications With Ruby: An AppleScript Alternative, by Matt Neuburg.
Covers rb-appscript 0.6.1. Last revised Jun 23, 2012. All content ©2012 by the author, all rights reserved.
Chapter 9: Scripting Additions
1. Calling a Scripting Addition Command
2. Viewing a Scripting Addition’s Dictionary
As explained in Chapter 3, a scripting addition (or osax, plural osaxen) is a resource containing code and a dictionary. A scripting addition’s dictionary defines primarily commands. Mac OS X comes with one very commonly used scripting addition, StandardAdditions.osax (located in /System/Library/ScriptingAdditions/), and many third-party scripting additions are available. (To install one, place it in a ScriptingAdditions directory, typically in your user ~/Library.)
Most scripting addition commands are intended to compensate for the paucity of the AppleScript language, adding rudimentary abilities to handle files, strings, numbers, and dates that are missing from AppleScript itself. One important reason for using Ruby is that it is amply supplied with abilities and libraries that AppleScript lacks, and therefore you are unlikely to need this kind of scripting addition command. You are more likely to use scripting additions that implement some sort of user interaction or feedback. The StandardAdditions.osax commands let you put up dialogs where the user can do such things as specify a folder, choose from a list, or be shown a message; you can also generate the system alert (beep) sound or make the computer speak text.
In rb-appscript, unlike AppleScript, scripting additions are not loaded automatically, and neither is the rb-appscript front-end module by which you access scripting addition commands. If you intend to use a scripting addition in your code, you must explicitly load the file osax.rb:
require 'osax' # might have to require 'rubygems' first
This brings the OSAX
module into existence. To discover what scripting addition files are installed, call the scripting_additions
class method:
require 'osax'
p OSAX.scripting_additions
#=> ["Adobe Unit Types", "Digital Hub Scripting", "StandardAdditions"]
To use a scripting addition command, first generate an OSAX::ScriptingAddition
instance with new
, supplying the name of the scripting addition file containing the desired command. The name is case-insensitive, and you should not include the .osax file extension (it will be stripped if present).
require 'osax'
sa = OSAX::ScriptingAddition.new("standardadditions")
Note: If you’re running on Snow Leopard, you may get an error when you try to run that script, warning that “OSAX::ScriptingAddition can’t dynamically retrieve scripting addition terminology within a 64-bit process.” The solution is to force Ruby to run in 32-bit mode, which is easily done: simply start any script that uses the
OSAX
module with this line:#!/usr/bin/arch -i386 ruby
For all remaining scripts in this chapter and the next that use
OSAX
, assume that the above line stands at the start if running on Snow Leopard.
As a shortcut, you can generate the same OSAX::ScriptingAddition
instance by calling the osax
class method instead:
require 'osax'
sa = OSAX.osax("standardadditions")
As an even shorter shortcut, if the scripting addition file you want to access is StandardAdditions.osax (which it probably will be), you can omit the parameter:
require 'osax'
sa = OSAX.osax
You can now issue a command by sending a method call to this OSAX::ScriptingAddition
instance. The syntax is the same as for any command (described in Chapter 6).
require 'osax'
sa = OSAX.osax
sa.say "Good morning."
The osax
class method is not merely a convenience form of ScriptingAddition.new
. In the case where you call it with no parameters, as shown above — and only in that case — it caches the OSAX::ScriptingAddition
instance the first time it is called (as a class instance variable), and returns the cached instance on subsequent calls. Thus there is no inefficiency in code like this:
require 'osax'
OSAX.osax.beep
OSAX.osax.say "Hey, wake up." # same ScriptingAddition instance
However, in any other case, it is up to you to retain the OSAX::ScriptingAddition
instance and reuse it as needed.
ASDictionary works on scripting additions, so you can use it to choose a scripting addition file and generate a dictionary presentation. Alternatively, you can use an AppleScript-based dictionary presentation (such as Script Debugger or Script Editor).
The help
command doesn’t work on an OSAX::ScriptingAddition
instance, but you can use the introspective instance methods commands
, which lists all commands, and parameters
, which lists the parameters for the specified command:
require 'osax'
sa = OSAX.osax
p sa.commands
#=> ["ASCII_character", "ASCII_number", "activate", "adding_folder_items_to", "beep", ...]
p sa.parameters("display_alert")
#=> ["as", "buttons", "cancel_button", "default_button", "giving_up_after", "message"]
A scripting addition command is executed within the context of some application or process. If you don’t specify such a context (and none of the above examples do so), that context is the current process, which in this case is probably Ruby. To specify a different context, send an OSAX::ScriptingAddition
instance one of the same methods you use to specify an application (see Chapter 4):
by_name
by_id
by_creator
by_pid
by_url
The result is a different OSAX::ScriptingAddition
instance, one whose commands are executed in the context of the application you specified. The application acts as a host for commands sent to this OSAX::ScriptingAddition
instance.
require 'osax'
finder_osax = OSAX.osax.by_name("Finder.app")
As a shortcut, instead of calling by_name
explicitly, you can supply the application’s name as a second parameter to the osax
class method:
require 'osax'
finder_osax = OSAX.osax nil, "Finder.app" # nil first parameter means StandardAdditions.osax
If the scripting addition command you intend to call presents some sort of visible interface such as a dialog, specifying an application context is a really good idea. Otherwise, rb-appscript must turn Ruby itself into a GUI application, which involves extra overhead. Besides, if the user is already looking at or working with some application, it probably makes sense to cause the dialog to appear within that application.
require 'osax'
f = OSAX.osax(nil, "Finder.app")
f.activate # note this technique
f.display_alert "Not So Fast", :message => "Comb your hair first."
Observe that the activate
command works; although apparently sent to the OSAX::ScriptingAddition
instance, it is routed to the host. This is true for all the built-in commands discussed in the last section of Chapter 6.
The standard scripting addition commands that generate dialogs all raise an exception if the user cancels out of the dialog:
require 'osax'
f = OSAX.osax(nil, "Finder.app")
f.activate
f.display_alert "Not So Fast",
:message => "Comb your hair first.",
:buttons => ["Cancel", "OK"],
:cancel_button => "Cancel"
# user presses the Cancel button
#=> Appscript::CommandError: CommandError OSERROR: -128 MESSAGE: User canceled.
Handling this exception is up to you, if you want to prevent it from percolating up to top level and bringing your code to an untimely halt. The Appscript::CommandError
instance’s to_i
method will reliably yield -128
if the user cancels.
require 'osax'
f = OSAX.osax(nil, "Finder.app")
f.activate
begin
f.display_alert "Not So Fast",
:message => "Comb your hair first.",
:buttons => ["Cancel", "OK"],
:cancel_button => "Cancel"
rescue Appscript::CommandError => e
exit if e.to_i == -128 # user cancelled, quit in good order
raise e # something else happened
end
# if we get here, the user didn't cancel
You’re looking at a draft of a chapter from a work in progress, tentatively titled Scripting Mac Applications With Ruby: An AppleScript Alternative, by Matt Neuburg.
Covers rb-appscript 0.6.1. Last revised Jun 23, 2012. All content ©2012 by the author, all rights reserved.
This book took time and effort to write, and no traditional publisher would accept it. If it has been useful to you, please consider a small donation to my PayPal account (matt at tidbits dot com). Thanks!