Home>History>

2009.11

2009-11-28

2009-11-28T17:49:01+09:00

AppleScript のスクリプトオブジェクト入門(その3)依存性のあるスクリプトオブジェクトを読み込む場合

前回までの復習です。

  • テキストエディットで選択されている箇所を取得/置き換えるスクリプトを紹介しました。
  • テキストエディットに関するハンドラを独立したスクリプトとしてまとめました。
  • 別ファイルとしてファイルに保存したスクリプトを load script コマンドで読み込むことができます。
  • でも、load script コマンドはファイルへの参照を与えなければいけないので大変です。
  • ModuleLoader を使えば、名前を指定するだけで読み込めます。

このように、他のファイルからロードされて、色んなスクリプトで共有されるスクリプトのことをモジュールと呼ぶことにしましょう。今回は、モジュールが他のモジュールに依存している場合のことを考えます。

まず、テキストの検索置換を行うハンドラもモジュールとしてトップレベルのスクリプトから外部に追い出します。こんな感じで。

on replace_subtext(a_text, target_text, replace_text)
set delim to AppleScript's text item delimiters
set AppleScript's text item delimiters to target_text
set a_list to every text item of a_text
set AppleScript's text item delimiters to replace_text
set a_result to a_list as text
set AppleScript's text item delimiters to delim
return a_result
end replace_subtext

このスクリプトを ~/Library/Scripts/Modules/StringEngine.scpt に保存することとします。サンプルだからハンドラが一つしか無いけど、その辺はご勘弁。AppleScript でのテキスト処理はいろいろ面倒なことが多い。テキスト処理のハンドラをまとめたスクリプトを一つつくっておくと何かと便利だと思いますよ。そんな時、拙作 XText をご参考頂ければと思います。

さて、この StringHandlers をテキストエディットに関するスクリプトオブジェクト TextEditHandlers とトップレベルのスクリプトの両方でロードする場合を考えます。

まず、TextEditHandlers はこんな感じになります。replace_in_selection ハンドラを付け加えました。

property ModuleLocation : (path to scripts folder from user domain as text) & "Modules:"
property StringHandlers : load script alias (ModuleLocation & "StringHandlers.scpt")


on replace_in_selection(old, new)
set a_text to selected_text()
set a_text to StringHandlers's replace_subtext(a_text, old, new)
set_selection(a_text)
end replace_in_selection

on selected_text() -- 選択されているテキストの取得
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
return value of attribute "AXSelectedText"
end tell
end tell
end tell
end selected_text

on set_selection(a_text) -- 選択されているテキストを置き換える。
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
set value of attribute "AXSelectedText" to a_text
end tell
end tell
end tell
end set_selection

トップレベルスクリプトは、こんな感じで、StringHandlers と TextEditHandlers の両方をロードすることにします。

property ModuleLocation : (path to scripts folder from user domain as text) & "Modules:"
property TextEditHandlers : load script alias (ModuleLocation & "TextEditHandlers.scpt")
property StringHandlers : load script alias (ModuleLocation & "StringHandlers.scpt")
on run
TextEditHandlers's replace_in_selection("あ", "い")
log StringHandlers's replace_subtext("This is a pen.", "This", "It")
end run

さて、load script に完全なファイル参照を与えなければいけないという問題は置いておいて、このように別のモジュールをロードしているモジュールが読み込む場合、次のようは問題があります。

  • StringHandlers を更新した場合、TextEditHandlers でロードしている StringHandlers はトップレベルスクリプトをコンパイルしても更新されない。
    • TextEditHandlers をコンパイルし直す必要があります。
    • 今回みたいに一つだけならいいのですが、複数のスクリプトをロードしたり、ロードしているスクリプトの階層が深くなったりすると手に負えなくなります(経験談)。
  • トップレベルの StringHandlers と TextEditHandlers の中の StringHandlers は別物になります。つまり、二つの StringHandlers が存在します。
    • 今回の場合、ファイルサイズが大きくなるだけで実害は無いと思います。
    • でも、StringHandlers に property を持たせた場合、その値は一致しなくなります。同じ物に統一した方が都合がいいケースがあると思います。
    • そのうち紹介したいと思っているスクリプトオブジェクトによる疑似クラス(と僕が名付けたテクニック)の場合は、同じ実体を共有してくれない困ります。

分かりやすく絵に描いてみました。

この問題に対する対処法の一つは、いくつか考えられます。それについて細かく説明しませんが、いずれにしても、ロードするモジュールの数が多くなってくると手に負えなくなるのでやめた方がいいです(経験談)。

ModuleLoader を使えば、一気に問題を解決できます。TextEditHandlers の StringHandlers 読み込み部分を次のように手直しします。

property StringHandlers : missing value

on module loaded by loader
tell loader
set StringHandlers to load module "StringHandlers"
end tell
end module loaded

