Evitare AppleScript tramite Ruby: rb-appscript o rubyosa?

Salve colleghi Mac Rubyists e AppleScript Hacker,

Per quelli di voi che hanno esperienza sia con rubyosa che con rb-appscript, mi piacerebbe sentire i pro e i contro di ciascuno, con quale si è deciso di mantenere e quale si consiglia per un esperto totalmente non AppleScript ruby veterano. Inoltre, ci sono altre opzioni che ho perso?

Per inciso, anche i suggerimenti relativi al lato AppleScript dell’equazione (ad esempio i dizionari di navigazione, ecc.) Sono i benvenuti.

Anche vedere alcuni esempi di codice aiuta molto.

Quoth kch:

È carino, ma ora sono curioso di sapere come il bridge di scripting si confronta con il applescript. Immagino che avrò un po ‘di lettura da fare.

SB omette alcune funzionalità che si trovano in AppleScript. Ad esempio, il seguente script sposta tutti i file dal desktop nella cartella Documenti:

tell application "Finder" move every file of desktop to folder "Documents" of home end tell 

In SB, la class SBElementArray limita fortemente la possibilità di applicare un singolo comando a più oggetti, quindi è necessario ricorrere all’API di basso livello oppure ottenere un elenco di singoli riferimenti di file e spostarli uno alla volta:

 require 'osx/cocoa'; include OSX require_framework 'ScriptingBridge' finder = SBApplication.applicationWithBundleIdentifier('com.apple.finder') destination = finder.home.folders.objectWithName('Documents') finder.desktop.files.get.each do |f| f.moveTo_replacing_positionedAt_routingSuppressed(destination, nil, nil, nil) end 

In rb-appscript, dovresti utilizzare lo stesso approccio di AppleScript:

 require 'appscript'; include Appscript app("Finder").desktop.files.move(:to => app.home.folders["Documents"]) 

SB nasconde il meccanismo degli eventi Apple molto più pesantemente di quanto non faccia AppleScript. AppleScript può essere difficile da capire, con la strana syntax, la tendenza a conflitti di parole chiave e simili, ma al di là del fatto che presenta in gran parte gli eventi di Apple così come sono. L’unico elemento di magia veramente significativo in AS è il suo comportamento ‘implicito ottenere’ quando valuta un riferimento letterale che non appare come parametro per un comando. Il più grande peccato di AppleScript è che la sua documentazione non spiega meglio come funziona, ma c’è un ottimo articolo di William Cook che getta molta luce su ciò che sta realmente accadendo.

L’SB, d’altra parte, è più difficile da fingere che sia una vera API Cocoa con un comportamento in stile Cocoa, quindi crea una grande quantità di magia. Il risultato è qualcosa di superficialmente attraente per gli sviluppatori di Cocoa, ma non appena queste astrazioni iniziano a perdere, come fanno sempre le astrazioni, sei completamente in mare per capire cosa sta succedendo. Ad esempio, SBElementArray afferma di essere un array – persino sottoclass NSMutableArray – ma quando si tenta effettivamente di utilizzare i suoi metodi di array, metà di essi funzionano e metà di essi no. In realtà, non è affatto una vera matrice; è un involucro attorno a un identificatore di oggetti evento Apple non valutato, fatto finta per fingere che sia un NSMutableArray. Quindi quando fa qualcosa di non-array, sei in gran parte ingannato per capire perché. E, come accennato in # 1, alcune di queste spesse astrazioni rendono difficile accedere alle funzionalità degli eventi Apple standard sottostanti.

SB innanzitutto cerca di essere una buona API Cocoa piuttosto che una buona API di eventi Apple, e finisce per non essere molto bravo in entrambi.

Appscript, per inciso, segue la guida di AppleScript e assume l’approccio opposto: fare gli eventi Apple correttamente, quindi preoccuparsi di accettare la lingua dell’host. Ecco perché alcune persone preferiscono RubyOSA su rb-appscript; mentre l’appscript è la soluzione più capace, se provi da uno sfondo fortemente orientato agli oggetti, sarà molto strano. Questo perché gli eventi di Apple utilizzano un paradigma basato su RPC-plus-query, e qualsiasi app in cui la somiglianza può avere OOP è puramente sintattica. L’analogia più vicina sarebbe quella di inviare XQueries su XML-RPC, e ci vuole un po ‘di tempo per abituarsi.

