スクリプト共有ライブラリ規格

AppleScriptは,MacintoshのSystem7において導入されたプログラムソフトウエア間の命令・データ交換技術であるAppleEventを記述するためのシステムです。AppleScriptはオブジェクト指向の言語体系を持ちますが,その運用はユーザーの自由にまかせたため,実際にはその技術の本質が一般には理解されず,オブジェクト技術としての側面は活用されてきませんでした。
ここに示すスクリプト共有ライブラリ規格は,オブジェクト指向技術としてのAppleScriptを活用するためのガイドラインであり,それと同時に,より複雑化するスクリプトを簡略化し,スクリプト制作者の労力を節約して効率のよい開発が行えるようにすることを目的としてます。
汎用性のある機能を持つスクリプトを共通のフォルダにインストールしておき,どのスクリプトアプリケーションからも利用できるようにします。これにより,スクリプトのソースを公開することなくその提供する機能を共有することができます。
このページではその具体的な方法を示すとともに,AppleScriptの持つオブジェクト指向の技術の本質を解き明かします。

スクリプト共有ライブラリのメリット

AppleScriptでは非常に頻繁に同一のルーチンが用いられます。また,他の人のスクリプトを改良して表示することもあります。しかしそれらの中で,スクリプトが肥大化したり,スクリプトの著作権が曖昧になりがちです。スクリプト共有ライブラリ規格は,これらを解消するためのものです。それぞれのスクリプトオブジェクトはスクリプトの名称,著作権の情報などを自分自身の中に持ったまま,他のスクリプトの内部にそれを組み込むことができます。また,共通のフォルダに共有ライブラリとしてインストールしておくことで,必要に応じてそれを読み込むこともできます。これにより,ルーチンの改訂の際にすべてのスクリプトソフトウエアを一斉に書き換えるのではなく,スクリプトオブジェクトを一つ置き換えるだけですむようになります。
また,一度メモリ内に読み込まれたスクリプトは,もはやファイルには依存せず,メモリ内で自由に複製したり,AppleTalkネットワーク内で転送することができます。そのような場合においても,この規格に準拠したスクリプトは固有の名称,バージョン番号,著作権情報を維持することができます。

「スクリプト」とは

そもそもAppleScriptとは,Macintoshの内部で処理されているAppleEventというアプリケーション・システム間の情報・命令のやりとりという「対話」を簡単な言語により記述することを目的としたシステムです。
AppleScriptの世界における「スクリプト」とは,実在するファイルに依存しないオブジェクトであり,Macintoshのメモリ空間およびAppleTalkネットワーク空間内において自由に移動,消去,複製を行うことができます。このことは,あなたがスクリプトを作成してあるファイル名においてそれを保存しても,それはあなたのスクリプトを格納しているファイルの名称にすぎないということです。同様に,アプリケーションとして実行されているときにおいても,アプリケーション名はその実行されているスクリプトを格納しているメモリ空間の名称にすぎないということです。
AppleTalkネットワーク上においては複数のMacintoshが稼働しています。また,それぞれのMacintoshの上で複数のスクリプトアプリケーションが動作します。それぞれのスクリプトアプリケーションのメモリ空間内にはさらに複数のスクリプトが展開されています。高度なスクリプティングを行う上で,これらのスクリプト群の連携を図るためには,それぞれを個別に認識する必要があります。そのためにはスクリプトに対して,実在するファイルに依存しない形で「名前」を与えることが重要です。名前を与えられたスクリプトは,その名前のもとに他のスクリプトと「対話」を行うことができます。

スクリプトの名称

ファイル名はそのスクリプトの名称とならないということはすでに述べました。そこで,スクリプトに本来的な意味で名前を与えたいと思います。そこで,仮にプロパティ(属性)変数を用いて次のように記述したとします。
property myName: "Sample Script"
確かに,これで名称を与えることができました。しかし,この方法はいくつかの問題を抱えています。最も重要なことは,この情報は外部から書き換えが可能であるという事です。このスクリプトをtargetObjectという変数に格納した場合(targetObjectはあくまでも外部のコンテナ側から見た呼称にすぎません),以下のようなスクリプトで名称を書き換えることが可能です。
set myName of targetObject to "Another Name"
これがスクリプト名ならまだいいのですが,著作権情報も書き換えが可能であるとなると,問題は深刻になります(再編集不可の状態であってもプロパティの変更には制約はありません)。また,このスクリプトの名称を取り出す際には次のようなスクリプトを利用することになります。
set theName to myName of targetObject
これではそのスクリプトの情報が外部からいくらでも操作可能であるということになってしまいます。また,外部から操作を行うスクリプトから見ても,自己(操作する側)と他(操作される側)の区別が混乱したスクリプトになっています。この問題を避けるため,スクリプト同志はある一定の定められたインターフェースによってルールに則った紳士的な対話を行うことが求められます。それは,以下のように記述されます。
on returnName()
    return "Sample Script"