on replace_in_selection(old, new)
set a_text to selected_text()
set a_text to StringHandlers's replace_subtext(a_text, old, new)
set_selection(a_text)
end replace_in_selection

on selected_text() -- 選択されているテキストの取得
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
return value of attribute "AXSelectedText"
end tell
end tell
end tell
end selected_text

on set_selection(a_text) -- 選択されているテキストを置き換える。
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
set value of attribute "AXSelectedText" to a_text
end tell
end tell
end tell
end set_selection

module loaded というハンドラを追加しました。このハンドラは ModuleLoader によって TextEditHandlers が読み込まれた時に実行されます。そして、このハンドラの中で StringHandlers を読み込むようにします。by パラメータにモジュールの読み込みおよび読み込んだモジュールをキャッシュする専用のスクリプトオブジェクト(loader script と呼びましょう)が渡されてきます。このスクリプトオブジェクトに対して、load module コマンドを実行します。スクリプティング機能追加の load module コマンドを直接実行すると、ご利益が半減します。

トップレベルスクリプトはこのようになります。

property parent : make loader
property TextEditHandlers : load module "TextEditHandlers"
property StringHandlers : load module "StringHandlers"

on run
TextEditHandlers's replace_in_selection("あ", "い")
log StringHandlers's replace_subtext("This is a pen.", "This", "It")
end run

ここで、parent という property が登場しました。これは、特殊な property で、スクリプトはparent に設定されたスクリプトオブジェクトを「継承」します。継承というのはオブジェクト指向用語で、要するは parent に設定されたスクリプトオブジェクトのハンドラや property がを自分のものになるということです。parent につけくわえる形でトップレベルスクリプトを定義するイメージです。

make loader コマンドは前出の loader script を生成するコマンドです。loader script では、独自の load module コマンドを定義しており、それを経由して読み込んだスクリプトをキャッシュします。load module コマンドを持った loader script を parent に設定しているので、それ以降の load module コマンドはスクリプティング機能追加のコマンドではなく、loader script の load module コマンドを実行しています。

こうすると、めでたく次のようになります。

次回は module loaded, make handler コマンドおよび loader script の役割/仕組みなど、ModuelLoader の細かい話をします。あとちょっとだけ ModuleLoader の宣伝をします。

しかし、全然オブジェクト指向の話が出てきませんね〜。次の次ぐらいから、そいう話をしたいです。

2009-11-26

2009-11-26T14:02:17+09:00

ModuleLoader 2.0

AppleScript のモジュールシステムです。モジュール/ライブラリを簡単に賢くロードできます。

ModuleLoader を使うとスクリプトファイル名を指定するだけでスクリプトを load することができます。load script コマンドのようにファイルパスを書き込む必要はありません。また、モジュールが依存しているモジュールを自動的にアップデートできます。

ModuleLoader には様々な機能がありますが、まずは load script コマンド の便利な代わりとして使い始めることができます。使い始めるにあたって、お手持ちのライブラリを書き直す必要はありません。

アップルスクリプターなら、must have な物に仕上がったと思う。たぶん、現状で技術的にあり得る最高の物だと自負しています。

しばらく、スクリプトオブジェクト入門を中断していたけど、ModuleLoader の宣伝もかねて再開します。といっても、相手がいないのが空しい。

2009-11-25

2009-11-25T14:33:35+09:00

Script Debugger 4.5 では OSAX コマンドを continue できない。

Script Debugger のバグを一つ見つけた。Script Debugger 4.5 では、OSAX コマンドを continue できない。例えば、次のようなスクリプトはエラーが起きる。

continue display alert "aaa"

スクリプトエディタでは大丈夫。要するに、Script Debugger では、OSAX コマンドを override したスクリプトを実行できないという訳だ。

2009-11-24

2009-11-24T14:34:20+09:00

RSS の content:encoded をやめた。

これまで、RSS から各トピックの全文を読めるようにしていたけど、やめた。理由は、google AdSennse 対策。アクセス数の少ない/稼げないサイトだから厳しいとは思っていたけど、想像以上に広告はクリックされない物です。サイトの結構なスペースを占めるのに、せいぜい 500 円/月 ぐらい。

このままでは馬鹿みたいなので、少しでもトップページへのアクセスを増やせん物かと思いまして。我ながらせこいと思います。

2009-11-23

2009-11-23T01:09:17+09:00

HelpBook.osax 1.1.1/1.0.3 のパッケージ差し替え

インストーラーに Mac OS X 10.6 で Mac OS X 10.5 用の HelpBook.osax をインストールしてしまう不具合があったので、パッケージを差し替えました。

2009-11-21

2009-11-21T20:49:57+09:00

スクリプティング機能追加用のインストーラ