SB tende a soffrire molto più problemi di compatibilità delle applicazioni rispetto ad AppleScript.

Alcuni di questi problemi sono dovuti a Sai Baba che impone le proprie idee su come l’evento Apple IPC dovrebbe funzionare in base a come funziona effettivamente . Ad esempio, SB crea un set di [pseudo] classi proxy che rappresentano le classi definite nel dizionario; impone quindi varie restrizioni su come è ansible interagire con tali oggetti basandosi principalmente su regole comportamentali classiche orientate agli oggetti.

Ad esempio, il seguente script ottiene i nomi di tutte le sottocartelle della cartella Documenti:

 tell application "Finder" get name of every folder of entire contents of folder "Documents" of home end tell 

Se provi lo stesso approccio in SB:

 finder.home.folders.objectWithName('Documents').entireContents.folders.arrayByApplyingSelector(:name) 

arriva fino al metodo #folders, quindi genera un errore perché il tipo di proprietà ‘whole contents’ nel dizionario del Finder è dichiarato come ‘reference’. Poiché non esiste una class “di riferimento” con elementi “cartella” definiti nel dizionario, SB non consente di build quella particolare query (a meno che non si desideri accedere alle API di basso livello e utilizzare codici AE grezzi). È perfettamente legale secondo le regole degli eventi Apple, ma non si adatta alla più stretta serie di regole OO-centric imposto da SB.

Altri bug sono dovuti all’SB fare ipotesi su come le applicazioni scriptable implementeranno determinati comandi e altre funzionalità. Per esempio:

 tell application "iTunes" make new playlist with properties {name:"test 1"} end tell 

Sai Baba non ti consente di sfruttare le scorciatoie fornite da iTunes (puoi omettere il riferimento all’object di origine in cui desideri creare la playlist, nel qual caso viene utilizzata la sorgente “Libreria” principale), quindi scrivilo in completo per un confronto migliore:

 tell application "iTunes" make new playlist at source "Library" with properties {name:"test"} end tell 

In SB scriverai questo come:

 itunes = SBApplication.applicationWithBundleIdentifier('com.apple.itunes') playlists = itunes.sources.objectAtIndex(0).playlists() newplaylist = itunes.classForScriptingClass(:playlist).alloc().initWithProperties({:name => 'test'}) playlists.addObject(newplaylist) 

Tuttavia, quando lo esegui, si blocca su #addObject. Nel suo tentativo di trasformare un singolo comando ‘make’ in un esercizio a più linee, SB assume che il parametro ‘at’ sarà sempre un riferimento di forma ‘fine di di ‘, che è il modo in cui Cocoa Scripting basate su applicazioni lo fanno. Le applicazioni Carbon non hanno un unico framework standard per implementare il supporto agli eventi Apple, quindi tendono a variare un po ‘di più nelle loro esigenze. iTunes, ad esempio, si aspetta un riferimento all’object contenitore, in questo caso “Libreria” di origine “”, e non gli piace quando SB passa “alla fine delle playlist di” Libreria “di origine. È così che molte applicazioni AppleScriptable sono, ma SB ignora quella realtà nella sua determinazione ad essere “orientata agli oggetti”.

Ancora più problemi sono causati quando un dizionario di applicazioni non è preciso al 100% o esaustivo in dettaglio. Né i formati aete né sdef consentono di descrivere in che modo l’interfaccia di scripting di un’applicazione funziona con dettagli al 100%; alcune cose devono solo essere indovinate dagli utenti, o descritte nella documentazione supplementare – la natura della proprietà di ‘interi contenuti’ del Finder è un esempio. Altre informazioni, come ad esempio quali classi di oggetti possono essere elementi di cui altre classi di oggetti e qual è il tipo di ciascuna proprietà, non vengono mai effettivamente utilizzate da AppleScript stesso: è solo lì come documentazione per l’utente. Poiché AppleScript non si basa su queste informazioni, eventuali errori verranno persi durante il test del supporto di scripting dell’applicazione contro AppleScript, dal momento che gli script funzionano perfettamente nonostante ciò. Sai Baba fa uso di tali informazioni, quindi ogni refuso provocherà la mancata o l’interruzione di funzionalità che devono essere aggirate passando nuovamente alle API di basso livello.

