スクリプト属性を設定しよう

スクリプト属性ってなに?

 AppleScriptは「オブジェクト指向言語(OOP:Object Oriented Progremming language)」の一つです。
 オブジェクト指向言語とは、プログラムをオブジェクトという単位で分けてプログラムしやすくした言語です。
 オブジェクトは性質と振るまいをまとめたものです。
 AppleScriptでは性質=属性(property)、振るまい=命令(command)と対応付けられます。

 アプリケーションのオブジェクトを使って、すでにオブジェクトを使うことには慣れているかと思いますが、AppleScriptではオブジェクトを作ることもできます。
 このオブジェクトをスクリプトオブジェクト(script object)といいます。
 今まで使っていたアプリケーションのオブジェクトを、スクリプトオブジェクトとの区別のため、アプリケーションオブジェクトと呼ぶこともあります。

 AppleScriptでは、書かれたスクリプトはすべて「スクリプトオブジェクト」になります。
 今まで、特に書いていませんでしたが、既にスクリプトオブジェクトを作っていたわけです。
 振るまいはハンドラとして既に作ったことがありますから、今回は性質=属性を作って活用してみましょう。

スクリプトに属性を作る

 スクリプトは、特殊な変数として属性を持つことができます。
 書式は次の通り。

property 属性 : 初期値

 複数の属性を作りたい場合は、この書式を繰り返して書くだけです。
 スクリプトの持っている属性を特に「スクリプト属性」といいます。

 基本的には変数として利用できるのですが、通常の局所(local)変数と違ってスクリプト内のどのハンドラからでも使えます。
 命名規則は変数と同じですが、本書では属性と局所変数を間違えないように、属性の最後には「 _ 」(アンダースコア)を付けます。

property prop_ : "スク"

set prop_ to prop_ & "リプト"
x()
prop_
--> "スクリプト属性"

on x()
	set prop_ to prop_ & "属性"
end x

 また、propertyはスクリプト全体で使われる値ですから、runハンドラの中に書くわけではありません。
 runハンドラを明示的に書いた場合は、runハンドラの外に書きます。

property prop_ : "スクリプト属性"

on run
	display dialog prop_
end run

 スクリプト属性は変数に近いものですから、アプリケーションオブジェクトの属性のように、値を変更した時に同時に命令のような動きをすることはありません。
 例えば、Finderのwindowオブジェクトのposition属性を変更するとウインドウの位置が変わりますが、スクリプト属性を変更しても、変わるのはスクリプト属性の値だけです。

属性に命令を書く

 propertyの値には、計算式や命令、参照を直接書くことができます。
 属性の内容は、スクリプトの構文確認時に実行され、値が初期化されます。

property userName_ : "ユーザー" & "さん"
property defaultFolder_ : choose folder "フォルダを設定して下さい"
property date_ : (current date)
property defaultEditor_ : application "TextEdit"

 利用者定義命令(ハンドラ)をpropertyの初期化に使う場合、ハンドラをproperty定義の前に書く必要があります。
 ハンドラは基本的にスクリプト中に書く位置は前でも後でも関係ないのですが、ここではpropertyの後にハンドラを置くと、エラーとなりますから注意して下さい。

on x()
	return "属性定義ハンドラ"
end x

property result_ : x()

result_
--> "属性定義ハンドラ"

属性を定数として利用する

 スクリプト属性がスクリプトのどの場所からでも利用できることを使い、属性を定数(constant)として使う場合もあります。
 定数は、スクリプト中で読まれるだけで、代入されない変数です。
 他の言語では、定数を設定する専用の仕組みが用意されているものもありますが、AppleScriptには用意されていないのでスクリプト属性を利用するわけです。

 定数をスクリプトの最初に定義しておけば、スクリプトの中を隅々まで読まなくても、簡単に変更できます。
 日本語以外の言語向けに作り変えること(localize)を考える場合は、特にダイアログに表示される文字列などを書いておくといいでしょう。
 本書では分かりやすいように、定数として利用され代入が行われない属性は全て大文字で書き、単語の切れ目には「 _ 」を使います。

property ALERT_PROMPT : "これは、警告です"