HelpBook.osax には、初めてインストーラを含めました。本当は PackageMaker で作りたかったけど、以下のことを PackageMaker でやる方法が分からず、AppleScript によるインストーラーを作りました。

  • User domain (~/Library/ScriptdingAdditions) か Local domain (/Library/ScriptingAdditions) どちらにインストールするか選択できるようにしたい
  • Mac OS X のバージョンに従って、インストールする物を変えたい(Mac OS X 10.5 用と Mac OS X 10.6 用のスクリプティング機能追加は別々)。

こういうことを PackageMaker でやる方法があれば、誰か教えていただけないでしょうか。

2009-11-21T17:49:46+09:00

HelpBook.osax 1.1.1/1.0.3

HelpBook.osax は任意のバンドル内の Help Book を ヘルプビューアへ登録/表示を行うスクリプティング機能追加です。

たとえば、アプリケーションを起動してヘルプメニューを選択せずとも、AppleScript でヘルプビューアでそのアプリケーションのヘルプを表示させることができます(Show Help)。

汎用目的にも使えるけど、もともとは AppleScript アプリケーションやスクリプトバンドルにヘルプ(HelpBook といいます)を持たせられるように作りました。HelpBook の設定は、バンドルの Info.plist に記載するのだけど、スクリプトバンドルの場合、保存すると Info.plist の内容が失われてしまいます。とくに、Script Debugger は Info.plist を消去するという非道の振る舞いをします。

Info.plist が失われていたら、Resources フォルダの recover-Info.list から自動的に復旧するという機能を AppleScript モジュール ShowHelpBook に持たせていたけど、その機能をスクリプティング機能追加で行うように HelpBook.osax をアップデートしました。XModules に公開している AppleScript モジュールがいちいち ShowHelpBook への依存性を持つのをやめたいと思いまして。

2009-11-18

2009-11-18T01:26:28+09:00

AppleScript のスクリプトオブジェクト入門(その2)ファイルからスクリプトオブジェクトを読み込む

前回で、スクリプトの中にハンドラライブラリとする予定スクリプトオブジェクトを作りました。このスクリプトオブジェクトをファイルに保存して、それを読み込むことをやります。

前回のテキストエディットの選択範囲を書き換えるスクリプトのスクリプトオブジェクト TextEditHandlers をファイルに保存します。script TextEditHandlers 〜 end scirpt の中身をスクリプトエディタの新規書類にコピー&ペースとしてスクリプトファイルに保存します。

on selected_text() -- 選択されているテキストの取得
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
return value of attribute "AXSelectedText"
end tell
end tell
end tell
end selected_text

on set_selection(a_text) -- 選択されているテキストを置き換える。
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
set value of attribute "AXSelectedText" to a_text
end tell
end tell
end tell
end set_selection

スクリプトオブジェクトをファイルに保存する場合は、script ScriptName 〜 end script で囲む必要ないです。

ホームフォルダ/ライブラリ/Scripts/Modules/TextEditHandlers.scrpt

に保存することにします。

そして、このスクリプトを読み込むスクリプトはこちら。

(*
GUI スクリプティングが有効になっている必要があります。
テキストエディットで、適当な書類をひらいて、何かを選択してから実行してください。
*)

on run
set path_to_module to (path to scripts folder from user domain as text) & "Modules:TextEditHandlers.scpt"
set TextEditHandlers to load script alias path_to_module
set a_text to TextEditHandlers's selected_text() -- テキストエディットで選択されているテキストの取得
set new_text to replace_subtext(a_text, "あ", "い") -- 取得したテキストの検索置換
TextEditHandlers's set_selection(new_text) -- テキストエディットで選択されているテキストを置き換える
end run

on replace_subtext(a_text, target_text, replace_text)
set AppleScript's text item delimiters to target_text
set a_list to every text item of a_text
set AppleScript's text item delimiters to replace_text
return a_list as text
end replace_subtext

ファイルからスクリプトオブジェクトを読み込むときは、load script コマンドを使います。load script ファイルに読み込みたいファイルの alias を与えればいいです。その返り値を変数に収めれば、その変数 TextEditHanlders がスクリプトオブジェクトです。テキストエディットに関するハンドラを別のファイルに追い出したので、すごくすっきりしましたね。

さて、このスクリプトでは、実行時に外部スクリプトを読み込んでいます。でも、property に読み込むのがおすすめです。何はともあれ、コードはこんな感じに。

(*
GUI スクリプティングが有効になっている必要があります。
テキストエディットで、適当な書類をひらいて、何かを選択してから実行してください。
*)
property ModuleLocation : (path to scripts folder from user domain as text) & "Modules:"
property TextEditHandlers : load script alias (ModuleLocation & "TextEditHandlers.scpt")

