osax

What is osax?

The OSAX module provides an easy way to call scripting additions (a.k.a. OSAXen) from Ruby. It exports a single public class, ScriptingAddition, and two functions, osax and scripting_additions.

The ScriptingAddition class represents a single scripting addition. It is similar to an appscript application object, except that it defines commands for the specified scripting addition instead of the application's normal commands.

Once you've created a ScriptingAddition instance, you can invoke its commands in exactly the same way as you would call a scriptable application's commands in appscript.

For example:

require "osax"

sa = OSAX::ScriptingAddition.new("StandardAdditions")

sa.say("Hello world", :using=>"Victoria")

The default application commands (run, activate, quit, etc.) are also available; see the appscript manual for details on those.

By default, ScriptingAddition objects are targeted at the current application. You can obtain a ScriptingAddition object targeted at another application by calling one of its by... methods.

OSAX.scripting_additions

The scripting_additions function returns the names of all installed scripting additions:

require "osax"

p OSAX.scripting_additions
# Result: ["Digital Hub Scripting", "StandardAdditions", ...]

OSAX.osax

The osax function provides a convenient shortcut for creating new ScriptingAddition instances.

osax(name=nil, app_name=nil) -- convenience method for
        creating a new ScriptingAddition instance
    name : String | nil -- scripting addition's name
            (nil = "StandardAdditions")
    app_name : String | nil -- target application's name/path, or nil
            for current application
    Result : ScriptingAddition

For example:

require "osax"

sa = OSAX.osax

p sa
# Result: #<OSAX::ScriptingAddition
#           name="StandardAdditions"
#           target=AEM::Application.current>

In addition, the osax function accepts an application name as an optional second argument, allowing you to specify the application you want to handle the scripting addition's commands, e.g.:

OSAX.osax("StandardAdditions", "System Events")

is shorthand for:

OSAX::ScriptingAddition.new("StandardAdditions").by_name("System Events")

To specify applications by other means (e.g. URL), create a ScriptingAddition object first, then call the appropriate by... method.

For convenience, if both arguments are nil then a ScriptingAddition object for StandardAdditions is created and returned. This object is also cached internally for efficiency and returned as-is in subsequent calls; thus, for example:

sa = osax
sa.some_command
sa.another_command

could also be written as:

osax.some_command
osax.another_command

without the additional overhead of creating a new ScriptingAddition object each time.

OSAX::ScriptingAddition

ScriptingAddition -- represents a single scripting addition and its
        target application

    Constructors:

        ScriptingAddition.new(name, terms=nil) -- make a ScriptingAddition
                object for the specified scripting addition, targeted
                at the current application
                
                name: string -- a scripting addition's name, 
                        e.g. "StandardAdditions"; basically its filename
                        minus the '.osax' suffix
                
                terms : module or nil -- an optional terminology glue
                        module,as exported by Terminology.dump; if
                        given, ScriptingAddition will use this instead 
                        of retrieving the terminology dynamically

    Methods:

        # Introspection:

        commands -- returns names of all available commands

        parameters(command_name) -- returns a command's parameter names

        # Specifying a different target application:

        # Each of the following methods returns a new ScriptingAddition
        # instance targeted at the specified application. The arguments
        # are the same as for the by_name, by_creator, etc. methods in
        # appscript.
        
        by_name(name) -- name or full path of application, e.g. "Finder"

        by_creator(creator) -- four-character creator code, e.g. "ttxt"

        by_id(id) -- bundle id, e.g. "com.apple.ical"

        by_pid(pid) -- Unix process ID, e.g. 4005

        by_url(url) -- eppc URL, e.g. "eppc://jukebox-mac.local/iTunes"

Examples

require "osax"

# Create a new ScriptingAddition object:

sa = OSAX.osax


# List all available commands:

p sa.commands
# Result: ["ASCII_character", "ASCII_number", "activate", ...]


# Call some commands:

sa.beep

p sa.path_to(:scripts_folder)
# Result: MacTypes::Alias.at("/Users/foo/Library/Scripts/")

p sa.display_dialog("Ruby says hello!",
                    :buttons=>["Hi!", "Howdy!", "Duuuude!"],
                    :default_button=>3)
# Result: {:button_returned=>"Howdy!"}

Notes

GUI interaction

When using scripting addition commands that require GUI access (e.g. display_dialog) targeted at the command-line Ruby interpreter, the osax module will automatically convert the non-GUI interpreter process into a full GUI process to allow these commands to operate correctly. If you want to avoid this, target these commands at a faceless GUI application such as System Events instead:

sa = OSAX.osax("StandardAdditions", "System Events")
sa.activate # give focus to System Events
p sa.display_dialog("Ruby says hello!",
                    :buttons=>["Hi!", "Howdy!", "Duuuude!"],
                    :default_button=>3)
# Result: {:button_returned=>"Duuuude!"}

64-bit limitations

The osax module currently only supports dynamic retrieval of scripting addition terminology when running in 32-bit processes. To use it in 64-bit processes, use the Terminology module's dump method to export a static terminology 'glue' module for the desired scripting addition (running it in a 32-bit process), then import that module and pass it as the second argument to the ScriptingAddition class's initialiser. For example, to export a glue module for Standard Additions:

require 'appscript'

Terminology.dump('/System/Library/ScriptingAdditions/StandardAdditions.osax', 
        'StandardAdditions', 'standard_additions.rb')

To create a new ScriptingAddition instance using the terminology provided by this glue module:

require 'osax'
require 'standard_additions'

sa = OSAX::ScriptingAddition.new('StandardAdditions', StandardAdditions)

Known problems

When using the osax module within RubyCocoa-based applications, avoid creating ScriptingAddition instances before the main event loop is started as this can result in the application behaving strangely (minimised windows don't expand correctly) due to a bug in OS X's OSAGetAppTerminology function.