load script 命令におけるスクリプトの再利用

前回までの AppleScript Programming Tips を読み返していて...こりゃだめだと反省。リファレンスはいらないですね。ということでリファレンスじみたことはやめます。すいませんが記事を修正、削除しました。リファレンスの方は別にまとめようかと画策中です。今回は load script 命令。run scriptstore script とくれば、load script 命令にも触れないわけにはいきません。

load script 命令というのは既にあるスクリプトを読み込んでスクリプトオブジェクトとして利用するための命令...言い換えると、既存のスクリプトを使い回して楽をするために必須の命令です。

少し、話が横道にそれますが...基本的には AppleScript のスクリプトって使い捨てだと思います。動けばそれでいいし、目的を達成できればそれで十分です。多くの人にとって AppleScript はそういうもので、既存のスクリプトを使い回すという概念があまり浸透していないように思えます。

多くのスクリプトが使い回すことを前提に組み立てられていません。多くのスクリプトは以下のような感じではないでしょうか。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return
    try
        repeat with this_item in selected_items
            set locked of this_item to not (locked of this_item)
        end repeat
    end try
end tell

Finder で選択している項目のロックを ON/OFF するスクリプトです。直線的なスクリプトです。もちろん、これでいいですし、エラー処理はしていませんがきちんと動きます。私の環境でもこのようなスクリプトが大部分を占めています。

AppleScript のいいところは、このような気楽な書きなぐりのようなスクリプトで十分目的を達成できるというところです。目的を達成できれば、これ以上スクリプトを保守/管理する気は起きません。次にこのスクリプトを開いて中身をみるのはいつになることか...。

また、AppleScript というのは個人の環境に強く依存するプログラム言語です。私に必須のスクリプトが必ずしもあなたに必須のスクリプトではないのです。なぜなら、AppleScript がアプリケーションを操作するための言語だからです。私は OmniOutliner を使ってこの書類を書いています。しかし、あなたは違うでしょう。私が何らかのアプリケーションを欲するとき、AppleScript に対応しているかどうかを気にかけますが、あなたはそうではないでしょう。

個人の環境に強く依存する AppleScript は、同時にスクリプトの書き方にも影響しています。

ハンドラの記述の仕方、参照の記述の仕方、命令の使い方、文字列の組み立て方、繰り返しの使い方、変数の命名方法、グローバル変数を多用するかどうか、ローカル変数を明示するかどうか等々...、様々なスクリプトを見てきましが、多種多様です。

AppleScript は同じことでも複数の書き方ができるため個性的なスクリプトが多いです。個性的なスクリプトを記述する人ほど、他人のスクリプトをそのまま使うことに忌避感を覚えます。このことが、使い回せるスクリプト(ライブラリ、モジュール)が公開されても利用されることがないことの遠因なのでは?思います。

駄法螺ですが。少なくとも自分自身にはそのような傾向があるかなと思います。

閑話休題。上記の Finder 項目のロックのトグル処理のスクリプトを開いて中身をみることがいつになるかは分かりませんが、Finder の選択項目に何らかの処理を施すスクリプトはそのうちにまた作ると思います。例えば、選択項目をゴミ箱に移動する、選択項目にラベルを付ける、選択項目の名前を変更する...。

スクリプトを使い回せるようにするにはどうすればいいのか、Finder の選択項目の処理を例に少し考えてみましょう。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return

    repeat with this_item in selected_items
        set label index of this_item to 1
    end repeat
end tell

このスクリプトは選択項目のラベルをオレンジ色にするスクリプトです。このスクリプトを見て、いくつかのことに気がつくと思います。

  1. 繰り返しの中身だけがロックのトグル処理のスクリプトと異なる
  2. 選択項目の処理というのは(エラー処理を入れていないものの)定型文だ

これらの部分です。スクリプト再利用の鍵となるのは処理の細分化です。個々の処理をハンドラで切り離せるかどうかを考えることがスクリプト再利用の第一歩です。まず、繰り返しの中身の処理はハンドラにすることができます。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return

    repeat with this_item in selected_items
        my label_orange(this_item)
    end repeat
end tell

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

ロックのトグル処理もハンドラにしました。たかだか一行の処理をハンドラにすることもないのでは?と思うかもしれませんが、ハンドラとはこういうものなのです。では、「選択項目の処理というのは定型文」という部分を考えてみましょう。選択項目の処理に毎回毎回この定型文をコピペするなり、記述するなりするのでしょうか?