on run
set a_text to TextEditHandlers's selected_text() -- テキストエディットで選択されているテキストの取得
set new_text to replace_subtext(a_text, "あ", "い") -- 取得したテキストの検索置換
TextEditHandlers's set_selection(new_text) -- テキストエディットで選択されているテキストを置き換える
end run

on replace_subtext(a_text, target_text, replace_text)
set AppleScript's text item delimiters to target_text
set a_list to every text item of a_text
set AppleScript's text item delimiters to replace_text
return a_list as text
end replace_subtext

property とは・・・

  • そのスクリプトに含まれるハンドラからなら、どこでも参照できる変数です。
  • 必ず、何からの初期値を与えて宣言しなればいけません。
  • property の宣言に式が含まれている場合は、コンパイル時に評価されます。
  • property の変更はスクリプトと一緒に保存されます。
  • 要するに、そのスクリプトオブジェクトの属性を定義できるということです。

という性質を持った変数です。要するに、property の宣言で、スクリプトの読み込みを行うとコンパイル時に読み込みが行われて、読み込まれたスクリプトと一緒に保存できちゃうのだ。実行時に読み込みが行われないから効率的だし、外部スクリプトの場所を移動してもスクリプトの実行にはなんの支障もない。

一方で、読み込まれる側のスクリプト(今の場合は、TextEditHandlers)が更新されても、コンパイルし直さないと更新されないというデメリットもある。一長一短だけど、僕は property に外部スクリプトをロードすることを好んでいる。

さて、上の二つのスクリプトで気に食わないことはないですか?僕は、

property ModuleLocation : (path to scripts folder from user domain as text) & "Modules:"

が気に食わない。外部スクリプトの絶対的な参照を load script コマンドに与えないといけないのが嫌だ。いろいろスクリプトを作っていると、外部スクリプトの場所を書き換えなければならないことがしばしば起きます。だから、外部スクリプトの名前だけを指定して、あらかじめ設定されたいくつかの場所から検索してロードして欲しい。と思いませんか?ModuleLoader を使えば、名前をしてするだけで外部スクリプトを探してきてくれます。

今、公開している ModuleLoader なら、こんな感じです。

property TextEditHandlers : load("TextEditHandlers") of application (get "ModuleLoader")

アプリケーションの指定が必要だけど、外部パスの場所を指定する必要なありません。application "ModuleLoader"ある場所から、外部スクリプトを探します。サブフォルダの中も探します。外部スクリプトの場所はかなり自由になります。

今、開発中の ModuleLoader なら、名前を指定するだけです。

property TextEditHandlers : load module "TextEditHandlers"

~/Library/Scripts/Modules もしくは /Library/Scripts/Modules の中から外部スクリプトを探します。サブフォルダの中も探します。どうです。ModuleLoader を使いたくなってきませんか?

2009-11-17

2009-11-17T02:21:57+09:00

AppleScript のスクリプトオブジェクト入門(その1)

予告通り、ModuleLoader を誰かに使ってもらう為に、AppleScript のスクリプトオブジェクトを基礎の基礎から説明します。基本的なことや他を当たれば分かることを、偉そうに講釈を垂れるのは非常に恥ずかしい。しかし、どっかの誰かの役に立つことを期待して頑張ります。

スクリプトオブジェクトとは、AppleScript 言語におけるオブジェクトシステムだ。property がいわゆるインスタンス変数、ハンドラがメソッドに相当する。AppleScript はプロトタイプベースのオプジェクと指向言語である。Objective-C, Java, C++ などクラスベースのオブジェクト指向言語とは異なり、クラスの概念は無い。class という言葉は使われるが、データの型とあまり意味が変わらず、クラスベースオブジェクト指向言語におけるインスタンスの設計図としてのクラスは無い・・・

という、小難しい話は後回しにしよう。とにかく、すぐ役に立つことを優先して解説を始めます。とりあえずは、ハンドラ/サブルーチンが使えればいいことにします。もし分かんないこと、知らないコマンドなどを使っていたら聞いてください。

AppleScript は手軽かつ立派なオブジェクト指向言語ですから、おいおいオブジェクト指向的側面も紹介します。AppleScript を通して、オブジェクト指向を理解するのも悪くないと思います。少なくとも僕は、AppleScript でオブジェクト指向を理解しました。オブジェクト指向を理解しておくと、Cocoa/Objective-C を勉強する時に役に立ちます、というより必須です。

スクリプトオブジェクトの最初の利用方法はハンドラのライブラリだと思います。皆さん、それぞれのご用途で AppleScript からコントロールするアプリケーションがあり、それらでよく行う処理というのは少なからずあると思います。もちろん、それらはハンドラとしてまとめられていると思いますが、複数のスクリプトでそれらを使い回すにはそれでは不十分です。ハンドラをコピー&ペーストしてやりくりしていませんか?そういうことは卒業して、よく使うハンドラをスクリプトオブジェクトとしてまとめて、色んなスクリプトで共有しましょう。

