通过Ruby避免AppleScript:rb-appscript或rubyosa?

你好同胞的Mac ruby​​ists和AppleScript的仇敌,

对于那些对rubyosa和rb-appscript有经验的人,我想听听每个人的优缺点,你决定坚持哪一个,哪一个你会推荐一个完全不是AppleScript的人ruby老前辈。 还有,我还有其他的select吗?

另外,任何处理AppleScript方面的提示(例如浏览字典等)也是受欢迎的。

看到一些示例代码也有很大帮助。

Quoth kch:

这很好,但现在我很好奇脚本桥比较applescript。 我想我会做一些阅读。

SB省略了AppleScript中的一些function。 例如,以下脚本将所有文件从桌面移动到“文档”文件夹:

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

在SB中,SBElementArray类严格限制了将单个命令应用于多个对象的能力,因此您必须使用低级API或者获取单个文件引用的列表,然后逐个移动它们:

 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 

在rb-appscript中,您将使用与AppleScript相同的方法:

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

SB比AppleScript更严重地混淆了Apple事件机制。 AppleScript可能会让你头痛,使用奇怪的语法,关键字冲突的倾向等,但除此之外,苹果事件大部分是按原样呈现的。 AS中唯一真正重要的一点就是当它评估一个不作为命令参数的字面引用时,它的“隐式get”行为。 AppleScript最大的罪过是它的文档并没有更好地解释它的实际工作方式,但William Cook有一篇非常好的论文,对实际发生的事情做了大量的阐述。

另一方面,SB最难假装它是一个真正的cocoa风格的Cocoa API,所以层层叠叠,具有很大的魔力。 其结果是对cocoa开发人员表面上的吸引力,但是一旦这些抽象开始泄漏 – 就像抽象总是这样 – 你完全了解正在发生的事情。 例如,SBElementArray声称是一个数组 – 它甚至是NSMutableArray的子​​类 – 但是当你真正尝试使用它的数组方法时,其中一半工作,一半不工作。 事实上,它根本不是一个真正的arrays; 它是一个未经评估的Apple事件对象说明符的包装,假装它是一个NSMutableArray。 所以当它做一些非数组的时候,你基本上都会理解为什么。 而且,正如#1中所提到的,其中一些粗糙的抽象使得难以访问下面的标准Apple事件function。

SB首先试图成为一个好的Cocoa API,而不是一个好的Apple事件API,并且最终也不是很好。

顺便说一句,Appscript遵循AppleScript的主导思想,采取相反的方法:正确地做苹果事件,然后担心适应主机语言。 这就是为什么有些人更喜欢RubyOSA而不是rb-appscript; 而appscript是更强大的解决scheme,如果你是来自面向对象的背景,它会感觉很奇怪。 这是因为Apple事件使用基于RPC加查询的范例,并且任何相似的appscript可能必须是OOP纯粹是句法。 最接近的比喻是通过XML-RPC发送XQueries,并且需要一些习惯。

SB比AppleScript倾向于遭受更多的应用程序兼容性问题。

其中一些问题是由于SB强加自己的想法,即苹果事件IPC 应该如何工作。 例如,SB创build一组代表字典中定义的类的[pseudo]代理类; 然后在很大程度上基于经典的面向对象行为规则的基础上,对如何与这些对象进行交互的方式施加各种限制。

例如,以下脚本将获取“文档”文件夹的所有子文件夹的名称:

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

如果您在SB中尝试相同的方法:

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

它会得到#folders方法,然后抛出一个错误,因为Finder字典中“全部内容”属性的types被声明为“引用”。 由于在字典中没有定义“文件夹”元素的“引用”类,所以SB不允许你构造特定的查询(除非你想下拉到低级API并使用原始AE代码)。 根据苹果公司的事件规则,这是完全合法的,但不符合SB规定的较窄OO中心规则。

其他错误是由于SB作出关于如何脚本化应用程序将如何实现某些命令和其他function的假设。 例如:

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

SB不允许你利用iTunes提供的任何快捷方式(你可以省略你想要创build播放列表的源对象的引用,在这种情况下使用主“库”源),所以让我们把它写入完整的比较:

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

在SB中,你可以这样写:

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

