你好同胞的Mac rubyists和AppleScript的仇敵,通過Ruby避免AppleScript:rb-appscript或rubyosa?
對於那些有rubyosa和rb-appscript的經驗的人,我想聽聽每個人的利弊,你決定堅持哪一個,以及哪一個你會推薦完全不是AppleScript精明的紅寶石老前輩。另外,還有其他的選擇我錯過了嗎?另外,任何涉及AppleScript方面的提示(例如瀏覽字典等)也是受歡迎的。
看到一些示例代碼也有很大幫助。
你好同胞的Mac rubyists和AppleScript的仇敵,通過Ruby避免AppleScript:rb-appscript或rubyosa?
對於那些有rubyosa和rb-appscript的經驗的人,我想聽聽每個人的利弊,你決定堅持哪一個,以及哪一個你會推薦完全不是AppleScript精明的紅寶石老前輩。另外,還有其他的選擇我錯過了嗎?另外,任何涉及AppleScript方面的提示(例如瀏覽字典等)也是受歡迎的。
看到一些示例代碼也有很大幫助。
答曰KCH:
這很好,但現在我很好奇 如何腳本橋比較 的AppleScript。我想我會有一些 閱讀。
SB省略了AppleScript的發現了一些功能。例如,下面的腳本從桌面移動所有文件到文件夾:
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的確混淆了蘋果事件機制。AppleScript可能會讓你頭痛,使用奇怪的語法,關鍵字衝突的傾向等等,但除此之外,它主要是將Apple事件按原樣呈現出來。 AS中唯一真正重要的魔法是當它評估一個不作爲命令參數的字面引用時的「隱式get」行爲。 AppleScript最大的罪孽是它的文檔並沒有更好地解釋它的實際工作方式,但有一個very good paper by William Cook可以說明實際發生的事情。另一方面,SB會盡其最大的努力假裝它是一個真正的可可風格的Cocoa API,因此層層疊加了大量的魔力。其結果是對可可開發人員表面上的吸引力,但是一旦這些抽象開始泄漏 - 就像抽象總是這樣 - 你就完全瞭解正在發生的事情。例如,SBElementArray聲稱是一個數組 - 它甚至是NSMutableArray的子類 - 但是當你真正嘗試使用它的數組方法時,其中一半工作,一半不工作。事實上,它根本不是真正的陣列;它是一個未評估的Apple事件對象說明符的包裝,假裝它是一個NSMutableArray。所以當它做了一些非數組類的時候,你基本上可以理解爲什麼。而且,正如#1所提到的,這些粗略的抽象使得難以訪問下面的標準Apple事件功能。
SB最先試圖成爲一個好的Cocoa API而不是一個好的Apple事件API,並且最終不是很好。
Appscript順便說一句,遵循AppleScript的主導思想,採取相反的做法:蘋果事件正確,然後擔心適應主機語言。這就是爲什麼有些人更喜歡RubyOSA而不是rb-appscript;而appscript是更強大的解決方案,如果你來自面向對象的背景,它會感覺很奇怪。這是因爲Apple事件使用基於RPC加查詢的範例,而任何類似的appscript可能不得不使用OOP純粹是句法。最接近的比喻是通過XML-RPC發送XQueries,並且需要一些習慣。
...
SB往往承受着比其他的AppleScript顯著多個應用程序兼容性問題。
這些問題有些是由於SB強加了自己的蘋果如何事件IPC 應該就如何實際上作品的最高工作思路。例如,SB創建一組代表字典中定義的類的[僞]代理類;然後在很大程度上基於傳統的面向對象行爲規則的基礎上,對如何與這些對象進行交互設置各種限制。
例如,下面的腳本得到的文檔文件夾的所有子文件夾的名稱:
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字典中的'entire contents'屬性的類型被聲明爲'reference'。由於在字典中沒有定義'文件夾'元素的'引用'類,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)
當你雖然運行它,它barfs上#addObject 。在試圖將單個「make」命令變成多行練習時,SB假定'at'參數始終是形式'<元素末尾> < object>'的引用,這就是Cocoa腳本基於應用程序來做到這一點。 Carbon應用程序雖然沒有用於實現Apple事件支持的單一標準框架,所以它們的要求往往會有所不同。例如,iTunes期望對容器對象的引用,在這種情況下是「源」庫「',並且當SB通過源」庫「的播放列表的末尾時不期望引用該容器對象。這就是許多AppleScriptable應用程序的方式,但SB在其「面向對象」的決心中忽視了這一現實。
當應用程序字典不是100%準確或詳盡無遺時,會導致更多問題。 aete和sdef格式都不能讓你描述應用程序的腳本接口如何以100%的細節工作;有些事情只能由用戶猜測,或者在補充文檔中描述 - Finder的「全部內容」屬性的性質就是一個例子。其他信息,比如哪些類的對象可以是其他類對象的元素,以及每個屬性的類型,AppleScript本身從未實際使用過 - 它僅作爲用戶文檔存在。由於AppleScript不依賴這些信息,因此在測試應用程序對AppleScript的腳本支持時會漏掉任何錯誤,因爲腳本儘管如此,仍然可以正常工作。 SB會使用這些信息,因此任何錯誤都會導致丟失或損壞的功能,這些功能必須通過再次下降到低級API來規避。
Appscript,BTW,也不是100%'AppleScript兼容',但它確實離得更近了。早期版本的appscript也嘗試在Apple事件上實施各種面向對象的規則,比如強制實施字典定義的對象模型,但是在經歷了一年的應用程序不兼容問題之後,我放棄了所有那些「聰明」的代碼,並在接下來的幾年嘗試黑盒子反向工程AppleScript的內部機械裝置,並且使得appscript儘可能地模擬它們。 「換句話說,如果你不能擊敗他們(你不能),那就加入他們吧。在appscript確實遇到了兼容性問題的地方,通常會有解決方法,包括翻轉內部兼容性設置,將應用程序術語導出到模塊,手工打補丁,然後使用它,或者下降到低級的原始AE代碼蜜蜂。
...
FWIW,我也插了幾個相關appscript東西。
首先,appscript網站上的ASDictionary和ASTranslate工具是您的朋友。 ASDictionary將以appscript風格的HTML格式導出應用程序字典,並啓用rb-appscript中的內置#help方法;非常適合irb的互動開發。 ASTranslate將採用AppleScript命令,並且(bugs willing)會以appscript語法返回相應的命令。
其次,rb-appscript的源代碼包含文檔和示例腳本。如果您安裝了appscript gem,請記住也要獲取這些資源的zip分配。
三,Matt Neuburg has written a book about rb-appscript。如果你正在考慮使用rb-appscript,請閱讀它。不管你最終決定什麼,去閱讀庫克博士的論文。
...
反正,希望有所幫助。 (哦,並且對長度表示歉意,但本週我剛剛寫了大約25000字,所以這只是一些輕鬆的放鬆。)
p.s.內德,你閃亮的美元在後。 ;)
我並不清楚「每個文件」和「files.each」之間的區別 - 他們是否都將所有文件從一個位置迭代移動到另一個位置? – Chuck 2009-08-22 21:22:15
區別在於誰在迭代。隨着files.each,你發送一個單獨的Apple事件來操縱每個元素。使用「每個文件」,您只需嚮應用程序發送一個單一事件,即「處理所有文件即可」。 Apple事件IPC最初是爲系統7設計的,其中上下文切換非常昂貴。爲了彌補,AppleScript設計師允許您爲每個事件執行多個操作。儘管AE IPC在OS X上的價格要便宜得多,但它仍然是優化的,因此使用更少的事件會更有效。如果您有數百個對象需要處理,這會產生變化。 – has 2009-08-23 08:48:07
這個答案應該刻在石頭上,或者至少變成一篇博客文章。 – dmkc 2010-07-16 02:58:55
Apple通過一個名爲「Scripting Bridge」的框架支持Cocoa兼容語言的腳本支持。我通過RubyCocoa/MacRuby使用它來滿足我的腳本需求。它包含在盒子裏,所以很方便。
require 'osx/cocoa'
require_framework 'ScriptingBridge'
iTunes = SBApplication.applicationWithBundleIdentifier 'com.apple.iTunes'
puts iTunes.selection.name
唯一的主要煩惱我發現腳本大橋是,你必須使用的包ID一樣,而不是名稱,但這是不是真的太大的問題,對我來說反正。它也只包含在10.5中,所以如果你需要Panther或Tiger支持,你需要其他的支持。
其他兩個,RB-appscript仍積極發展,同時RubyOSA得到有效幾年前凍結,所以我可能會與前走。隨着Ruby 2,MacRuby和其他新實現帶來語言的變化,rb-appscript更有可能在未來發揮作用。否則他們非常相似。我基本上把rb-appscript看作是RubyOSA的一個新版本,儘管它在技術上並不真實。
我沒有試過RubyOSA,但我已經對rb-appscript取得了巨大成功。它對我來說非常完美,比直接使用AppleScript更好。
你見過this thread comparing the two?它有一個很好的詳細答案,注意不同之處。
偉大的鏈接。摩爾閱讀。 – kch 2009-08-21 21:13:33
使用rb-appscript。 rb-appscript的開發者是AppleScript和底層OSA架構的全球專家之一。 rb-appscript建立在他多年的經驗之上,包括原始的Python版本的appscript。他提供的支持非常出色。 – 2009-08-21 21:44:14
請注意,**'rb-appscript'是** [**不再支持**,不建議用於新項目](http://appscript.sourceforge.net/status.html)。 – 2012-03-13 06:30:43
爲什麼不使用腳本橋? – Chuck 2009-08-21 04:52:17
你可以擴展嗎? – kch 2009-08-21 05:40:22