end returnName
外部からこのスクリプトの名称を得るときは,以下のようなスクリプトで得ることができます。
tell targetObject to returnName()
--> "Sample Script"
または,以下のようにも記述できます。
set theName to returnName() of targetObject
プロパティの外部から変更可能という点をメリットとして生かしたい場合には,この方法はプロパティとも併用することができます。
property myName: "Sample Script"
on returnName()
    return myName
end returnName
また,外部から強引に属性を取り出すわけではなく,スクリプト自身に能動的に「名前」を返させることから,スクリプト自身が自分の判断により名称を変更して振る舞うことも可能です。下の例では,日曜日だけ返す名称が変更されます。
on returnName()
    if weekday of (current date) is Sunday then
        return "Idle Script"
    else
        return "Sample Script"
    end if
end returnName
スクリプト自身がメモリ空間内において自己同一性を確立することが可能となるわけです。これはメモリ空間内において複製やネットワーク上で転送を受けたときも保証されます。ちなみに,以下はメモリ内における複製の例です。この操作ではアプリケーション内部での占有メモリは2倍になります。
copy targetObject to copiedObject
なお,スクリプトをメモリ空間内に展開したりファイルに格納するには,load/store命令を用います。この命令がAppleScriptをコンポーネントアーキテクチャーとして確立させている重要なキーとなります。
set targetObject to load script (choose file)
store script targetObject in (new file)
補足:
name,versionというpropertyを設定することもできますが,AppleScript自身が持つ属性の継承に障害を与え,その影響が予測困難なためおすすめできません。

スクリプト本体の規格

以上のような概念を元にして,これを現実的な運用面に限って最小限の規格として成立させる上で必要なのは,外部とのインターフェースとなるハンドラ(ルーチン)です。そこで,以下のようなインターフェースを規格として提案します。
returnName()
スクリプトオブジェクトの名称を文字列で返す。スクリプトオブジェクトのファイル名を変えたりネットワーク上で転送してもスクリプトの同一性を保つためのもの。
returnVer()
バージョン番号を返す。バージョン番号の比較に用いるため,bやβなどの文字を含まず,演算可能な数値(実数)であること。
returnInfo()
そのスクリプトの簡単な機能を説明する文字列を返す。ダイアログに表示可能な程度の長さであること。つまり,127字(255バイト)以内。
returnCR()
著作権表示の文字列を返す。あえて字数制限はありませんが,AppleScriptのダイアログの制限以内である255バイト以下が望ましいと考えられます。
run
実行されるべきスクリプトが定義されるハンドラ。特定のルーチンを提供するためのスクリプトでは空でもかまいません。実行するとそのスクリプトに関するガイドが表示されるようにしておくといいかも知れません。
当サイトで配布中のライブラリは,原則として編集可能なスクリプトオブジェクトはファイル名の末尾をObject,編集不可のスクリプトオブジェクトはファイル名の末尾をObjとしています。これらのライブラリを管理するためのユーティリティとしてCyberHandlerAS Runnerファイル情報を提供しています。ドラッグ&ドロップすることによりライブラリを実行したりライブラリの情報を表示することができます。

ファイルタイプとクリエータタイプについて

スクリプト共有ライブラリのファイルタイプ・クリエータタイプは自由です。というのは,スクリプトは本来的に 'scpt' リソースにのみ依存するものであって,このリソースが存在する限り,そのファイルのファイルタイプ・クリエータタイプはそのスクリプトの機能に影響を与えません。このことはスクリプト共有ライブラリの設計を考える上で重要です。
スクリプト共有ライブラリを作成する場合には,通常,Apple純正のツールであるスクリプト編集プログラムが使用され,保存したファイルのファイルタイプは 'osas' ,クリエータタイプは 'ToyS' となります。 store script 命令を用いた場合にも同様です。この場合,スクリプト編集プログラムが提供するアイコンによりスクリプトファイルであることが明示されます。しかし,再編集不可のスクリプトであった場合,ダブルクリックをしてもスクリプト編集プログラムはこれを開くことができません。
スクリプト編集プログラムが開くことのできない再編集不可のスクリプトであった場合,スクリプト編集プログラムの書類である意味はあまりありません。すなわち,クリエータタイプがスクリプト編集プログラムのものである必要はありません。そこで,ファイルタイプとして 'shlb' を与えることができます。漢字Talk7.5.3以降であれば,このファイルタイプを持つファイルには,システムが標準で持つ Shared Library Manager によって共有ライブラリとしてのアイコンが与えられます。また,ダブルクリックすると機能拡張フォルダに入れるように促すメッセージが表示されるようになります。
CyberHandlerのクリエータタイプである 'CBHD' を与えられたファイルの場合,ダブルクリックによりCyberHandlerが呼び出されて, returnName ハンドラなどが返す情報が画面に表示されるようになります。これはFinderが表示する情報よりもさらに詳細な情報を表示させることが可能です。