具体的な例として、テキストエディットのスクリプトを考えてみましょう。このスクリプトは、テキストエディットで選択項目を取得して、取得した結果を加工して、選択項目と置き換えるスクリプトです。実用的でしょう。

(*
GUI スクリプティングが有効になっている必要があります。
テキストエディットで、適当な書類をひらいて、何かを選択してから実行してください。
*)

on run
set a_text to selected_text() -- テキストエディットで選択されているテキストの取得
set new_text to replace_subtext(a_text, "あ", "い") -- 取得したテキストの検索置換
set_selection(new_text) -- テキストエディットで選択されているテキストを置き換える
end run

on selected_text() -- 選択されているテキストの取得
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
return value of attribute "AXSelectedText"
end tell
end tell
end tell
end selected_text

on set_selection(a_text) -- 選択されているテキストを置き換える。
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
set value of attribute "AXSelectedText" to a_text
end tell
end tell
end tell
end set_selection

on replace_subtext(a_text, target_text, replace_text)
set AppleScript's text item delimiters to target_text
set a_list to every text item of a_text
set AppleScript's text item delimiters to replace_text
return a_list as text
end replace_subtext

このスクリプトの目的は単純ですが、テキストエディトでは selection property がサポートされていないので、GUIスクリプティングを駆使しています。結構大変です。エラー処理を含めると、もっと量が増えると思います。そうすると、テキストエディットをコントロールしている selected_text(), set_selection() あたりのハンドラを他のスクリプトと共有したくなりますよね。その準備として、これらのハンドラを一つのスクリプトオブジェクトとしてまとめます。

(*
GUI スクリプティングが有効になっている必要があります。
テキストエディットで、適当な書類をひらいて、何かを選択してから実行してください。
*)

on run
set a_text to TextEditHandlers's selected_text() -- テキストエディットで選択されているテキストの取得
set new_text to replace_subtext(a_text, "あ", "い") -- 取得したテキストの検索置換
TextEditHandlers's set_selection(new_text) -- テキストエディットで選択されているテキストを置き換える
end run

script TextEditHandlers
on selected_text() -- 選択されているテキストの取得
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
return value of attribute "AXSelectedText"
end tell
end tell
end tell
end selected_text

on set_selection(a_text) -- 選択されているテキストを置き換える。
tell application "System Events"
tell application process "TextEdit"
tell (get value of attribute "AXFocusedUIElement")
set value of attribute "AXSelectedText" to a_text
end tell
end tell
end tell
end set_selection
end script

on replace_subtext(a_text, target_text, replace_text)
set AppleScript's text item delimiters to target_text
set a_list to every text item of a_text
set AppleScript's text item delimiters to replace_text
return a_list as text
end replace_subtext

このように、script ScriptName ~ end script で囲うと、その部分がスクリプトオブジェクトになります。これで、2つのハンドラは、スクリプト TextEditHandlers に所属するようになりました。ちなみに、スクリプトオブジェクトとは「スクリプトそのもの」です。何も難しいことは(とりあえずは)無いです。このスクリプト全体もスクリプトオブジェクトです。だからスクリプトオブジェクトの中にスクリプトオブジェクトが入っている構造になります。

スクリプトオブジェクトの中のハンドラを呼び出すには、

handler_name() of ScriptName

とか、

ScriptName's handler_name()

という具合にします。ちなみに僕は後者が好みです。

次回に続く・・・.次は、スクリプトオブジェクトをファイルからロードする話をします。

2009-11-16

2009-11-16T12:14:19+09:00

path to me の話を更新

AppleScript でスクリプトから、自分自身のファイル参照を取得するには スクリプティング機能追加のコマンド path to me を実行する。少なくとも、Mac OS X 10.4 の頃は、path to me で自分自身への参照が得られるかは、スクリプトを実行しているアプリケーションに依存しており、スクリプトファイルへの参照ではなくスクリプトが実行しているアプリケーションへの参照が返ってくることもしばしばだった。

Mac OS X では、どんなアプリケーションでも path to me でスクリプトファイルへの参照が得られるので、スクリプティング機能追加からイベントの送信元のスクリプトを知ることができるのかしら?できるなら、自分のスクリプティング機能追加でも使いたいな、と思って Applescript-implementors mailing list に質問をポストしたところ、Mac OS X 10.3 で追加された OSA API、OSADoScriptFile、OSALoadFile を使うと、自動的にそうなりますよ、ということがわかった。つまり、API レベルでの改良で対応されており、スクリプティング機能追加からはスクリプトファイルに関して知ることはできないということらしい。

期待した情報が得られなかった。また、OSA API で、特定のスクリプティング機能追加のコマンドを特別扱いするのはいかがな物かと思う。

