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.


Prev: Just Enough Ruby
Next: The Application Object
Contents

Chapter 3: Apple Events

1. What Makes a Scriptable Application Scriptable

2. The Birth and Death of an Apple Event

3. The Form of an Apple Event

4. Dictionaries

5. Scripting Additions

To understand how we’re going to use Ruby and rb-appscript to communicate with scriptable applications, you need a sense of how communication with scriptable applications (technically known as interapplication communication, or IAC) works in the first place. What makes a scriptable application scriptable, and how can rb-appscript step into the communications chain and take the place of AppleScript? That is what this chapter will tell you. As you will see, it’s all about some little packets of power called Apple events.

You don’t need to be completely conversant with the details mentioned in this chapter. You are unlikely, in the normal course of things, to form any Apple events and send them “by hand”; the whole point is that rb-appscript is going to do that for you. But it will be really helpful for you to have a general of sense of what Apple events are, so that you understand what it is that rb-appscript is going to do for you. So, read this chapter in a relaxed frame of mind, but do read it. You’ll be glad you did.

1. What Makes a Scriptable Application Scriptable

An application is scriptable if it is possible to communicate with that application using a program that sends messages to the application, instead of using the application’s GUI (graphical user interface) by way of the mouse and keyboard. In particular, the purpose of these messages is to ask the application for information, and to tell the application to perform certain actions. On Mac OS X (as well as all Macintosh systems since System 7), the standard system-level message structure for scriptable applications is the Apple event.

By “standard,” what I mean is this. In theory it might be possible for an application to make itself scriptable in a manner completely unique to itself. The developer of the application could tell users: “You perform the following steps, and you’ll be able to communicate with my application.” But, way back in 1991, the folks at Apple Computer, Inc., did something clever. They effectively said: “There’s no need for an application to make itself scriptable in a manner completely unique to itself. We will describe for you a message structure called an Apple event. Just make it so that your application can receive and parse and obey Apple event messages, and presto, your application will be scriptable in a standard way. We promise to make it easy for folks to send Apple event messages to your application, and we promise to keep supporting Apple events going forward. So just implement the ability to receive Apple events, folks, and join the ever-growing family of Macintosh scriptable applications. Your users will love you for it, and you’ll make more money.”

By “system-level,” what I mean is this. Apple Computer, Inc., didn’t just describe Apple events; they didn’t just design Apple events on paper, as it were. They built into the Macintosh system the actual means for constructing, sending, receiving, and parsing Apple events. They didn’t make it trivial for an application to implement the ability to receive Apple events, but they did make it possible, in a way that very strongly encouraged developers who wanted their applications to be scriptable to make them scriptable through Apple events.

So, what makes a scriptable application scriptable? It’s the fact that it is listening for Apple events, and that, when an Apple event arrives, it knows how to parse it, understand what it’s being told to do, and, if appropriate, do it.

I say “if appropriate” because clearly it would not make any sense to require that every scriptable application be able to do everything. An application, by nature, knows how to do certain things, and a scriptable application will be able to respond to Apple events having to do with what it knows how to do. The Finder knows how to do things with a folder (copy it, move it to the trash, report its size, rename it). iTunes knows how to do things with a song (play it, put it in a playlist, change its title). So we may say that, in some sense, each scriptable application defines a repertoire of acceptable Apple events that it is willing to entertain.

(The fact that each scriptable application defines a different repertoire of acceptable Apple events means that each scriptable application presents the user with a different set of scriptability challenges. But, about that, more in due time.)

2. The Birth and Death of an Apple Event

Referring to the following diagram (Figure 3–1) and the numbered steps therein, here’s a moment-by-moment description of how an Apple event comes into being and is sent from one application to another.

image

