ruby macos applescript rb-appscript rubyosa

¿Evitar AppleScript a través de Ruby: rb-appscript o rubyosa?



macos (4)

Hola compañeros rubistas de Mac y odiadores de AppleScript,

Para aquellos de ustedes que tienen experiencia con rubyosa y rb-appscript, me gustaría escuchar los pros y los contras de cada uno, con cuál decidieron seguir, y cuál recomendarían para un experto que no sea AppleScript. Rubí veterano. Además, ¿hay alguna otra opción que me haya perdido?

Además, cualquier sugerencia relacionada con el lado de AppleScript de la ecuación (por ejemplo, navegando en diccionarios, etc.) también es bienvenida.

Ver algunos ejemplos de código también ayuda mucho.


Apple incluye soporte de scripting para lenguajes compatibles con Cocoa a través de un marco llamado "Scripting Bridge". Lo uso a través de RubyCocoa / MacRuby para mis necesidades de scripting. Está incluido justo en la caja, así que es bastante conveniente.

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

La única gran molestia que he encontrado en el Scripting Bridge es que tienes que usar las ID de los paquetes de esa manera en lugar de los nombres, pero de todas formas eso no es un gran problema para mí. También se incluye solo en 10.5, por lo que si necesita soporte de Panther o Tiger, necesitará uno de los otros.

De los otros dos, rb-appscript todavía se desarrolla activamente, mientras que RubyOSA se congeló efectivamente hace un par de años, por lo que probablemente iría con el primero. Como Ruby 2, MacRuby y otras implementaciones nuevas producen cambios en el lenguaje, es más probable que rb-appscript funcione en el futuro. De lo contrario son bastante similares. Básicamente, trato a rb-appscript como una nueva revisión de RubyOSA, aunque no es técnicamente cierto.


No he probado RubyOSA, pero he tenido un gran éxito con rb-appscript . Ha funcionado perfectamente para mí y es mucho mejor que trabajar con AppleScript directamente.

¿Has visto este hilo comparando los dos ? Tiene una buena respuesta detallada notando las diferencias.


Quch kch:

Eso es bueno, pero ahora tengo curiosidad acerca de cómo el puente de scripting se compara con applecript. Supongo que tendré algo de lectura que hacer.

SB omite alguna funcionalidad encontrada en AppleScript. Por ejemplo, la siguiente secuencia de comandos mueve todos los archivos del escritorio a la carpeta Documentos:

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

En SB, la clase SBElementArray restringe severamente su capacidad para aplicar un solo comando a múltiples objetos, por lo que debe recurrir a la API de bajo nivel o bien obtener una lista de referencias de archivos individuales y moverlas de una en una:

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

En rb-appscript, utilizarías el mismo enfoque que AppleScript:

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

...

SB ofusca el mecanismo de eventos de Apple mucho más fuertemente que AppleScript. AppleScript puede ser una molestia para entenderlo, con la sintaxis extraña, la tendencia a los conflictos de palabras clave, etc., pero más allá de eso, en gran parte presenta los eventos de Apple como están. La única pieza realmente importante de magia en AS es su comportamiento de ''obtención implícita'' cuando evalúa una referencia literal que no aparece como un parámetro para un comando. El mayor pecado de AppleScript es que su documentación no explica mejor cómo funciona realmente, pero hay un muy buen documento de William Cook que arroja mucha luz sobre lo que realmente está sucediendo.

SB, por otro lado, hace todo lo posible por fingir que se trata de una API de Cocoa genuina con un comportamiento de estilo Cocoa, por lo que se acumula una gran cantidad de magia. El resultado es algo superficialmente atractivo para los desarrolladores de Cocoa, pero tan pronto como esas abstracciones comienzan a filtrarse, como las abstracciones invariablemente lo hacen, está completamente en el mar en términos de entender lo que está pasando. Por ejemplo, SBElementArray afirma ser una matriz, incluso subclases NSMutableArray, pero cuando realmente intenta usar sus métodos de matriz, la mitad de ellos funciona y la mitad de ellos no. De hecho, no es una matriz real en absoluto; es una envoltura alrededor de un especificador de objeto de evento de Apple no evaluado, fingido para fingir que es un NSMutableArray. Entonces, cuando hace algo similar a una matriz, estás en gran parte disecado para entender por qué. Y, como se mencionó en el número 1, algunas de estas abstracciones densas dificultan el acceso a la funcionalidad estándar de eventos de Apple que se encuentra debajo.

SB, en primer lugar, trata de ser una buena API de Cocoa en lugar de una buena API de eventos de Apple, y termina no siendo muy buena tampoco.

Incidentalmente, Appscript sigue el ejemplo de AppleScript y adopta el enfoque opuesto: haga los eventos de Apple correctamente y luego se preocupe por acomodar el lenguaje principal. Es por eso que algunas personas prefieren RubyOSA sobre rb-appscript; Si bien Appscript es la solución más capaz, si viene de un fondo muy orientado a objetos, se sentirá muy extraño. Esto se debe a que los eventos de Apple utilizan un paradigma basado en RPC-plus-query, y cualquier appcript parecido a OOP es puramente sintáctico. La analogía más cercana sería enviar XQueries a través de XML-RPC, y lleva un tiempo acostumbrarse.

...

SB tiende a sufrir significativamente más problemas de compatibilidad de aplicaciones que AppleScript.