スクリプト共有ライブラリのインストール

スクリプトを設計する上で,ライブラリがどのフォルダにあってもそれを読み込むようなスクリプトを作成することは可能ですが,Macintosh用ソフトウエアとしてのわかりやすさを重視して,当サイトで配布しているスクリプトアプリケーションは共有ライブラリを以下の順序で読み込みます。
  1. アプリケーション本体と同じフォルダ内のファイルのデータフォーク
  2. システムフォルダ内の shared libraries フォルダ内のファイルのデータフォーク(MacOS7.6の場合のみ,将来はサポートされません)
  3. システムフォルダ内の機能拡張フォルダ内のファイルのデータフォーク
  4. アプリケーション本体と同じフォルダ内のファイルのリソースフォーク
  5. システムフォルダ内の shared libraries フォルダ内のファイルのリソースフォーク(MacOS7.6の場合のみ,将来はサポートされません)
  6. システムフォルダ内の機能拡張フォルダ内のファイルのリソースフォーク
  7. ライブラリ名が「Obj」で終わっている場合,末尾を「Object」になおしてはじめからやり直す

スクリプトオブジェクトの最新版をインストーラでインストールしても,各アプリケーションのフォルダ内に古いバージョンが入っているとそちらが優先的に読み込まれます。これは,スクリプトアプリケーションごとにカスタム化されたライブラリを利用できるようにするためです。
以前は機能拡張フォルダの中に「ScriptObject Libraries」フォルダを作成して利用していましたが,MacOS7.6において shared libraries フォルダがシステムレベルでサポートされたので,そちらを利用するようにしました。機能拡張フォルダ以外の場所にインストールすることを推奨しているのは,わずかでもシステムの起動速度の低下の要因となることを避けるためです。しかし,MacOS8からは shared libraries フォルダが機能拡張フォルダに統合されてしまったので, shared libraries フォルダの利用は将来的には停止する予定です。
これらのフォルダの利用はオプションであり,必須ではありません。機能拡張フォルダを利用したい方は機能拡張フォルダを,初期設定フォルダを利用したい方,システムフォルダ直下を利用したい方などはそのようにしていただいてもかまいません。そのライブラリを参照するソフトをそれに応じて設計すれば問題はありません。

リソースを使用しない共有ライブラリ

ここまでのスクリプト共有ライブラリは,本質的にただのコンパイル済みスクリプトであり,そのスクリプトは他のスクリプトオブジェクトと同様に,ファイルの中の 'scpt' リソースと呼ばれる部分に格納されたものでした。これをこれを利用するためには標準機能追加の load script / store script 命令を用いますが,漢字Talk7.5.5以下のシステムにおいて store script 命令をスクリプトアプリケーションの内部で利用した場合,スクリプトアプリケーションが終了する瞬間にシステムエラーが発生することがありました。MacOS7.6以降のシステムにおいても,システム全体は保護されるものの,スクリプトアプリケーションの動作が不安定になる副作用があります。
この問題を解決するため, Data Component Object フォーマットによって共有ライブラリを保存する方法が新たに採用されました。 Data Component Object はコンパイル済みスクリプトをそのままデータフォークにバイナリデータとして保存することができます。コンパイル済みスクリプトはスクリプト表現形式の設定に影響を受けませんし,再編集可能なスクリプトも再編集不可のスクリプトも利用できます。ライブラリのインストールや利用には Data Component Object ライブラリの writeDataFile ルーチンや readDataFile ルーチンが利用されます。
この Data Component Object フォーマットの共有ライブラリの利用は,いくつかの問題点があります。それは,従来の共有ライブラリを読み込むための loadObj ルーチンを書き換えなければ利用できないと言う点です。ただしこれは, loadObj ルーチンを書き換えるだけで対応できるということにもなります。問題なのは,この loadObj ルーチンは Data Component Object フォーマットを読むため, Data Component Object の readDataFile ルーチンに依存します。ところが, Data Component Object 共有ライブラリ自体が Data Component Object フォーマットで提供されるようになると,新しい共有ライブラリを利用するスクリプトを作成する場合には,何らかの方法で Data Component Object フォーマットを読むための方法を考えなければいけません。
AS Runnerは Data Component Object フォーマットに対応した loadObj ルーチンを持つので,AS Runner内で実行されるすべてのスクリプトは loadObj ルーチンを自前で持つ必要はありません。またこのページで配布する loadObj() Handler というスクリプトライブラリも新しい loadObj ルーチンを提供します。しかし,それ以外のスクリプトが対応できるようになるまでの間,スクリプト共有ライブラリは 'scpt' リソースと Data Component Object フォーマットのデータフォークをひとつのファイルに持ちます。
この Data Component Object フォーマットの採用の大きな意義は,スクリプトオブジェクト以外の任意の形式のデータをライブラリとして利用できるようになることです。リスト,レコードからQuickDrawデータに至るまで,自由にライブラリとして loadObj ルーチンから利用できるようになります。さらに,最新の Data Component Object はこれらのデータをインターネット経由で利用することができるルーチンも備えています。