2009-11-15

2009-11-15T18:39:30+09:00

ISHIWARARI さん、寄付ありがとうございました。

大変ありがとうございます。54人目。

お気軽に、コメント、ご意見、ご要望を頂ければ幸いです。

2009-11-15T17:48:38+09:00

次の ModuleLoader の話(その2)

実装はほとんど終了した。まだ、自分でも全然使い込んでいないし、マニュアルもいっぱい書き直さなきゃいけないし、今の ModuleLoader は十分安定しているので、急いで公開するつもりは無いです。もし、興味を持っていただける方がいらっしゃいましたら、メールください。

さて、モジュール/ライブラリのロードシステムは AppleScript の重要な Missing Part だと思って鋭意 ModuleLoader の開発を続けていたけどコメントをもらったことが無い。幸いなことに、いらねーよ、というコメントも無い。ModuleLoader を使ってもらえないのは、ご利益が理解されていないからだと思うことにする。詳しいことは「AppleScript ソースコードの分割と共有」と ModuleLoader のマニュアル に全部書いてきたつもりだけど、読まれていない/理解されていないものと都合良く考えることにする。

そこで、そもそも AppleScript の script objectとは、ということから、それを有効利用した ApplScript、script object をモジュールとしてロードすること、ModuleLoader のご利益と仕組みなど、基礎の基礎から解説を試みたいと思う。そして、ModuleLoader がちょっとでも利用してもらえるようにしたい。

話の流れは・・・

  • script object とは
  • script object の活用事例
  • script object をファイルからロードする。
  • ModuleLoader の利用法
  • ModuleLoader の仕組み

という感じを考えているけど、実際どうなることやら。随時、質問も受け付けちゃうぜ。script object について知りたいことがあったら、コメント欄に書き込んでください。

2009-11-13

2009-11-13T21:05:07+09:00

次の ModuleLoader の話(その1)

現在、ModuleLoader の大幅なアップデートを開発している。

ちなみに、ModuleLoader とは、AppleScript のモジュールライブラリの管理/ロードシステムです。Perl とか Ruby とか、いまどきのスクリプト言語は決められた場所からモジュールをさがして読み込んでくれる機能があるよね。ライブラリ次第でその言語のパワーは大きく左右されると思うのだけど、AppleScript には標準ではモジュール管理/検索機能は備わっていない。

ちっちゃい AppleScript はオンライン上でよく見かけるけど、ガッツリAppleScirpt で作り込んだアプリケーションはあんまり見たことがない。充実したライブラリの欠如が AppleScript の可能性を大きく制限している可能性があると思っている。

AppleScript がモジュールを読み込む仕組みがまったく無いかというと、そういう訳ではなく load script コマンドでファイルからスクリプトオブジェクトを読み込むことができる。でも、絶対パスを指定しないとだめだぜ。名前だけ指定したら探してきてほしいと思いませんか?また、依存関係を解決する仕組みも無いぜ。読み込んできたモジュールが別のモジュールを必要としていたら、それらも読み込んでほしいと思いませんか?

そんなことができるのが ModuleLoader です。

ちなみに、ModuleLoader の仕組みはオリジナルじゃないです。appleMods というライブラリおよびモジュール読み込みシステムから基本的なアイデアを拝借しています。結構立派なのが既にあるのに、なんでわざわざ自作したかというと、appleMods にいろいろ気に食わないことが多かったから。一番受け入れられないのが、普通のスクリプトをモジュールとして扱えないこと。独自の構造のバンドルじゃないと、モジュールになれないみたい。ModuleLoader は、load script できるものなら何でもかんでもモジュールとして扱えます。

もう一つ気に食わないのは、かっちりした使い方を強制されること。ModuleLoader は appleMods と同じようなこともできるし、だだモジュールを検索してロードするだけというルーズな使い方もできる。あと、appleMods はまともにメンテされている気配がない。メールを送っても梨の礫。

さて、前置きが長くなりましたが、次の ModuleLoader は次のようにアップデートする予定。

  • スクリプティング機能追加になります。
    • 現在、ModuleLoader はアプリケーションなので、モジュールのロードの度に起動する。コンパイルの時だけだからそんなに気にならないけど、シェル環境とか、実行時にコンパイル/モジュールのロードには向かない。というわけで、スクリプティング機能追加にします。
    • モジュールの検索も C で実装したスクリプティング機能追加が行うので、ちょっとは速くなるはず。
  • デフォルトのモジュールの置き場所を ~/Library/Scripts/Modules と /Library/Scripts/Modules に固定。
    • 今の、ModuleLoader は、ModuleLoader がある場所からモジュールを検索している。だから、ModuleLoader とモジュールを同じ場所においておけば、どこに持っていってもいいのだけど、スクリプティング機能追加にしちゃうと、そんな自由度は無くさざる得ない。あと、実行時のロードにも向かない。でも、モジュールの検索場所はそれなりに自由に設定/変更できるようにするつもり。