Figure 3–1

  1. It all starts with a program or process or application (whatever you like to call it) that decides, somehow, that it’s going to send an Apple event to another application. Let’s call this first application the sender, and the application that it’s going to send the Apple event to, the target. So, in Figure 3–1, we see the sender over on the left side. It has no particular icon in the diagram, signifying that it’s an anonymous application. It could be anything. In real life, if you were using AppleScript to script an application, and if you were running your AppleScript code in Apple’s Script Editor, the sender would be the Script Editor. In this book, of course, the sender will be Ruby.

  2. Having made up its mind to send an Apple event, the sender proceeds to assemble the Apple event. The Apple event consists of two kinds of stuff:

  3. Having constructed the Apple event, the sender contacts the system and hands it the Apple event. The system is portrayed as a Finder-like icon sitting in the middle of Figure 3–1. Think of the system here as playing a role similar to the post office. Actually, the system is more like one of those telegram delivery people you read about in a Sherlock Holmes story (or in Henry Miller). The system looks at the delivery instructions on the outside of the envelope to see who the target application is. If it can’t locate the target application, the system reports a delivery failure back to the sender, and that’s the end of the story. Various other things can go wrong at this stage as well. But let’s presume that all is well, and the system determines that the Apple event is deliverable.

  4. The system attaches to the Apple event a secondary envelope marked “reply”. This is why I say that the system is like a telegram messenger. The system isn’t just going to hand the Apple event message over to the target application; it’s going to stand there waiting, with one hand on the reply envelope, waiting for the target application to insert a reply.

  5. The system delivers the Apple event to the target application, which is portrayed in this case as being iTunes, over to the right side of Figure 3–1. The target application examines the message (the contents of the Apple event) and responds in some way. The ideal response is that the target application should do what it’s told: it starts playing the currently selected song, or it changes the name of the second playlist to Brahms, or it looks up the location on disk of the second song in the third playlist. But of course at this point things might go wrong. There might be no currently selected song, or no second playlist, and so forth. Or the target might not be able to read the message at all.

  6. So, no matter what, the target application is expected to put something into the reply envelope reporting its response to the message; and this step shows it doing so. If the message was, “Play the currently selected song,” and if the target application did that, it might just reply, “OK.” If it was asked for the location of the second song in the third playlist, the reply will be that location. If things went wrong, the reply will be that things went wrong. But one way or another, the target application is supposed to reply. Then, and only then, the target application is finished with the Apple event and lets go of it.

  7. The original Apple event is now dead; it has been delivered and replied to, and is of no further interest. The target application, moreover, has let go of the reply and is now going about its own business, whatever it may be. As far as the target application is concerned, the incident is over. The system, meanwhile, has not let go of the reply envelope. It still has one hand firmly on the reply, and is now shuttling back to the sender to deliver it.

  8. The system hands the reply to the sender and lets go of it. The system’s role in the story of this Apple event is now over, and the system can go back to whatever it was doing before all of this got started. Meanwhile, the sender has received the reply, and can examine it and proceed accordingly, whatever that may mean in this particular case.

The song-and-dance of Apple event formation and delivery, and especially the central role of the system as the go-between, may seem rather elaborate. But it is all actually quite understandable. At the far left and right of Figure 3–1 are two different processes. For them to communicate requires a certain amount of coordination and flexibility. There’s many a slip twixt the cup and the lip; things can take time, and things can go wrong. The sender should not be allowed to waylay or interrupt the target in the middle of whatever it may be doing at the moment, nor should the sender be held hostage to the vagaries of the target’s behavior while awaiting a reply. The architecture of Apple event sending is designed to introduce certainty and orderliness into an uncertain world. This is why the sending of an Apple event is separated into the sending of two distinct messages — the original Apple event and the reply — and why both messages are handled by the system.

Consider the position of the sender, back in step 1, as it resolves to send an Apple event. In all probability, sending this Apple event is part of a longer sequence of actions. The sender might need the reply from the Apple event in order to proceed to the next step in that sequence; or, even if not, the sender might need at least to know that the command expressed by the Apple event has been obeyed before proceeding.

