Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using "whose" on arrays in Javascript for Automation

Playing with the new JS for automation using Script Editor. I'm getting an error on the final line of the following:

var iTunes = Application("iTunes");
var sources = iTunes.sources();
var library = sources.whose({name : "Library"});

Confirmed that the sources array is as expected (two elements, one with name "Library" and one "Internet Radio"). But that final line chokes with Error on line 3: TypeError: undefined is not a function (evaluating 'sources.whose({name : "Library"})').

As far as I can tell, I'm using the right syntax for the whose function. (I also tried with an explicit _equals clause to the same result.) What am I doing wrong?

like image 856
Ben Zotto Avatar asked Nov 22 '14 01:11

Ben Zotto


1 Answers

What am I doing wrong?

Short answer: it's not your fault. The JXA documentation is a sack of lies.

Longer explanation: an object's elements have nothing to do with Arrays. They represent a one-to-many relationship in an object graph, in this case, between a source object and zero or more library objects.

While many relationships may reflect the underlying implementation's containment hierarchy, there is no obligation to do so; e.g. Finder allows you to identify objects on the desktop in multiple ways:

items of folder "Desktop" of folder "jsmith" of folder "Users" of disk "Macintosh HD" of app "Finder"
items of folder "Desktop" of folder "jsmith" of folder "Users" of startup disk of app "Finder"
items of folder "Desktop" of home of app "Finder"
items of folder "Macintosh HD:Users:jsmith:Desktop" of app "Finder"
items of desktop of app "Finder"
items of app "Finder"
[etc.]

Apple event-based application scripting is based on remote procedure calls plus simple first-class queries. It's not OOP, regardless of superficial appearance: that's just syntactic sugar to make its queries easy to read and write.

...

In this case, your second line is telling iTunes to get a list (Array) of query objects (ObjectSpecifiers) that identify each of the source objects in your iTunes application:

var iTunes = Application("iTunes");
var sources = iTunes.sources();

Once you've got an Array, you can't use it to construct further queries, because JavaScript doesn't know how to build queries itself. What you actually want is this:

var iTunes = Application("iTunes");
var sourcesSpecifier = iTunes.sources;
var librarySpecifier = sourcesSpecifier.whose({name : "Library"});

That will give you an object specifier that identifies all of the source objects whose name is "Library". (If you only want to specify the first source object named "Library", use the byName method instead of whose; it's simpler.)

--

Personally, I consider all this somewhat academic as JXA's Apple event bridge implementation, like its documentation, is mostly made of Lame and Fail anyway. It mostly works up to a point, then poops on you beyond that. If your needs are modest and it's "good enough" to do then more power to you, but for anything non-trivial stick with AppleScript: it's the only supported solution that works right.

(The AppleScript/JXA team has no excuse for such crap work either: I sent them an almost-finished JavaScriptOSA reference implementation months ago to study or steal from as they wished, which they totally ignored. So you'll excuse my pissiness, as this was a solved problem long ago.)

like image 199
foo Avatar answered Sep 19 '22 12:09

foo