誰か、コメントプリーズ。

2009-11-13T10:25:51+09:00

URI Escape 1.3.1/1.2.3

以下を行うスクリプティング機能追加です。

  • パーセントエスケープされた URI(URL) をアンエスケープします。
  • 任意の文字列をパーセントエスケープします。

スクリプトエディタで辞書が開けない不具合を修正しました。

2009-11-09

2009-11-09T19:41:12+09:00

Delete Messages Now 1.0

Mail.app で選択されているメッセージをゴミ箱に移動すること無く、すぐさま消去します。使い方は、ただ起動するだけです。スクリプトメニューなどに登録して使ってください。

たくさんのメッセージが選択されているときは、いくらかいくらか処理に時間がかかります。終了したら Growl を使って通知します。したがって、Growl もインストールしておいてください。

自分は、もっぱら「迷惑メール」フォルダの掃除に使っています。迷惑メールは相変わらず、すごい勢いでやってきます。迷惑メールフィルターのおかげでそれらを目にすることはほとんど無いのですが、必要はメールも間違って迷惑メールと判断されてしまうことも無い訳ではなく、ときどき迷惑メールフォルダの中身を確認しています。その時、ついでに確認した迷惑メールを消去するのに便利です。

2009-11-09T13:12:30+09:00

寄付を催促するウインドウ

最近、リリースしているソフトの多くは、起動時に次のような寄付を催促するウインドウを表示するようにしている。基本的に、全部フリーウェアのつもりなんだけど、見返りがあるのにはこしたことはないと思って。

「既に寄付をした / 今後このメッセージを表示しない」のチェックボックスを ON にしてもらうと、それ以降は表示しないという仕組みになっています。さらに、「既に寄付をした / 今後このメッセージを表示しない」のチェックボックスを ON にしても、バージョンアップすると、再度表示されるようになります。

ちなみに、寄付をいただいたからと言ってこのウインドウを二度と表示しなくなるようは仕組みはありません。自分も、開発中にバージョンを上げると、このウインドウが表示されて、いちいちチェックボックスをクリックしています。

少々うっとおしいかもしれないけど、あんまり深刻に考えなくてもいいです。容赦なく、チェックボックスを ON にしてください。でも、ただより高いものはないですから、気に入って使い続けていただけるなら寄付をお願いします。

2009-11-09T12:02:08+09:00

AppleScriptDoc 1.3

ライブラリ/モジュールとしての AppleScript のリファレンスマニュアルを生成するアプリケーションです。

ソースコードにかかれたコメントから、スクリプトのリファレンスマニュアルを HTML として出力します。バンドル形式のスクリプトの場合、バンドル内に(ヘルブビューアに表示する) Help Book としてリファレンスマニュアルをセットアップできます。

  • Mac OS X 10.6 に対応しました。

2009-11-09T10:45:27+09:00

XFile 1.2.2

XFile はファイル操作(移動/削除、属性の取得などなど)に統一的なオブジェクト指向インターフェースを与えるAppleScript モジュールです。

2009-11-09T00:53:48+09:00

FileClipper 3.0.4

クリップボードに参照があるファイルをコピー、移動、エイリアスファイル/シンボリックリンク/ハードリンクの作成を行うアプリケーションです。Finder がクリップボードに入れるファイル参照を読み取りますので、コピー&ペーストの要領の自然な操作で扱えます。

  • Finder で表示オプションパレットが開かれている時に、エラーが発生することがある不具合を修正しました。

2009-11-07

2009-11-07T17:45:43+09:00

PowerSelect 2.2.1

PowerSelect は選択されているフォルダの中から、条件にマッチしたファイルを検索します。それらのすべて、もしくは一部だけ選択できます。

以下のような用途に便利です。

  • たくさんファイルを含んでいるフォルダからファイルを見つけ出したい。
  • ある条件にマッチしたファイルを選択して・・・・
    • コピーしたい
    • 移動させたい
    • 削除したい
    • 何かのアプリケーションに Drag&Drop したい
    • その他、何かの処理を行いたい。

Finder で表示オプションパレットが開かれている時に、エラーが発生することがある不具合を修正しました。

2009-11-07T02:03:00+09:00

Marcus Jarrett さん、寄付ありがとうございました。

ものすごく久々の寄付。53 人目。

頑張り具合と寄付ってどうして比例しないのだろう。

2009-11-06

2009-11-06T19:13:47+09:00

PowerRenamer 3.1.3

たくさんのファイルの名前を一度に変更するアプリケーションです。あるパターンにマッチしたファイル名の一部を、別の文字列に置き換える事ができます。正規表現を使った検索置換も行えます。またファイルが Finder で並んでいる順番で、番号付けを行えます。

  • 番号付けモードが壊れていたのを直しました。