Appscript, BTW, non è al 100% compatibile con AppleScript, ma si avvicina di molto. Le prime versioni di appscript cercavano anche di imporre varie regole OO su eventi Apple, come imporre il modello di oggetti definito dal dizionario, ma dopo un anno di incorrere in incompatibilità con le applicazioni ho ghanato tutto quel codice “intelligente” e ho passato i prossimi anni a provare a le macchinazioni interne di AppleScript per la retroingegnerizzazione di black-box e la creazione di appscript le emulano il più fedelmente ansible. “Se non riesci a sconfiggerli (che non puoi), unisciti a loro”, in altre parole. E dove appscript colpisce un problema di compatibilità, di solito ci sono dei modi per aggirarlo, tra cui sfogliare le impostazioni interne di compatibilità, esportare la terminologia dell’applicazione su un modulo, applicarlo a mano e usarlo, oppure scendere al suo codice EA grezzo di basso livello API.

FWIW, dovrei anche colbind alcuni gadget relativi all’appscript.

In primo luogo, gli strumenti ASDictionary e ASTranslate sul sito di appscript sono tuoi amici. ASDictionary esporterà i dizionari delle applicazioni in formato HTML in stile appscript e abiliterà anche il metodo #help incorporato in rb-appscript; ottimo per lo sviluppo interattivo in irb. ASTranslate prenderà un comando AppleScript e (intenzioni di bug) restituirà il comando equivalente nella syntax di appscript.

In secondo luogo, la distribuzione di origine di rb-appscript contiene sia la documentazione che gli script di esempio. Se installi la gem dell’app, ricordati di prendere anche la distribuzione zip per quelle risorse.

Terzo, Matt Neuburg ha scritto un libro su rb-appscript . Vai a leggerlo se stai pensando di usare rb-appscript. E vai a leggere il documento del dott. Cook, indipendentemente da ciò che alla fine decidi.

Comunque, spero che aiuti. (Oh, e mi scuso per la lunghezza, ma ho appena scritto circa 25000 parole questa settimana, quindi questo è solo un leggero rilassamento.)

ps Ned, il tuo lucente dollaro è nel post. 😉

Non ho provato RubyOSA, ma ho avuto un grande successo con rb-appscript . Ha funzionato perfettamente per me ed è molto più bello di lavorare direttamente con AppleScript.

Hai visto questo thread confrontando i due ? Ha una bella risposta dettagliata notando le differenze.

Apple include il supporto di scripting per le lingue compatibili con Cocoa attraverso un framework chiamato “Scripting Bridge”. Lo uso attraverso RubyCocoa / MacRuby per le mie esigenze di scripting. È incluso nella confezione, quindi è abbastanza conveniente.

 require 'osx/cocoa' require_framework 'ScriptingBridge' iTunes = SBApplication.applicationWithBundleIdentifier 'com.apple.iTunes' puts iTunes.selection.name 

L’unica grande seccatura che ho trovato su Scripting Bridge è che devi usare gli ID bundle come quello piuttosto che i nomi, ma non è comunque un gran problema per me. Inoltre è incluso solo in 10.5, quindi se hai bisogno del supporto Panther o Tiger, avrai bisogno di uno degli altri.

Degli altri due, rb-appscript è ancora triggersmente sviluppato mentre RubyOSA è stato effettivamente congelato un paio di anni fa, quindi probabilmente andrei con il primo. Poiché Ruby 2, MacRuby e altre nuove implementazioni determinano cambiamenti nel linguaggio, rb-appscript ha maggiori probabilità di funzionare in futuro. Altrimenti sono abbastanza simili. In sostanza, considero rb-appscript come una nuova versione di RubyOSA, anche se non è tecnicamente vero.

Risposta breve: rb-appscript.

Perché Scripting Bridge sembra essere un casino e RubyOSA è stato interrotto.