変更履歴


スクリプトアプリケーションからの利用法

スクリプト共有ライブラリを利用する際には,スクリプト共有ライブラリをスクリプトアプリケーションのメモリに読み込んで利用しなければなりません。loadObjルーチンを正確に記述するのは煩雑な処理となりますが,このページからダウンロードできるライブラリを使用すると下記のように簡単に記述することができます。
property loadObjHandler : load script file "loadObj() Handler"
on loadObj(ObjName)
    loadObj(ObjName) of loadObjHandler
end loadObj

display dialog (returnName() of loadObj("ClipboardObject")) buttons {"OK"} default button 1
loadObj(ObjName) ルーチンはコアライブラリであるCyberHandlerObjectおよびそれを実装した実行環境であるAS Runnerにも組み込まれているので,AS Runnerにおいて実行されるスクリプトは loadObj() Handler の組込みを省略できます。

ライブラリのサンプル

ライブラリを自作するためのサンプルを作成しました。また,自作のスクリプトライブラリを配布する際に利用できるように,簡単にカスタマイズできるインストーラを作成しました。既存のライブラリの開発用のインストール(リソース形式のコンパイル済みスクリプトへの変換)やネットワークインストーラによるアップデートが行えます。

このスクリプト共有ライブラリ規格はAppleとは関係ありません。

補記:スクリプトアプリケーションのバグの回避

スクリプトアプリケーションにおいて画像処理などで大量のデータを処理すると,スクリプト自身がその大きさに膨らんでしまい,スクリプトアプリケーションの終了時にはそれがファイルに保存され,次回使用時にもメモリに展開されるというものです。
これにより,同じスクリプトアプリケーションを何回も使っていると,そのスクリプトアプリケーションのメモリ消費が大きくなり,メモリ消費がメモリ割り当ての上限に達したときは「out of memory」になります。上限ぎりぎりの場合,スクリプトアプリケーションの動作が不安定になります。
これは,runなどのハンドラ内で使用された変数は,明示された広域変数が記述されていなくても自動的に広域変数になってしまうからだと考えられています。回避方法は,run/openハンドラ内では変数にデータを展開せずに,サブルーチンを利用することです。こうすることにより,サブルーチン内のデータはそのサブルーチンの処理が終わると同時に確実にメモリ内から消去されるため,スクリプトの肥大化を避け,起動・終了時に読み書きするスクリプトの量が減ることから,apletの起動・終了も体感速度が大幅に向上します。
具体的には,以下のようなサブルーチンを利用したスクリプトを作成し,runハンドラ内にはサブルーチンの呼び出しだけを記述します。サブルーチンの末尾で「結果」が返されると明示されない変数である result が生成されて広域変数として登録されてしまうため,サブルーチンの末尾は一切の結果を返さない命令であるか,returnが記述されている必要があります。
on run
   main() --メモリーリークを避けるため
end run
on main() --サブルーチン名は自由(run/open/print/quit/idle以外)
   --ここで処理を行う
   return --必須
end main

これとは別の問題ですが,store script命令を利用した際に「ディスクに誤りが起きました」というエラーが発生し,「スクリプトの変更が保存できませんでした」と表示されることがあります。特に漢字Talk7.5.5以前のシステムでは,これに加えてシステムの挙動が不安定になったり,クラッシュしたりします。この問題の解決方法はいまだ確立されていません。MacOS 7.6以降においては,エラーはスクリプトアプリケーションのメモリ内部のみに限定され,システム全体のプロセスは保護されます。この問題の解決方法をご存じの方はご一報下さい。回避方法は,store script命令は使用せずにDataCompObjフォーマットを使用することです。

関連ページ
   エラー番号一覧
   Data Component Object
   CyberHandler
   ネットワークインストーラ
   ライセンスについて


Copyright(C) SUZUKI Kazufumi, All rights reserved.