当你运行它的时候,它会在#addObject上创buildbarf。 在试图把一个make命令变成一个多行的练习中,SB假设'at'参数将始终是<object>的<end>元素的forms的引用,这就是Cocoa脚本基于应用程序做到这一点。 碳应用程序虽然没有实现苹果事件支持的单一标准框架,但它们的要求往往会有所不同。 例如,iTunes期望对容器对象(在这种情况下为“源”库“”)的引用,并且在SB通过源“库”的播放列表的末尾时不期望引用该容器对象。 这就是苹果应用程序的许多应用程序,但是SB在“面向对象”的决心中忽略了这个现实。

但是,当应用程序字典不是100%准确或详尽无遗时,会造成更多的问题。 aete和sdef格式都不能让你描述一个应用程序的脚本接口如何以100%的细节工作。 有些事情只能由用户猜测,或者在补充文档中描述 – Finder的“全部内容”属性就是一个例子。 其他信息,比如哪些类的对象可以是其他类对象的元素,以及每个属性的types,AppleScript本身并不会实际使用这些信息 – 它仅作为用户文档存在。 由于AppleScript不依赖于这些信息,因此在testing应用程序对AppleScript的脚本支持时,将会错过任何错误,因为即使脚本工作得很好,脚本也能正常工作。 SB确实使用了这些信息,所以任何input错误都会导致丢失或破坏的特性,这些特性必须通过再次下降到低级API来避免。

应用程序脚本,BTW,也不是100%的“AppleScript兼容”,但它确实是非常接近。 早期版本的appscript也试图在苹果事件上强加各种面向对象的规则,比如强制实施字典定义的对象模型,但是在经历了一年的应用程序不兼容问题之后,我放弃了所有那些“聪明”的代码,并在接下来的几年中尝试黑盒子反向工程AppleScript的内部诡计,并使appscript尽可能地模拟它们。 “换句话说,如果你不能击败他们(你不能),那就join他们吧。 在appscript确实遇到了兼容性问题的时候,通常会有解决方法,包括翻转内部兼容性设置,将应用程序术语导出到模块,手动修补,然后使用它,或者直接下载到低级的原始AE代码蜜蜂。

FWIW,我也应该插入一些相关的应用程序的好东西。

首先,appscript网站上的ASDictionary和ASTranslate工具是你的朋友。 ASDictionary将以appscript风格的HTML格式导出应用程序字典,并启用rb-appscript中的内置的#help方法; 非常适合irb的互动发展。 ASTranslate将采取AppleScript命令,并且(错误愿意)以appscript语法返回相应的命令。

其次,rb-appscript的源代码包含文档和示例脚本。 如果您安装了appscript gem,请记住也要获取这些资源的zip分配。

第三, Matt Neuburg写了一本关于rb-appscript的书 。 如果您正在考虑使用rb-appscript,请阅读它。 去读库克博士的论文,不pipe你最终决定什么。

无论如何,希望有所帮助。 (哦,并且表示歉意,但本周我刚刚写了大约25000字,所以这只是一些轻松的放松。)

ps奈德,你shiny的美元在后。 ;)

我还没有尝试RubyOSA,但我已经与rb-appscript取得了巨大的成功。 它对我来说非常合适,比直接使用AppleScript更好。

你看过这个线程比较两个 ? 它有一个很好的详细答案,注意到不同之处。

苹果公司通过一个名为“Scripting Bridge”的框架,为Cocoa兼容语言提供脚本支持。 我通过RubyCocoa / MacRuby使用它来满足我的脚本需求。 它包含在盒子里,所以很方便。

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

我发现Scripting Bridge的唯一主要烦恼是你必须使用类似于名称的包ID,但是对于我来说这不是一个真正的问题。 它也只包含在10.5中,所以如果你需要Panther或Tiger的支持,你将需要其他人之一。

另外两个,rb-appscript仍然在积极开发,而RubyOSA在几年前被有效地冻结了,所以我可能会和前者一起。 随着Ruby 2,MacRuby和其他新的实现带来语言的变化,rb-appscript更有可能在未来工作。 否则,他们非常相似。 我基本上把rb-appscript看作是RubyOSA的一个更新版本,尽pipe这在技术上不是这样。

简短的回答:rb-appscript。

因为Scripting Bridge似乎是一团糟,RubyOSA已经停产。