16

大家好,Mac ruby​​ists 和 AppleScript 仇恨者,

对于那些同时使用 ruby​​osa 和 rb-appscript 的人,我想听听它们各自的优缺点,你决定坚持使用哪一个,以及你会推荐哪一个来完全不了解 AppleScript红宝石老前辈。另外,还有其他我错过的选择吗?

顺便说一句,也欢迎任何处理等式的 AppleScript 方面的技巧(例如浏览字典等)。

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

4

4 回答 4

47

知识库:

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

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

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 可能会让您难以理解,奇怪的语法,关键字冲突的倾向等等,但除此之外,它主要按原样呈现 Apple 事件。AS 中唯一真正重要的魔法是它的“隐式获取”行为,当它评估一个不作为命令参数出现的文字引用时。AppleScript 最大的罪过是它的文档没有更好地解释它的实际工作原理,但是William Cook 的一篇非常好的论文对实际发生的事情提供了很多启示。

另一方面,SB 最难假装它是具有 Cocoa 风格行为的真正 Cocoa API,因此层层叠加了大量的魔法。结果是表面上吸引了 Cocoa 开发人员,但一旦这些抽象开始泄漏 - 正如抽象总是如此 - 你就完全无法理解正在发生的事情。例如,SBElementArray 声称是一个数组——它甚至是 NSMutableArray 的子类——但是当你真正尝试使用它的数组方法时,其中一半有效,一半无效。事实上,它根本不是一个真正的数组。它是一个未评估的 Apple 事件对象说明符的包装器,假装它是一个 NSMutableArray。因此,当它执行类似非数组的操作时,您在很大程度上无法理解为什么。而且,正如#1 中提到的,

SB 首先尝试成为一个好的 Cocoa API,而不是一个好的 Apple 事件 API,但最终都不是很擅长。

顺便说一下,Appscript 遵循 AppleScript 的领导并采取了相反的方法:正确处理 Apple 事件,然后担心适应宿主语言。这就是为什么有些人更喜欢 RubyOSA 而不是 rb-appscript;虽然 appscript 是功能更强大的解决方案,但如果您有大量面向对象的背景,那会感觉很奇怪。这是因为 Apple 事件使用基于 RPC-plus-query 的范例,并且任何与 OOP 相似的 appscript 可能都是纯语法。最接近的类比是通过 XML-RPC 发送 XQueries,这需要一些时间来适应。

...

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

其中一些问题是由于 SB 强加了自己的想法,即 Apple 事件 IPC应该如何在其实际工作方式之上工作。例如,SB 创建了一组 [pseudo] 代理类,表示字典中定义的类;然后,它主要基于经典的面向对象的行为规则对您如何与这些对象进行交互施加了各种限制。

例如,以下脚本获取 Documents 文件夹的所有子文件夹的名称:

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 字典中的“整个内容”属性的类型被声明为“引用”。由于字典中没有定义“文件夹”元素的“参考”类,SB 不允许您构建特定查询(除非您想下拉到低级 API 并使用原始 AE 代码)。根据 Apple 事件规则,这是完全合法的,但不符合 SB 强加的更狭窄的以 OO 为中心的规则集。

其他错误是由于 SB 假设可编写脚本的应用程序将如何实现某些命令和其他功能。例如:

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

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

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 上出错。在尝试将单个“make”命令转换为多行练习时,SB 假设“at”参数将始终是表单“<object> <elements> 的结尾”的引用,这就是 Cocoa Scripting基于应用程序可以做到这一点。但是,Carbon 应用程序没有用于实现 Apple 事件支持的单一标准框架,因此它们的要求往往会有所不同。例如,iTunes 需要对容器对象的引用,在本例中为“源“库””,并且当 SB 传递“源“库”的播放列表结束”时不喜欢它。很多 AppleScriptable 应用程序就是这样,但 SB 在决心成为“面向对象”时忽略了这一现实。

然而,当应用程序字典不是 100% 准确或详尽详尽时,会导致更多问题。aete 和 sdef 格式都不允许您 100% 详细地描述应用程序的脚本接口是如何工作的;有些事情只需要用户猜测,或者在补充文档中描述 - Finder 的“整个内容”属性的性质就是一个例子。其他信息,例如哪些对象类可以是哪些其他对象类的元素,以及每个属性的类型是什么,AppleScript 本身从未实际使用过——它仅作为用户文档存在。由于 AppleScript 不依赖此信息,因此在针对 AppleScript 测试应用程序的脚本支持时会遗漏任何错误,因为尽管如此,脚本仍然可以正常工作。

顺便说一句,Appscript 也不是 100% 的“AppleScript 兼容”,但它确实更接近了。appscript 的早期版本还试图对 Apple 事件施加各种 OO 规则,例如强制执行字典定义的对象模型,但在遇到应用程序不兼容一年后,我收集了所有“聪明”的代码,并在接下来的几年里试图黑盒对 AppleScript 的内部机制进行逆向工程,并使 appscript 尽可能地模拟它们。换句话说,“如果你不能打败他们(你不能),那就加入他们”。在 appscript 确实遇到兼容性问题的地方,通常有一些方法可以解决它,包括翻转内部兼容性设置、将应用程序术语导出到模块、手动修补它,然后改用它,

...

FWIW,我还应该插入一些相关的 appscript 好东西。

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

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

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

...

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

ps Ned,你闪亮的美元在帖子里。;)

于 2009-08-22T17:47:40.727 回答
5

我没有尝试过 RubyOSA,但我在rb-appscript方面取得了巨大成功。它对我来说非常有效,而且比直接使用 AppleScript 要好得多。

你看过这个帖子比较两者吗?它有一个很好的详细答案,指出了差异。

于 2009-08-21T16:31:12.280 回答
3

Apple 通过称为“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 的更新版本,尽管它在技术上并不正确。

于 2009-08-21T16:11:04.910 回答
2

简短的回答:rb-appscript。

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

于 2009-12-04T22:29:27.870 回答