This tutorial will explain how to use RubyOSA to control scriptable Mac OS X applications.
We expect you to have some Ruby programming background before starting reading it.
We also expect you to have properly installed RubyOSA on your machine. You can read information about the installation of RubyOSA on the main page.
RubyOSA is a bridge between Ruby and the Open Scripting Architecture (OSA). The key idea is to create a Ruby API on the fly based on a target application’s scriptable definition.
Once you target an application with RubyOSA you get a special kind of proxy object, which is the entry point of the generated Ruby API.
Let’s start with something easy:
require 'rbosa' # (1)
app = OSA.app('iTunes') # (2)
puts app.current_track.name # (3)
We need to require the RubyOSA bridge first (1). Note that if you installed RubyOSA via RubyGems you might want to require ‘rubygems’ before.
Then we get an handle on the iTunes application (2), under the form of an OSA::ITunes::Application object. The OSA::ITunes module and everything inside is automatically generated during OSA.app().
Finally we use the application object to get the name of the current track, and we print the result to the standard output (3).
As you just learnt, you can locate an application by passing its name to OSA.app(). But this is not the only way.
You can also locate an application by providing its signature (as a four characters string), or bundle ID:
OSA.app(:signature => 'hook') OSA.app(:bundle_id => 'com.apple.iTunes')
More interestingly, if the application you want to control hasn’t been installed in one of the standard applications locations, RubyOSA may have some trouble locating it. You can provide the full path of the application to work around this:
OSA.app(:path => '/Somewhere/Applications/MyApp.app')
Note that OSA.app(‘iTunes’) is a convenience shortcut to OSA.app(:name => ‘iTunes’).
Getting an application object is not very useful if you don’t know which messages it responds to, as well as their purposes.
RubyOSA comes with a tool called rdoc-osa which allows you to generate some documentation about the API RubyOSA dynamically generated for a given scriptable application.
For example:
$ rdoc-osa --name iTunes [...] $ open doc/index.html
rdoc-osa acts as a front-end to the RDoc tool. By default it generates HTML documentation in the doc directory of the current working directory.
Here is an example of what is generated to describe iTunes’s Ruby API.
You can also generate RI documentation:
$ rdoc-osa --name iTunes --ri [...] $ ri current_track
To learn more about rdoc-osa, type rdoc-osa -h at the command line.
You may want to call commands defined in Scripting Additions terminologies. A popular one is StandardAdditions, delivered in Mac OS X as /System/Library/ScriptingAdditions/StandardAdditions.osax.
In order to do that you use the #merge method on your application object:
app = OSA.app('iTunes')
app.merge('StandardAdditions')
app.beep
puts app.choose_file
The StandardAdditions API will be merged into the application object, allowing you to call new methods on it.
This will ask iTunes to emit a beep, then to present the file chooser window. The chosen path will be printed to the standard output on success, otherwise an exception will be raised (if the user cancels the window for example).
#merge works like OSA.app(). You can also provide either the signature, bundle ID or full path to a scriptable addition using respectively the :signature, :bundle_id or :path keys/values.
RubyOSA can controls scriptable applications running on other machines.
The Remote Apple Events option must be checked in the Sharing preferences pane on the remote machine.
Then, you need to supply to RubyOSA the address of the machine when getting your application object:
app = OSA.app('iTunes', :machine => 'my-remote-machine.local')
app.play
An authentication window will appear asking you for the username and password of your remote account. If the authentication succeeded, RubyOSA will download the scriptable definition of the remote application, and then acts as normal, except that events will be sent to the remote computer instead of the local one.
If you want to avoid the authentication window you can pass the username and password to RubyOSA directly:
app = OSA.app('iTunes',
:machine => 'my-remote-machine.local',
:username => 'John Foo',
:password => 'johnfoo42')
app.play
Note that you can only control remote applications by providing their names. Trying to provide the signature, bundle ID or even the path on the remote machine won’t work (for now).
The OSA module provides some global variables that you can set to change the behavior of RubyOSA.
| Name | Default value | Description |
| OSA.lazy_events | true | Controls whether OSA proxy objects are resolved on demand, or always. By default objects are only resolved on demand, when necessary. Setting this variable to false will force every object returned by RubyOSA to be resolved. This may affect the performance of your program as one or more extra Apple events will be sent. |
| OSA.utf8_strings | false | Controls whether strings will be encoded as Unicode or ASCII. By default strings are encoded in ASCII as some applications cannot handle Unicode strings. |
| OSA.timeout | -1 | Controls the timeout duration of Apple events sent by RubyOSA. The value is expressed in ticks. By default it’s set to -1 which means about one minute. -2 means that there is no timeout. |
| OSA.wait_reply | nil | Controls whether RubyOSA should expect a result from the Apple events it will send. When set to nil, the default, it will determine this from the scriptable definition. However, it’s pretty rare but you may encounter a malformed application command. You can therefore either set this variable to true or false to force RubyOSA to send back or not a return value. |
You set these settings like this:
OSA.timeout = -1 app.do_something
For your convenience, we added an API to change one or more settings only for a given scope:
OSA.set_params(:timeout => -1, :lazy_events => false) do app.do_something app.do_something_again end # Here the settings will get their original values