Therefore the delivery instructions that accompany the Apple event as it is formed in step 2 can include information about the sender’s willingness to await a reply. The sender can express a lack of concern with any reply; in that case, the system will stop at step 5, not waiting for a reply and not reporting back to the sender (and not even reporting an error at step 3 if the message is undeliverable), and the sender will carry on with its sequence of actions immediately after handing the Apple event over to the system. But in the vast majority of cases, the sender will want to wait for a reply, and in that case, the Apple event will have a timeout value specifying the maximum amount of time the sender is willing to wait. The sender can specify a timeout value, or a default timeout value of 60 seconds will be used. The timeout value constitutes a contract between the sender and the system: either the system will be back in step 8 with the reply before the timeout interval has elapsed, or else the system will be back in step 8 at the end of the timeout interval with a report that it couldn’t obtain a reply from the target in time. This could happen, for example, because the target was too busy to respond to the Apple event at all, or because execution of the command contained in the Apple event is taking the target longer than the timeout interval.

The central role of the system, therefore, takes all the pressure and uncertainty off the sender. The sender packages up the Apple event and hands it to the system, and then just sits back and waits, secure in the knowledge that no matter what, the system will be back within the timeout interval with a report. The target application might not be found, the target application might take too long to reply, the target application might crash while trying to execute the Apple event’s instructions; but the system is not going to crash (we fervently hope and believe). So the sender places its faith in the system and lets the system do all the worrying about how delivery of and response to the Apple event is going.

By the same token, the sender itself might crash while waiting for a reply. But because the sender is not intimately connected either to the system or to the target during the delivery of the Apple event and the target’s response to it, that’s not really a big deal. In such a case, the system will simply attempt to return to the sender with the reply, discover that the sender has mysteriously vanished, shrug its shoulders, and move on.

3. The Form of an Apple Event

The last thing you want to do is to have to deal directly with an Apple event. You don’t want to see an Apple event directly, in what is usually called its raw form. A raw Apple event is complicated and tightly encoded. It is intended to be formed by a program and parsed by a program. It is meant for computer analysis, not for human eyes.

Nonetheless, since Apple events are the medium of scriptability, and since scriptability is our subject, it will be salutary at least to look at one. You will thus be able to see an Apple event’s structure, even though you may not understand all the details.

To see an Apple event, we’ll intercept one as the system delivers it. In particular, we’ll set up an interception service so that we can view any Apple events sent to iTunes; then we’ll send one and intercept it.

Start at the command line, with iTunes not running:

$ export AEDebugReceives=1
$ open /Applications/iTunes.app

Now we’ll use Apple’s Script Editor to send an Apple event. Start up Script Editor, make a new script window, and enter and run this script:

tell application "iTunes" to play track 1 of playlist "Library"

Finally, look in the Console. Here you’ll find a schematic description of the Apple event that iTunes just received. It will look roughly like this (though some details may vary):

AE2000 (2041): Received an event:
------oo start of event oo------
{ 1 } 'aevt':  hook/Play (ppc ){
          return id: 132841498 (0x7eb001a)
     transaction id: 0 (0x0)
  interaction level: 112 (0x70)
     reply required: 1 (0x1)
             remote: 0 (0x0)
  target:
    { 1 } 'psn ':  8 bytes {
      { 0x0, 0x2260001 } (Script Editor)
    }
  optional attributes:
    { 1 } 'reco':  - 1 items {
      key 'csig' - 
        { 1 } 'magn':  4 bytes {
          65536l (0x10000)
        }
    }

  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' - 
        { 1 } 'obj ':  - 4 items {
          key 'form' - 
            { 1 } 'enum':  4 bytes {
              'indx'
            }
          key 'want' - 
            { 1 } 'type':  4 bytes {
              'cTrk'
            }
          key 'seld' - 
            { 1 } 'long':  4 bytes {
              1 (0x1)
            }
          key 'from' - 
            { 1 } 'obj ':  - 4 items {
              key 'form' - 
                { 1 } 'enum':  4 bytes {
                  'name'
                }
              key 'want' - 
                { 1 } 'type':  4 bytes {
                  'cPly'
                }
              key 'seld' - 
                { 1 } 'TEXT':  7 bytes {
                  "Library"
                }
              key 'from' - 
                { -1 } 'null':  null descriptor
            }
        }
    }
}