display dialog ALERT_PROMPT

 スクリプト本体に直接リテラル(注1)を書く方法は、読みにくく修正が困難になりますから、値は属性を利用した定数に入れて、スクリプトの最初にまとめておくのがいいでしょう。
 もちろん、必ずそうしなければいけないわけではありません。

 デバッグ(注2)中であることを真偽値でスクリプト属性に記録しておいて、次のスクリプトの用に使うと、スクリプトの最初のIS_DEBUG属性をfalseにかえるだけで、デバッグ用に書いておいたスクリプトが実行されなくなります。
 スクリプトを作っている最中は、重宝するでしょう。

property IS_DEBUG : true

if IS_DEBUG then log "デバッグ中だよ" -- (注3)

設定の保存に属性を使う

 スクリプト属性はスクリプト終了後もスクリプト(注1)に保存されるので、前回入力された値などの設定の管理などに使えます。

 例えば、この属性を使ってスクリプトを起動した回数をカウントしたり、以前に入力した値を記憶する用途などが挙げられます。
 次のスクリプトをアプレットとして保存して、何度も起動すると、表示される起動回数が増えていきます。

property num_ : 0

set num_ to num_ + 1
display dialog "起動回数:" & num_

 エディタ上でも、コンパイルせずに[実行]を繰り返すと、起動回数がカウントされるのが確かめられます。
 コンパイルしたり、スクリプトを変更して保存すると、属性は初期値に戻ります。

 スクリプト属性はファイルに保存されますから、当然大きな文字列やリストなどを属性に入れておくと、ファイルサイズが大きくなります。
 また、スクリプトの起動にも時間がかかるようになります。
 記録不要な属性の値はスクリプトの終了前に、空文字列などの小さなサイズの値に置き換えるといいでしょう。

scriptで外部スクリプトを使う

 AppleScriptには、すでに保存されているスクリプト書類を読み込んで使うための仕組みが、いくつか用意されています。
 その中でも一番簡単なのが、scriptを使った方法です(注1)。

 読み込む書類はスクリプトライブラリのフォルダに置きます。
 スクリプトライブラリの位置は、ユーザのホーム以下の"/Library/Script Libraries/"です。

 Libraryフォルダは通常表示されていませんが、Finderの[移動]-[フォルダへ移動..]に"~/Library"を入力して移動すれば存在しています。
 もしくは、optionキーを押しながら[移動]-[ライブラリ]を選んでください。

 初期状態では"Script Libraries"フォルダはないので、ライブラリフォルダに作っておきます。
 本書は、AppleScript入門なのでスクリプトを用意しておきましょう。
 以下のスクリプトでライブラリが開かれ、スクリプトライブラリのフォルダが作られます。

set pathToScriptLibrariesFolder to ¬
	POSIX path of (path to library folder from user domain) ¬
	as POSIX file -- (注2)

tell application "Finder"
	open pathToScriptLibrariesFolder
	make folder at pathToScriptLibrariesFolder ¬
		with properties {name:"Script Libraries"}
end tell

 スクリプトライブラリは頻繁に使うことになりますので、Dockなりなんなりに登録しておくといいでしょう。
 そして、先ほど「設定の保存に属性を使う」で作ったスクリプトを"counter.scpt"という名前で、スクリプトライブラリに保存しておきます。

 さて、これで準備は整ったのでスクリプトが読み込めます。
 読み込んだスクリプトは、アプリケーションと同様にtellブロックに指定して利用できます。

 [実行]を繰り返すたび、カウントアップされていくことが確認できると思います。
 これで動くのは「暗黙のrunハンドラ」があるからです(注3)。
 run以外のハンドラが読み込んだスクリプトに存在していれば、それも利用できます。

tell script "counter" -- (注4)
	run
end tell

 属性も同じようにして使えるのですが、Itsを属性の前に付けて(注5)、tellブロックで指定したスクリプトオブジェクトのものであることを明示する必要があります。

tell script "counter"
	its num_
end tell

 一度使ったスクリプトオブジェクトはスクリプト内で使い回されるので、気軽に何度も呼び出して構いません。
 呼び出すたびにファイルを読んだりはしないのです。

 スクリプトバンドルやアプレットの場合、バンドルを開くとContentsフォルダその中にResoucesフォルダがありますが、そこにScript Librariesフォルダを作って、"Contents/Resouces/Script Libraries"としておけば、まず最初にこのフォルダにあるスクリプトをscriptに割り当てます。
 エディタでバンドル情報を見れば、Resoucesフォルダが表示されるので、そこで追加するのが簡単です。
 スクリプトが複雑化した場合に、ファイルを分割できて、なかなか便利です。