2009-11-06T16:20:59+09:00

FileSorter 3.0.4

ファイルの参照のリストを、Finderで表示されている順番に並び替えます。アイコン表示、リスト表示、カラム表示に応じてソートの仕方を自動的に選択します。

2009-11-05

2009-11-05T01:41:48+09:00

InsertionLocator 1.2.7

Finder で選択されている場所を取得する AppleScript モジュールです。

Finder の insertion location プロパティは、常に Finder ウィンドウのトップレベルを意味します。選択されているフォルダやリスト表示で表示されているサブフォルダを取得したい場合もあるでしょう。InsertionLocator はそれらを取得することができます。そして、アプリケーションにあわせて細かい振る舞いをカスタマイズすることができます。

2009-11-04

2009-11-04T18:30:57+09:00

New Message with Account 1.0.2

Mac OS X に含まれているメールソフト Mai.app で、アカウント(送信元のメールアドレス)と署名を指定して新規メッセージを作る AppleScript です。

アカウントを指定してメッセージを作成するので、間違った送信元のアドレスでメッセージを送ってしまうトラブルを避けることができます。また、メッセージに含める署名もあわせて選ぶことができます。

スクリプトメニューなどに登録して使ってください。実行すると次のようなダイアログが表示されます。

僕は、Spotlight メニューから起動して使っています。メールを送りたくなったら、Mail.app に切り替えることなく、どんな状況からでも、メールを作り始められるので非常に快適です。

2009-11-03

2009-11-03T16:35:23+09:00

Compiled Script Icon FileClipper 3.0.3

クリップボードに参照があるファイルをコピー、移動、エイリアスファイル/シンボリックリンク/ハードリンクの作成を行うアプリケーションです。Finder がクリップボードに入れるファイル参照を読み取りますので、コピー&ペーストの要領の自然な操作で扱えます。

  • Finder でフローティングパレットが表示されていると、エラーが発生することがある不具合を修正。
  • 「ここに移動」で svn mv ではなく、svn cp が実行される不具合を修正。
  • GUI スクリプティングが OFF の場合、システム環境設定を起動するのではなく、GUI スクリプティングを有効にする許可を求めるようにした。

2009-11-03T16:22:25+09:00

ターミナル.app の custom title を個別に変更できない件を bug report してみた。

Apple Bug Reporter に以下のように報告した。へたくそな英語で恥ずかしいけど、公開してみる。

Summary:

Setting a custom title of a terminal tab through AppleScript cause changing all of custom titles of terminals.

In Terminal.app, custom title of the tab can be changed using the Inspector panel. The custom title se by hands are not shared between tabs of Terminal.app.

It looks no way to do same things using AppleScript.

Steps to Reproduce:

Open a few terminal window and execute following AppleScript.

tell application "Terminal"
set custom title of selected tab of window 1 to "Hello"
end tell

Expected Results:
The custom title sat by above script should be restricted in the selected tab of window 1.
Actual Results:
All of custom titles of tabs of the same settings set will be changed to "Hello"

Notes:
I believe the ability each sessions can have own custom titles, because the window title is important visual identifier.

そしで、ご返答は、お決まりのこれ。

After further investigation it has been determined that this is a known issue, which is currently being investigated by engineering.

この bug は一度、Mac OS X 10.5.7 で修正されて、Mac OS X 10.6 で再発している。ちょっとお粗末じゃないかという気がする。その気になれば、一瞬で修正できるバグだと思うのだけど。

たいしたバグじゃないかもしれないけど、自分はこの bug を回避する為に TerminalControl.osax を作ってしまった。こんなことの為にユーザーに本来なら必要ないスクリプティング機能追加のインストールを強いるのは心苦しい。

2009-11-03T09:12:05+09:00

Trash It 1.1.2

前面のアプリケーションで開いているファイルを閉じてゴミ箱に捨てる AppleScript です。Spotlight メニューやスクリプトメニューなどから実行してください。

アプリケーションで開いているファイルをゴミ箱に入れたくなることは、ときどきありますよね。例えば、Web からダウンロードしてきた PDF ファイルが見当違いのものであったとか、文書/コードを書き始めてやっぱやめたと思ったときとか。

Finder でそのファイルが表示されていればいいですが、そうでなかったら次のようなステップを踏むことになると思います。

  1. 書類ウインドウのプロキシアイコンを使って、Finder でファイルを表示させる。このとき、アプリケーションが Finder に切り替わる。
  2. 書類を開いているアプリケーションに戻って、ファイルを閉じる。
  3. Finder にもどって、閉じたファイルをゴミ箱にいれる。

Trash It は上記の手順を一発で行います。