それはなんだか面倒だ、と思ったならこの部分もハンドラにしてしまってください。

Script Editor で開く

on process_Finder_selection()
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return

        repeat with this_item in selected_items
            my label_orange(this_item)
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

単にハンドラにしただけです。ですが、これでは使い勝手が悪いです。繰り返しの部分を毎回書き変える必要があります。この部分を目的に応じてほかのハンドラに差し替えれるようにできれば、もう少し使い勝手がいいものになります。

Script Editor で開く

property action : missing value

on run
    set action of me to label_orange
    my process_Finder_selection()
    set action of me to toggle_lock
    my process_Finder_selection()
end run

on process_Finder_selection()
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return

        repeat with this_item in selected_items
            my action(this_item)
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

これで個々のハンドラはそれぞれ単一の処理を行うようになりました。今のところ、このことがどのような意味を持っているのかが分かりにくいかもしれません。しかし、使い回すことを考えながらスクリプトを作っていると、その利点が分かってくることと思います。

上記のスクリプトをデスクトップに FinderUtility.scpt として保存しておきます。load script 命令を使ってスクリプトを読み込んでみます。

load script 命令は AppleScript ファイルを引数にとり、file 参照か alias で指定します。ファイルはコンパイル済みスクリプトかアプリケーション形式で保存したスクリプトファイルです。実行専用で保存されたアプリケーションでもかまいません。返り値はスクリプトオブジェクトです。

Script Editor で開く

set the_object to load script (choose file of type {"scpt", "app", "scptd"} without invisibles)
--> «script»

では FinderUtility.scpt を読み込み、選択項目の処理をしてみましょう。

Script Editor で開く

set the_folder to path to desktop as text
set the_file to the_folder & "FinderUtility.scpt"

set FinderUtility to load script file the_file

tell FinderUtility
    -- 選択項目のラベルをオレンジに
    set action of it to label_orange of it
    process_Finder_selection()
    delay 1
    -- 選択項目のラベルをなしに
    set action of it to label_none of me
    process_Finder_selection()
end tell

on label_none(this_item)
    tell application "Finder"
        set label index of this_item to 0
    end tell
end label_none

FinderUtility.scpt を読み込んで、action 属性に実際の処理を記述したハンドラを設定し、processFinderselection ハンドラを呼べばいいだけです。action 属性に設定するハンドラは別段どこのスクリプトにあってもかまいません。ここでは同一のスクリプト内に書いていますが、ハンドラだけを別のスクリプトに保存しておき load script 命令を使って読み込んだものでもかまいません。

ちなみに、ハンドラに引数としてハンドラを渡さないのはどうしてなのか?

Script Editor で開く

on run
    my process_Finder_selection(label_orange)
end run

on process_Finder_selection(action)
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return
        repeat with this_item in selected_items
            try
                my action(this_item)
            on error m number n
                display dialog {n, return, return, m} as text
            end try
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

これだとエラーになるからです。このエラーを回避するためにハンドラをいったんスクリプトオブジェクトの属性におさめ、その属性を参照するようにしています。スクリプトの実行時にハンドラを動的生成し、処理を切り替えるときによく使われるテクニックです。他にもハンドラにスクリプトオブジェクトを渡して処理を行うというのもよく使われるテクニックです。

Script Editor で開く

script FinderAction
    on do_action(this_item)
        tell application "Finder"
            (* 何らかの処理 *)
            log "Call do_action of FinderAction"
        end tell
    end do_action
end script

script SEAction
    on do_action(this_item)
        tell application "System Events"
            (* 何らかの処理 *)
            log "Call do_action of SEAction"
        end tell
    end do_action
end script

on run
    tell application "Finder"
        set selected_items to selection as alias list
        if selected_items is {} then return
        repeat with this_item in selected_items
            my do_action(this_item, SEAction)
            my do_action(this_item, FinderAction)
        end repeat
    end tell
end run

on do_action(this_item, this_object)
    this_object's do_action(this_item)
end do_action

このような使い方は AppleScript ではあまりしませんが、トリッキーなものではありません。知っていると、スクリプトでできることの幅が広がりますし、保守/管理も楽になります。

0 件のコメント :

コメントを投稿