use文で外部スクリプトを使う

 さらにスクリプトライブラリを便利に使う方法として、useがあります(注1)。
 use文はproperty文と同じようにrunハンドラの外に書きます。
 書式は次の通り。

use スクリプトオブジェクト名 : script "スクリプトファイル名"

 では、先ほど「scriptで外部スクリプトを使う」で行ったのと同じ動作のスクリプトを書いてみましょう。

use CounterScpt:script "counter"

tell CounterScpt
	run
end tell

 use文で指定したCounterScptは、propertyで設定した属性と同じ扱いになります。
 つまり、スクリプト中どこからでも呼び出せます。

 スクリプトライブラリに置いたスクリプトが増えると、フォルダに分けて分類したくなると思います(注2)。
 そうすると、scriptに指定する文字もフォルダを含んで長くなりがちです。
 そういう場合に、use文を使って短いスクリプトオブジェクト名を付けておいて、そちらを使い回せばスクリプトが見やすくなります。

 なお、use文を使うとOSAXが使えなくなります。
 use文を使ってかつOSAXも使いたいときは、次のように記述します(注3)。

use scripting additions

 正直面倒臭いし、なんでこういう仕様になっているのかよくわからないと思います。
 おそらく、Appleは今後OSAXを極力使わない方向にAppleScriptを持って行きたいのではないでしょうか。
 そうだとすれば、この仕様も納得できます。

loasd script命令で外部スクリプトを使う

 StandardAdditions.osaxのload script命令を使えば、スクリプトライブラリ外のスクリプトも読み込めます。
 例えば、ユーザーフォルダの下にあるスクリプトフォルダ(~/Library/Scripts/)に、"counter.scpt"を置いた場合、次のようになります。

set thePath to ¬
	POSIX path of (path to scripts folder) & "counter.scpt"
set CounterScpt to load script file thePath

 あとはuseと同様にtellブロックの中で使います。
 ここでは局所(local)変数に代入していますが、propertyに代入してしまえば、ほぼuseと同じように使えます。

スクリプトを保存する

 読み込んだスクリプトのスクリプト属性の値は、元のファイルに保存されません。
 大抵はそれで問題ないのですが、どのスクリプトから見ても共通の属性が欲しい場合もあります。

 スクリプトを保存したい場合は、StandardAdditions.osaxのstore script命令を使います。
 次のようなスクリプトで、スクリプトオブジェクトを属性ごと保存することができます。
 CounterScptのパスに、既にスクリプトオブジェクトが入っていて、theAliasにはスクリプトファイルの参照が入っているとします。

store script CounterScpt in theAlias  replacing yes

 例えば、スクリプト属性の設定しか書いていないスクリプトを作って、それを読み込み、また保存することで、簡単に設定の保存が行えるスクリプトを作ることができます。
 まず、次のようなスクリプトを書いて、"globalProp.scpt"と名前をつけてスクリプトライブラリに保存します。
 storeMyself()ハンドラは、path to meで自身のパス(エイリアス値)を得て、store script meで自分(スクリプトオブジェクト)を保存しています。
 つまり、自分自身にスクリプトオブジェクトを保存しています(注1)。

property prop_ : 1

on storeMyself()
	store script me in (path to me) replacing yes
end storeMyself

 次に、そのスクリプトを利用するスクリプトを作ります。
 エディタ上で[実行]と[コンパイル]を繰り返しても、prop_が初期化されずにカウントアップしていくことが確認できるかと思います。

use CounterScpt : script "globalProp"
tell CounterScpt
	set its prop_ to (its prop_) + 1
	storeMyself() -- これで保存
	its prop_
end tell

 設定がスクリプトの外にあるので、複数のスクリプトで利用できて便利ですし、write命令よりも手軽に利用できるのもいいところです。

【今回のまとめ】

スクリプト属性は、

  • スクリプトオブジェクト全体で利用できる
  • ファイルに自動的に保存される

外部のスクリプトは、

  • スクリプトライブラリはscriptで利用できる
  • use文で属性として使う
  • load script命令だとどこのスクリプトでも読み出せる
  • store script命令で保存できる