Algunos de estos problemas se deben a que SB impone sus propias ideas sobre cómo debería funcionar el evento IPC de Apple, además de cómo funciona realmente . Por ejemplo, SB crea un conjunto de [pseudo] clases de proxy que representan las clases definidas en el diccionario; luego impone varias restricciones sobre cómo puede interactuar con esos objetos en gran medida basándose en reglas de comportamiento clásicas orientadas a objetos.

Por ejemplo, la siguiente secuencia de comandos obtiene los nombres de todas las subcarpetas de la carpeta Documentos:

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

Si intentas el mismo enfoque en SB:

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

llega hasta el método de las # carpetas, luego arroja un error porque el tipo de propiedad ''contenido completo'' en el diccionario del Finder se declara como ''referencia''. Dado que no hay una clase de ''referencia'' con los elementos de ''carpeta'' definidos en el diccionario, SB no le permite crear esa consulta en particular (a menos que desee ir a las API de bajo nivel y usar códigos AE sin procesar). Es perfectamente legal de acuerdo con las reglas de eventos de Apple, pero no encaja dentro del conjunto de reglas más estrecho centrado en OO impuesto por SB.

Otros errores se deben a que SB hace suposiciones sobre cómo las aplicaciones de secuencias de comandos implementarán ciertos comandos y otras características. Por ejemplo:

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

Sin embargo, SB no te permite aprovechar los accesos directos proporcionados por iTunes (puedes omitir la referencia al objeto fuente en el que quieres que se cree la lista de reproducción, en cuyo caso se usa la fuente principal de "Biblioteca"), así que escríbelo en Completo para una mejor comparación:

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

En SB escribirías esto como:

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

Cuando lo ejecutas, barfs en #addObject. En su intento de convertir un solo comando ''make'' en un ejercicio multilínea, SB asume que el parámetro ''at'' siempre será una referencia de la forma ''end of <elements> of <object>'', que es como Cocoa Scripting Las aplicaciones basadas en aplicaciones lo hacen. Sin embargo, las aplicaciones de Carbon no tienen un marco estándar único para implementar el soporte de eventos de Apple, por lo que tienden a variar un poco más en sus requisitos. iTunes, por ejemplo, espera una referencia al objeto contenedor, en este caso ''source "Library"'', y no le gusta cuando SB pasa el final de las listas de reproducción de source "Library" ''. Así es como son muchas las aplicaciones de AppleScriptable, pero SB ignora esa realidad en su determinación de ser "orientada a objetos".

Sin embargo, se producen más problemas cuando un diccionario de aplicación no es 100% preciso o exhaustivo en detalle. Ni los formatos aete ni sdef le permiten describir cómo funciona la interfaz de scripts de una aplicación con un 100% de detalle; Algunas cosas solo deben ser adivinadas por los usuarios o descritas en la documentación complementaria: la naturaleza de la propiedad de "contenido completo" de Finder es un ejemplo. Otra información, como qué clases de objetos pueden ser elementos de qué otras clases de objetos y cuál es el tipo de cada propiedad, nunca es utilizada por AppleScript, sino que está únicamente allí como documentación del usuario. Dado que AppleScript no se basa en esta información, se perderán todos los errores al probar la compatibilidad de los scripts de la aplicación con AppleScript, ya que los scripts funcionan bien a pesar de ello. SB emplea esa información, por lo que cualquier error tipográfico allí resultará en características faltantes o rotas que deben ser evitadas al caer de nuevo a las API de bajo nivel.

Appscript, BTW, tampoco es 100% compatible con AppleScript, pero se acerca mucho más. Las primeras versiones de appscript también intentaron imponer varias reglas de OO en los eventos de Apple, como imponer el modelo de objeto definido en el diccionario, pero después de un año de encontrar incompatibilidades con las aplicaciones, obtuve todo el código "inteligente" y pasé los siguientes años intentando black-box ingrese las maquinaciones internas de AppleScript y haga que Appscript las emule lo más cerca posible. "Si no puedes vencerlos (lo que no puedes), únete a ellos", en otras palabras. Y donde Appscript encuentra un problema de compatibilidad, por lo general hay formas de evitarlo, como cambiar la configuración de compatibilidad interna, exportar la terminología de la aplicación a un módulo, parchearlo a mano y usarlo en su lugar, o bajar a su código AE sin procesar de bajo nivel. APIs.

...

FWIW, también debo conectar algunas golosinas relacionadas con appscript.

Primero, las herramientas ASDictionary y ASTranslate en el sitio appscript son tus amigos. ASDictionary exportará diccionarios de aplicaciones en formato HTML al estilo de scripts y también habilita el método integrado de ayuda en rb-appscript; Ideal para desarrollo interactivo en irb. ASTranslate tomará un comando de AppleScript y (errores dispuestos) devolverá el comando equivalente en la sintaxis de appscript.

En segundo lugar, la distribución de origen de rb-appscript contiene documentación y scripts de muestra. Si instala la gema de appscript, recuerde también agarrar la distribución zip de esos recursos.

En tercer lugar, Matt Neuburg ha escrito un libro sobre rb-appscript . Ve a leerlo si estás pensando en usar rb-appscript. Y ve a leer el artículo del Dr. Cook, independientemente de lo que finalmente decidas.

...

De todos modos, espero que ayude. (Ah, y disculpas por la longitud, pero acabo de escribir unas 25000 palabras esta semana, así que esto es solo un poco de relajación ligera).

ps ned, tu dólar brillante esta en el post ;)


Respuesta corta: rb-appscript.

Debido a que Scripting Bridge parece ser un desastre y RubyOSA ha sido descontinuado.