------oo  end of event  oo------

Let’s analyze this Apple event into its fundamental parts. I’m going to ignore some of it, but not much. (Oh, and you can quit iTunes now, unless you want it to go on playing while you read the rest of this discussion.)

          return id: 132841498 (0x7eb001a)
     transaction id: 0 (0x0)
  interaction level: 112 (0x70)
     reply required: 1 (0x1)
             remote: 0 (0x0)
  target:
    { 1 } 'psn ':  8 bytes {
      { 0x0, 0x2260001 } (Script Editor)

This is the information about the Apple event. The original target (iTunes) has been removed because we are intercepting the Apple event after it has passed through the system and is being delivered; instead, at this stage, the sender (Script Editor) has been substituted. Observe the flag indicating that the sender is waiting for a reply. No timeout value is attached to this event, so the default timeout of 60 seconds is in force. The rest of the information isn’t important for our purposes.

{ 1 } 'aevt':  hook/Play (ppc ){

The eight letters hookPlay, written as two sets of four letters separated by a slash, are the Apple event code for an actual command, whose English-language equivalent is “play”. These sets of four letters are a pervasive feature of Apple events, and are called four-letter codes. (They are actually numbers written as four letters; four bytes can be viewed indifferently as either four packed ASCII characters or an integer.) The fact that one of the four-letter codes is Play is convenient but not representative; in general, four-letter codes need have no relationship to their English-language equivalent.

key '----' - 

Most Apple events take at least one parameter. Every parameter of an Apple event is labeled with a four-letter code; in other words, Apple event parameters are distinguished by name, not by order. This line announces that we are about to be shown this Apple event’s parameter, and tells us its name, which is the four-letter code '----'. This name is set aside for what is called the direct parameter; if an Apple event has parameters, one of them will nearly always be the direct parameter.

{ 1 } 'obj ':  - 4 items {
  key 'form' - 
    { 1 } 'enum':  4 bytes {
      'indx'
    }
  key 'want' - 
    { 1 } 'type':  4 bytes {
      'cTrk'
    }
  key 'seld' - 
    { 1 } 'long':  4 bytes {
      1 (0x1)
    }
  key 'from' - 

Now we’re starting to read the value of this Apple event’s direct parameter. The first line tells us the datatype of this value: it is what’s called an object specifier. An object specifier specifies an object; that is to say, it picks out, among a number of possible objects, which one we want to talk about. It consists of four pieces, which are labeled with four-letter codes: 'form', 'want', 'seld', and 'from'. The order in which these are displayed to us is meaningless, so I’m free to choose a different order in describing the four pieces; I’ll describe this object specifier in the order 'want', 'form', 'seld', 'from':

{ 1 } 'obj ':  - 4 items {
  key 'form' - 
    { 1 } 'enum':  4 bytes {
      'name'
    }
  key 'want' - 
    { 1 } 'type':  4 bytes {
      'cPly'
    }
  key 'seld' - 
    { 1 } 'TEXT':  7 bytes {
      "Library"
    }
  key 'from' - 
    { -1 } 'null':  null descriptor

I’ll describe this second object specifier in the same order as the first:

The interesting thing is that even though you are not a computer and would not really like to have to deal with these four-letter codes all the time, you can readily see how this Apple event might indeed be the equivalent of the English-like AppleScript expression play track 1 of playlist "Library" with which we started. And you can readily imagine what’s happened behind the scenes. Clearly the expression play track 1 of playlist "Library" has been somehow translated into its Apple event equivalent. This, indeed, is the heart of how AppleScript works — the heart, in fact, of what AppleScript is. It is a language in which you, the user, can use a phrase like play track 1 of playlist "Library" and have the language translate it into an Apple event for you. Not only that; you can use a phrase like tell application "iTunes" to, and have the language package up the Apple event and hand it over to the system with instructions to pass it on to iTunes. In short, AppleScript is all about constructing and sending Apple events.

But Apple events themselves are completely language-neutral and language-independent. An Apple event doesn’t know or care how it was formed. As long as an Apple event is a valid Apple event, it’s a valid Apple event. This means that in a situation like this, where we would like to tell iTunes to start playing a particular song, we are perfectly free to construct and send the desired Apple event in some other way. In this book, that other way will be Ruby. This book is about using Ruby to construct and send Apple events.

4. Dictionaries

The foregoing section repeatedly presupposes an equivalence between certain Apple event four-letter codes and English words: for example, I have said that 'hook/Play' means “play,” that 'cTrk' means “track,” and that 'cPly' means “playlist”. Words of this kind are called terms, or (collectively) terminology; and the equivalence between a term’s four-letter code and its English form is maintained by means of a resource called the dictionary. If an application is to be scriptable with raw Apple events alone, then it is sufficient for the developers of the application to promulgate some kind of separate documentation describing the specification for those Apple events; but if an application is to be scriptable with AppleScript, which uses English-like terminology, then the application must contain a dictionary. Each application that is scriptable with AppleScript contains a dictionary defining terminology proper to that application (as for example with iTunes, whose dictionary defines terms like “play,” “track,” and “playlist”); AppleScript itself also maintains a dictionary of terminology at system level, defining some commonly used terms such as “name” and “window”.

The dictionary, then, is what makes it possible for the programmer to use English-like terms instead of raw four-letter codes. When we say, in AppleScript:

tell application "iTunes" to play track 1 of playlist "Library"

the AppleScript compiler sees that we wish to target iTunes, asks iTunes for its dictionary, looks up the terms “play,” “track,” and “playlist” in that dictionary, finds that they are all there along with their corresponding four-letter codes, substitutes the four-letter codes, uses them (and the syntax of the expression) to form an actual Apple event, and, when we run the script, hands the Apple event off to the system for delivery.

In that example, translation goes from English-like terms to four-letter codes. But the same thing must also happen in reverse. For instance, we might say, in AppleScript:

tell application "iTunes" to get selection

If we run that script in the Script Editor, the reply might come back something like this:

{file track id 411 of user playlist id 397 of source id 41 of application "iTunes"}

But obviously that isn’t how the reply really came back from the system. What the system delivered back to Script Editor was of course an Apple event — something, in fact, like this:

{ 1 } 'aevt':  aevt/ansr (ppc ){
          return id: 25821190 (0x18a0006)
     transaction id: 0 (0x0)
  interaction level: 112 (0x70)
     reply required: 0 (0x0)
             remote: 0 (0x0)
  target:
    { 1 } 'psn ':  8 bytes {
      { 0x0, 0x800001 } (iTunes)
    }
  optional attributes:
    < empty record >
  event data:
    { 1 } 'aevt':  - 1 items {
      key '----' - 
        { 1 } 'list':  - 1 elements {
          { 1 } 'obj ':  - 4 items {
            key 'want' - 
              { 1 } 'type':  4 bytes {
                'cFlT'
              }
            key 'from' - 
              { 1 } 'obj ':  - 4 items {
                key 'want' - 
                  { 1 } 'type':  4 bytes {
                    'cUsP'
                  }
                key 'from' - 
                  { 1 } 'obj ':  - 4 items {
                    key 'want' - 
                      { 1 } 'type':  4 bytes {
                        'cSrc'
                      }
                    key 'from' - 
                      { -1 } 'null':  null descriptor
                    key 'form' - 
                      { 1 } 'enum':  4 bytes {
                        'ID  '
                      }
                    key 'seld' - 
                      { 1 } 'long':  4 bytes {
                        41 (0x29)
                      }
                  }
                key 'form' - 
                  { 1 } 'enum':  4 bytes {
                    'ID  '
                  }
                key 'seld' - 
                  { 1 } 'long':  4 bytes {
                    397 (0x18d)
                  }
              }
            key 'form' - 
              { 1 } 'enum':  4 bytes {
                'ID  '
              }
            key 'seld' - 
              { 1 } 'long':  4 bytes {
                411 (0x19b)
              }
          }
        }
    }
}

It is left as an exercise for you, the reader, to disentangle the three levels of embedded object specifier in that Apple event and persuade yourself that it really is the equivalent of the English-like expression:

{file track id 411 of user playlist id 397 of source id 41 of application "iTunes"}

Clearly the English-like expression was generated by using the dictionary in reverse, looking up the four-letter codes (such as 'cSrc') in the dictionary and substituting English-like terms (such as “source”).

In using Ruby to send Apple events to scriptable applications, we don’t want to have to use raw Apple event four-letter codes if we can avoid it. We want to use English-like terminology and have Ruby use the dictionary to translate between this and the four-letter codes, in both directions, just as AppleScript does. And that is exactly what we and Ruby will do, thanks to rb-appscript. (However, we can use rb-appscript to form and send raw Apple events directly if we want to, and some occasional examples of doing this will appear later in this book.)

For example, let’s return to the Apple event we sent in the previous section, using this AppleScript expression:

tell application "iTunes" to play track 1 of playlist "Library"

Using Ruby and rb-appscript, we could form the raw Apple event equivalent “by hand”, and send it, like this:

AEM::Application.by_pid(855).event('hookPlay', 
  {'----' => AEM.app.elements('cPly').by_name("Library").elements('cTrk').by_index(1)}).send

But we’re not going to! Instead, we’ll write something like this:

Appscript.app("iTunes").playlists["Library"].tracks[1].play

You’ll observe that this involves essentially all the same English-like terminology as the AppleScript equivalent; some of the terms appear in the plural instead of the singular, but basically it’s the same “play” and “track” and “playlist”. The similarity is no coincidence; it’s because both AppleScript and rb-appscript are looking in the same place — the iTunes dictionary — to learn what English-like terms are legal, and to translate them into four-letter codes to construct and send what is ultimately exactly the same Apple event.

So now you understand why using Ruby instead of AppleScript to communicate with scriptable applications is possible — not only possible, but fun. It’s possible because an Apple event is an Apple event; the system and the target are indifferent as to the language and source from which the Apple event comes. It’s fun because if an application is scriptable with AppleScript, it provides a dictionary, and rb-appscript can read this dictionary and can implement the English-like terminology that it contains, so that the user, instead of having to form raw Apple events by hand, can employ English-like terminology and a simple, convenient syntax.

Some applications, for one reason or another, must be running in order to supply their dictionary. This means that if you do anything in rb-appscript that requires an application’s dictionary, and if rb-appscript has not already loaded that application’s dictionary, the application will launch. For example:

Appscript.app("iTunes").howdy

The term “howdy” isn’t even real; it isn’t something you can successfully say to iTunes, and running that script results in an error message. Nevertheless, in order to discover that “howdy” isn’t something you can actually say to iTunes, rb-appscript must consult iTunes’s dictionary; and in order to do that, if iTunes isn’t already running, iTunes must launch. There’s nothing you can do about this; it isn’t your fault, and it isn’t rb-appscript’s fault. It’s just a feature of how iTunes implements its dictionary. It is possible for an application to implement its dictionary in such a way that it doesn’t need to be running in order to supply its dictionary, and some applications do so, but iTunes isn’t one of them.

So far, I have spoken as if the dictionary’s only clients were languages like AppleScript and Ruby. But a dictionary has another client — you, the human user. A dictionary is machine-readable, but it is also intended to be presented to a human being, a programmer. That’s how you know what a scriptable application’s terminology is. That’s how you know that when you’re talking to iTunes you can use words like “play” and “track” and “playlist.”

This fact often leads beginners to believe that the dictionary constitutes complete documentation on how to script a particular application. It doesn’t. Because every application defines its own terminology in its own dictionary, without much particular relationship to the terminology of other applications, and because the dictionary may describe inaccurately or inadequately the range of what Apple events that application accepts or what Apple event(s) you would have to send to an application in order to achieve a particular goal, the implicit promise of Apple events, that they would (as I put it earlier) make all scriptable applications scriptable in a standard way, has never come to fruition. In fact, the reality is just the opposite: scripting each application is a new and different challenge. Interpreting and understanding an application’s dictionary is a major part of meeting this challenge, and this book will spend considerable time explaining how to do this; but much of the process of learning to script an application often involves sheer experimentation, not to mention the acquisition of esoteric wisdom from users who have travelled this road before you. These shortcomings are implicit in the nature of Apple events (the freedom of different applications to interpret and respond to them differently) and the dictionary (its failure to tell you some of what you need to know), and there’s nothing that rb-appscript can do about them.

5. Scripting Additions

A scripting addition is a resource containing code and a dictionary. It is not an application; rather, AppleScript loads automatically any scripting additions that it finds in certain fixed locations, such as the user’s ~/Library/ScriptingAdditions directory, and the terms defined in scripting addition dictionaries (most commonly, commands) are made globally available. The idea is that scripting additions, provided both by Apple and by other developers, effectively constitute extensions to AppleScript’s own dictionary.

By “available globally” I mean that scripting addition terminology can be used both when targeting a particular scriptable application and when targeting no application at all. Sometimes it makes a difference what scriptable application is being targeted. In particular, some scripting addition commands present the user with visible interface, such as a dialog. If such a command is given when targeting a particular application, the dialog will appear within that application. These dialogs are typically modal, so it’s just as if the application itself had put up a modal dialog: if that application is not frontmost, its icon will bounce up and down in the Dock, alerting the user to deal with the dialog, and the user cannot do anything else in that application before dismissing the dialog.

Unlike AppleScript, with rb-appscript no scripting additions are loaded automatically. In Ruby, if you want to use a scripting addition command, you must explicitly load the scripting addition that implements it. Furthermore, scripting addition command names do not, in Ruby, automatically inject themselves into the namespace of an application’s commands (or into any kind of global namespace); if you want to execute a scripting addition command in the context of a particular application, you can, but you must say so.

Having to know what scripting addition implements a particular scripting addition command is not something that AppleScript users are accustomed to (because scripting addition commands are “just there,” globally available), so rb-appscript’s way of dealing with scripting addition commands might seem inconvenient. I would argue that just the opposite is the case: the rb-appscript way of dealing with scripting additions has some decided advantages over AppleScript’s. If you don’t use any scripting addition commands, there’s no overhead in Ruby of loading the scripting addition terminology, as there is in AppleScript. If you do use a scripting addition command in the context of an application, it is called rapidly, because you have specified explicitly what it is you want to do; in AppleScript, on the other hand, the way a scripting addition command is called in the context of an application is that the command is first sent to the application, an error is generated (because the application itself does not implement that command), and then an attempt is made to resolve the command as a scripting addition command. In short, scripting addition commands do not pollute the global namespace in Ruby the way they do in AppleScript, and that’s a good thing.

By the way, back in the System 7 days when every Macintosh file had a four-letter type code and a four-letter creator code, the type code for scripting addition files was 'osax'. For this reason, a scripting addition is often referred to as an osax (unofficial plural, osaxen). I’m telling you this so that you won’t be surprised when rb-appscript’s commands for dealing with scripting additions turn out to be in a file called osax.rb (and a module called OSAX).


Prev: Just Enough Ruby
Next: The Application Object
Contents

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!