AppleScript ソースコードの分割と共有

Contents

Introduction

ある程度 AppleScript を堪能になってくると、困ってしまうのは長いソースコードを複数のファイルに分割できないかという事ではないでしょうか?長いソースコードを一つのファイルにおさめてしまうと、見通しが悪くなりますし、使い回しのきくコードは独立したファイルに保存してライブラリ/モジュールとして使い回したいものです。

AppleScript のソースコード分割の基本は スクリプティング機能追加の 「load script」コマンドです。「load script」コマンドは スクリプトファイルを script object として読み込む事ができます。script object が AppleScript でのコード分割の単位になります。script object とは、何であるかはご存知であるとします。

これから、load script コマンドによるコードの共有方法を詳しく説明しますが、ある程度の規模のライブラリになると、load script では 大変すぎて現実的でなくなります。load script コマンドを使い込まず、ModuleLoader を使われることをおすすめします。

load script コマンド と script object

まず、load される スクリプト「ModuleA」を用意します。なお、load されるスクリプトを「モジュールスクリプト」と呼ぶ事にしましょう。

property _msg : "I'm ModuleA"

on show_message()
display dialog my _msg
end show_message

そして、以下のように「MainScript A」から 「ModuleA」を load すると「ModuleA」の property や handler を使用する事ができます。

-- ModuleA を 変数 ModuleA に load
set ModuleA to load script file "Macintosh HD:Users:tkurita:Factories:Websites:scriptfactory:web-content:Monologue:SharingSourceCode:_scripts:ModuleA.scpt"

ModuleA's show_message() -- ModuleA の handler を呼び出し

このように、script object を単位としてしかコードを分割する事はできません。分割したソースコードはそれぞれ独立した script object になってしまいます。オブジェクト指向を心がければ特に不自由する事はないかと思います。

property に load script する

「MainScript A」では、実行時にスクリプトを load していました。多くの場合は、コンパイル時にスクリプトを load した方が良いでしょう。load するスクリプトが見つからない等のトラブルを避ける事ができるからです。配布にも都合がいいです。そのためには、以下のように property にスクリプトを load します。

-- ModuleA を property に load
property ModuleA : load script file "Macintosh HD:ModuleLocation:ModuleA.scpt"

ModuleA's show_message() -- ModuleA の handler を呼び出し

このように、property にスクリプトをロードすると、実行時には "ModuleA.scpt" は必要なくなります。property の値はコンパイル時に評価されてしまうからです。同じフォルダから複数のスクリプトをロードする際には、次のようにフォルダのパスを変数にしても良いでしょう。これまた、変数 _libfolder はコンパイル時のみ参照されるので、実行時には無くてもかまいません。

property _libfolder : "Macintosh HD:ModuleLocation:"
-- Module A を property として ModuleA に load
property ModuleA : load script file (_libfolder & "ModuleA.scpt")
property ModuleB : load script file (_libfolder & "ModuleB.scpt")

このように、property にモジュールをロードすれば、実行時にはモジュールファイルと縁を切ることができるのが AppleScript のいいところの一つです。

ちなみに、上の例のように複数(に限りませんが)のスクリプトをロードする際には、そのフォルダのパスを変数にすることによって、だいぶ記述量を減らすことができます。しかし、後で述べるように ModuleLoader を使う方法がおすすめです。

モジュールスクリプト間での呼び出し(その1)

複数のモジュールスクリプトを load した時、モジュールスクリプトから別のモジュールスクリプトの handler や property にアクセスすることが必要になるでしょう。これには global 変数が トップレベルのスクリプトの property と内容が共有されるという性質を使います(詳細は、「侵略の global 変数」 を参照)。

例えば、「ModuleA」の handler を呼び出すライブラリスクリプト「ModuleB」は以下のように書きます。

global ModuleA -- トップレベルの property ModuleA と値を共有

on show_message()
ModuleA's show_message() -- ModuleA の handler を呼び出し
end show_message

そして、「ModuleA」及び「ModuleB」を load するスクリプトは以下のようになります。

-- Module A を property に load
property ModuleA : load script file "Macintosh HD:ModuleLocation:ModuleA.scpt"
-- Module B を property に load
property ModuleB : load script file "Macintosh HD:ModuleLocation:ModuleB.scpt"

-- ModuleBの handler の呼び出し。ModuleB は ModuleA にアクセスしている
ModuleB's show_message()

ライブラリの管理 - MoudleLoader -

以上のテクニックは、ソースコードを分割して規模の大きなスクリプトを構築するのに必須です。また、独自のライブラリを作りコードを再利用する為にも必要でしょう。僕は、「XModules」で自分が再利用しているコードの一部を公開しています。汎用的なコードを抽出し、多くのスクリプトで使い回せば開発効率が上がるだけでなくバグも少なくなるのではないでしょうか?

さて、他の多くの言語が備えているように、ライブラリ/モジュールへのフルパスを指定するのではなく、自動的に探してくれる機能が欲しいところですが、残念な事に AppleScript にはそのような機能はありません。

そこで、僕は ModuleLoader という簡単なモジュール管理システムを作りました。load script コマンドの代わりに、ModuleLoader を使うとソースコード中にファイルパスを書き込むことなくスクリプトをロードすることができます。詳しくは、MoudleLoader のマニュアルをお読みください。

property ModuleA : module
property loader : boot (module loader) for me

ModuleA's show_message()

モジュールスクリプト間での呼び出し(その2)

「モジュールスクリプト間での呼び出し(その1)」で説明したような global 変数を経由してのモジュールの共有法方法はいくつか問題があります。

上記の問題は ModuleLoader を使うと一気に解決できます。

ModuleA をロードしている ModuleC, ModuleD を考えます。次の例のように ModuelC, ModuleD で ModuleLoader を使って ModuelA をロードします。

property ModuleA : module

boot (module loader) for me -- 単体でテストするときのみ必要

property に ModuleA をロードしているので、ModuleC, ModuleD は単体で動作させることが可能です。

load script コマンドを使って ModuleC, ModuleD をロードすると、次のような問題があります。

ModuleLoader を使うとこれらの問題が解決できます。そして、ModuleC, ModuleD を次のようにロードすると、共有されている ModuleA が同一の物になりり、最新のものにアップデートされます。

property ModuleC : module
property ModuleD : module
property loader : boot (module loader) for me

ModuleC's ModuleA is ModuleD's ModuleA
-- result : true

マニュアルの作成

使い回せるモジュールができたらマニュアルを書くことを検討してみてください。自分一人で使うにしても、使い方やハンドラなどはすぐに忘れてしまう物です。AppleScriptDoc を使えば必要最小限の手間で手軽に表示することができるリファレンスマニュアルを構築することができます。

AppleScriptDoc はソースコード中のコメントから HTML ファイルを生成します。スクリプトバンドルの場合、バンドルに Help Book として収め、ヘルプビューアで表示させることができます。そして、ヘルプビューアからシンタックスをコピーしたり、スクリプトエディタでソースコードを開いたりすることができます。