ラベル Review の投稿を表示しています。 すべての投稿を表示
ラベル Review の投稿を表示しています。 すべての投稿を表示

「あなたもマジックができる」というけれど...

たまには AppleScript とは違う話題などを。iPhone アプリあなたもマジックができるについて。まぁ、iPhone アプリのことですが、マジックに関することを少し。殴り書きですが。

実は、買うつもりはなかったのです...。

AppBank さんのレビュー([iPad, iPhone] あなたもマジックができる: 誰でも手品がマスターできる!1460)を見てから、App Store でレビューを見ていてボタンを押し間違えてしまいました。

せっかく買ったのですからレビューと思ったのですが、読み返してみてレビューというより、問題提起、疑問の提示みたいになってしまいました。

このアプリのレビューを書いているサイトは AppBank さんを始め、いくつかあり、おおむね高評価なのですが、それらとは全く違うレビューになります。たまたま、少しマジックのことを知っているので、レビューというより批判です。

ところで、こういったアプリ(基本的には動画を表示するだけのアプリ。アプリというより、こんなもの Web ページでもいいでしょ、わざわざアプリにする必要ないでしょ)のレビューはどこまで内容に突っ込んでいいのでしょうか。このようなアプリだけではなく、例えば、電子書籍のようなもの。最近、読んだものでは iPhoneとツイッターは、なぜ成功したのか?。

こういうものって内容のレビューをすればいいのでしょうか。それとも、アプリの使い勝手をレビューすればいいのでしょうか。アプリの使い勝手ということでいえば、両者とも最低です。なぜ、iPhone や Twitter の使い心地を論じている書籍のアプリがあれほど使いにくいのかがいまだに分かりません。

どうして、誰も使いにくいって言わないのだろう。

iPhoneとツイッターは、なぜ成功したのか?のレビューではないので多くは書きませんが、どうして、林信行さんがあの内容であのアプリを承諾したのかが理解できません。あの内容なら、ぜひ、こだわってほしかったです。使い勝手に。

あれじゃ、書籍をそのまま流し込んだだけじゃないの?一つ言うなら、時々、注釈が入るのだけど、その注釈は注釈の説明に移動するリンクが張られているのだけど、その注釈の説明はおおむね、章の最後に記述されている。多くの書籍はそのようになっていますし、このアプリでも踏襲されています。

問題は、書籍と同じ構成のため、ページをめくっていくと再度同じ注釈に出くわすということです。こんなもの、飛ばせよ。一度、見たのだから。断言するけど、これは書籍のデータをそのまま流し込んだだけ。iPhone には iPhone の見え方、見せ方があるでしょ。これだけのことで内容は良くても、読む気はなくなってしまいます(特に内容が内容なだけに)。

似たようなことはあなたもマジックができるにも言えます。マジックの手順の説明は 1, 2, 3...と番号順に進んでいくのですが、時々、他のマジックの手順 5 を参照してちょうだい、みたいなことが書かれている。書かれているのだけど、そこに移動するリンクはない。当然、参照して戻るためのリンクもない。

えっとね。マジック入門のようなアプリを作っているんでしょ。いってみれば、専門書。それが、なんでこういうところの使い勝手が悪いの?

マジックに関する書籍で一番面倒なのは、いろんなページに行ったり来たりしなければならないこと。アプリなんだし、相互参照ぐらい簡単でしょ。たとえ、それが動画でも。

iPhone や iPad などのモバイルアプリや電子書籍はまだまだ混沌とした状況だから玉石混淆...というのは分かります。しかし、手を抜いているようにしか見えないこれらの問題は必ず自分たちの首を絞めます(もちろん、真っ先にレビューしなければいけないはずのマジシャンの首もね)。

そして、これはあなたもマジックができるのレビューを掲載しているいくつかのサイトの問題でしょうけど、ほんとに買ったの?ほんとに買って中身を見てみたなら腹がたったと思うのですが、そういうレビューは見かけない。レビューを書くなら、いいところだけではなく、悪いところも書いてください。

で、なんで腹がたったかというと、解説の文章がアマチュア以下だから。少なくとも金をとって、マジックの演技をし、解説しているのなら、プロ根性を見せろと言いたい(マジックで演技を見せ、なんらかの報酬を得ればその人はプロです。本人の意思に関係なく。また、文章を書いて金をとっているのでしょ。もう少し、まともな文章を書いてください)。

で、内容なのですが...。

評価は分かれるでしょうね。おそらく、全くマジックをしたことのない初心者やマジックに興味のある人には、手頃な値段ということもあって受け入れられるでしょう。内容も数だけはありますし。

いわゆるマニアには酷評されるでしょう。紹介されているマジックが初心者向きだからではありません。作品のクレジットもなければ、誰が演じているのかも分からない。何を見せたいのかも分からなければ、パズルだけを紹介しているその姿勢(あの実演動画はいったいなにが不思議なのか?ビックリ箱としての驚きはあるでしょうが、それだけです)ゆえに。

客観的に見るなら、少しマジックをかじったことのある人が、小銭を稼ぐためにマジックの知識を切り売りしている...といった感じがします。

個人的には情報商材かよ、という気がします。

マジックをしたことがなく、マジックに興味ある、覚えたい、という人にはお勧めできるか?

マジックをすることはないけど、興味本位で見てみたいというだけなら購入されてもいいのでは?満足感は得られるでしょう。

意欲がある人には、お勧めしません。意欲がある人には初期投資が高くついても、書籍をお勧めします。英語でも構わないなら、Magic: The Complete Course (Book & DVD)。日本語でなら、華麗に決める本格マジック。どちらも DVD で一流のマジシャンによる演技、解説が見られます。

でも...こういうアプリってこれから増えるんだろうな。

収録されているマジックは以下になります。といっても、現象を書くと分かる人には分かってしまうので詳しく書くことはできません。

  1. 混沌な予言
  2. 4エース出現
  3. コイン移動
  4. 予想外のカード当て
  5. コイン消失
  6. ティッシュの復活
  7. 不思議な予言
  8. コイン貫通
  9. ミックスアップ
  10. 恋人の出会い

ところで、マジックで一番大事なものって何でしょうか。

それは、「マジックでなにを見せるか、なにを言いたいか、なにを表現したいか」だと思います。ごくごく当たり前のことを書けば、マジックなんてタネさえ知れば誰にでもできます。しかし、タネを知り Mr.マリックがやっていることと同じことをしても、同じように万雷の拍手を得ることはできません。Mr.マリックにはマジックを使って表現したいことがあります。マジックを使って伝えたいことがあります。

マジックというのはただの道具です。それ自体は面白くもなんともありません。面白くなるかどうか...人に感銘を与えるかどうかは、演技者が道具をどう扱うかにかかっています。

残念ながらあなたもマジックができるには「伝えたいこと」も「表現したいこと」も「自己主張」もありません。あったのかもしれませんが、伝わってきませんでした。どちらかというとマジックのタネ教えます、といった暴露サイトと同じレベルです。

もし、このアプリケーションの作成者がマジシャンを増やしたい、マジシャンの蒙を啓きたいと思ってこのアプリを作ったのだとしたら、やり方が間違っています。もっと、丁寧に作ってください。

Google Chrome を入れてみた

Google Chrome の Mac、Linux 正式版が出たってことで、インストールしてみた。

興味はあったけど、インストールはしていなかった。興味はあったけど、関連するような情報は集めていなかった。ほぼ、無垢(?)な状態で無邪気に、新しいおもちゃを与えられた子供のようにわくわくしながら Google Chrome をインストールし、使い方を学び、環境を構築するまでのメモ in Snow Leopard。

Google Chrome のサイトよりディスクイメージをダウンロードし、Applications フォルダにコピー。

起動すると「Google Chrome へようこそ」というウィンドウが表示される。

google_chrome_welcome.jpg

Safari から設定のインポートを行い、Google へのレポートの送信にチェック(デフォルトの設定のまま)。「Google Chrome を起動」ボタンを押す。

なにかウィンドウが表示されるが、一瞬で消えてしまうので詳細は分からず。おそらく、インポートの状況を表すウィンドウだったのだろうが、勝手に消えない方が嬉しかった。

Google がデフォルトのホームページ。

メニューをチェック。基本的には Safari と似たようなメニュー構成、および、ショートカット(Keyboard and mouse shortcuts : Getting started - Google Chrome Help)。ヘルプが一部翻訳されていないのは、ご愛嬌。タブブラウザだからか、「新規ウィンドウ」より「新規タブ」が先にある。

Safari のようにブックマークバーのショートカットキーは割り当てられないよう。

ヘルプは Google の Web ページが表示される。

「表示」メニューに「開発/管理」メニューがある。「ソースを表示」はカラーリングと行番号が表示されて見易い。「デベロッパーツール」と「JavaScript コンソール」は Safari と同じ(Webkit!)。独特なのは「タスクマネージャ」かな。タブごとにプロセスを割り当てる Google Chrome ならでは。試しにタブのプロセスを終了させると、なかなか愛嬌のあるアイコンが表示される。一昔前の Mac を思い出させる。

google_chrome_crash.jpg

環境設定を開く。

「基本設定」タブで起動時に表示するページの設定、検索エンジンや既定のブラウザにするかどうかの設定ができる。ツールバーという見慣れないものがあるので、チェックを入れてみる。

ホームメニューはともかく、ページメニューとツールメニューがツールバー(ではないけど、似たようなもの)に右端に追加される。これは移動させられない?

「個人設定」タブで他のコンピュータとの同期(Google アカウントが必要)やパスワードの管理(キーチェーンアクセス.app で管理)、他のブラウザからの設定のインポートができる。

ブラウザからのインポートは、Safari からはパスワードと検索エンジンのインポートはできないみたい。Firefox からは可能。これは、Safari の仕様のため?

「高度な設定」タブでいろいろな設定ができる。設定項目が多いためか、ウィンドウ内に収まらない。そのためスクロールして設定項目を表示させる。

どうも Google Chrome の設定は、ボタンを押すと別でウィンドウかシートが表示され、詳細な設定を行うようになっているよう。これはこれで不必要な項目が並ばなくて、ごちゃごちゃしなくていいのだけど、いちいちウィンドウを閉じたり面倒でもある。環境設定のタブの数は増やしてもいいので、別のウィンドウを極力少なくしてほしいな。

また、「高度な設定」にいろいろ詰め込みすぎたためか必要な設定を探すのに時間がかかった。最初、フォントの設定がどこにあるのか分からなかった。

閲覧の履歴の削除は期間を指定して削除するみたい。だけど、閲覧履歴をどれぐらいの期間保存しておくのかの指定はどこでできるのだろう?もしかして、Google Chrome は全期間の履歴を保持するのがデフォルト?

環境設定で行える設定を行い、とりあえず、ブラウジング。自分のサイトなんかを見てみる。

基本的にブラウザに求めるものはシンプルさ。拡張機能やユーザースクリプトは、ほとんど利用したことがない。ブラウザに用意されている機能だけですませることが多い。ブックマークレットさえ、ほとんど利用しない。Google Chrome のインストールを機にいろいろと入れてみる。

といっても、Google Chrome で何ができるか分からないのだけど。

そういうわけで検索。

検索しながら気づくのだけど、URL アドレスが表示されているところ...。ホストの部分以外は灰色で表示される。また、ダブルクリックでホストの部分だけ選択できる。これは、ちょっと便利。

リンクにマウスを持っていくと、ウィンドウの下部に URL がひっそりと表示される。が、全部は表示されない。全部表示させられるのかな?これは、Safari にも欲しいな...。

ページの読み込みは速い。Google Chrome のホーム画面は、Safari の Top Sites よりも情報量が多いのだけど、なぜかシンプルで使いやすいと思った。ちなみに Safari の Top Sites は使っていない。

google_chrome_new_tab.jpg

とりあえず、Google Chrome まとめWiki を参考に色々と試してみる。Windows での情報だけど。

アドレスバーに about: と入力し、リターンキーを押すと様々な情報を得ることができるのだけど、現在ではいくつか利用できないものがある(about:stats など。Windows と Linux では使えるようだ)。

アプリケーションショートカットの作成も行えない...。これはちょっとショック。

すべての起動オプションは試していないけど、使えるみたい。Terminal などで以下を実行すれば、シークレットモード(Safari のプライベートブラウズのようなもの)で Google Chrome を起動できる。

$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --incognito

拡張子 command のシェルスクリプトファイルを作れば、ダブルクリックで起動オプションを適用して Google Chrome を起動できる。

#!/bin/sh

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --incognito &

exit 0

これをエディタで記述し、拡張子 command で保存する。Terminal で実行権限を与える。

$ chmod +x 保存したファイルのパス

ファイルをダブルクリックすることで Google Chrome をシークレットモードで起動できる。いちいちターミナルのウィンドウが開いてイヤだ...という方は、[Mac OS X] シェルスクリプトとかの CUI アプリケーションを Mac OS X 方式の .app にする方法 [簡単 5 ステップ]を参考にアプリケーションにしてしまうといいと思う。

これよりも簡単な方法は、拡張子を sh にしてスクリプトメニューに放り込んでおく方法。スクリプトメニューはなかなか優れもので、シェルスクリプトも実行できる(シェルスクリプト以外の Perl や Ruby や Python スクリプトでも)。

script_menu_in_shell_script.jpg

ユーザースクリプトは拡張機能として ~/Library/Google/Chrome/Default/Extensions にインストールされる。とりあえず、以下のものを入れてみた。

Create Link は、AppleScript でまともな操作ができないので必須。他にもいろいろあるみたいだけど、量が多いので、また後日。

拡張機能はわりと簡単に作れるようなので、AppleScript の対応を待つよりも拡張機能で対処した方が早いかも。

検索エンジンの追加、キーワードによる検索も利用可能。アドレスバー(Omnibox と呼ぶらしい)が地味に便利。アプリケーションショートカットは作れないけど、キーワードによるショートカットはできますね。

google_chrome_search_engine.jpg

これでアドレスバーに gmail と入力し、リターンキーを押すと Gmail が表示される。

ダウンロードと全履歴からの検索が便利。

AppleScript にはいちおう対応はしているけれど、ブラウザとして必要な機能は提供されていない。Automator のサービスは一部対応。アドレスバー(Omnibox というらしい)での文字列選択時にはコンテクストメニューからサービスの実行が可能。Web ページ上の文字列選択ではサービスは利用できない。

Omnibox の文字列選択からは文字列を選択したときのサービスが利用できるので、「URL を開く」を使えば、同じページをデフォルトのブラウザで表示することは可能。

とりあえず、一日使った感想。

起動が速いし、動作はきびきびとしている。Safari を常用している人なら、ショートカットキーも同じような感じだしそれほど違和感なく利用できると思う。個人的には常用してみようと思う。

ModuleLoader.osax の真価

「load script を置き換える」に続き、ModuleLoader.osax のことなどを。ModuleLoader.osax の機能をOSAX としてではなく、Script Editor のプラグインとして利用できないのかなぁ...などと妄想する日々。この記事に関して Script factory さんが補足記事を書いてくれています。ModuleLoader.osax の理解が深まると思いますので参照してみてください。

ところで、Windows で自動化を行うソフトとして WinMacro なるものが紹介されていますね。この記事を読んで...QuicKeys?

よくよく考えてみたらこういう OS 全体の操作を記録、操作するようなソフトウェアって Mac に古くからありますね。いまなら Automator でしょうか。AppleScript は記録がろくに動かなくなってしまいましたが...。

まぁ、Windows のことはよくわからないので OS の自動化ソリューションにどのようなものがあり、どれくらいの需要があるのかは知らないのですが。

さて。

まずは、お詫びです。「load script を置き換える」で次のスクリプトが動かなかった件ですが...ModuleLoader.osax のバージョンが古い為に起こったエラーでした。

on run
    -- /Library/Scripts 以下から AppleScript Help.scpt を検索
    -- additional paths オプションで検索パスを指定
    -- 検索パスを other paths オプションで additional paths で指定したパスだけに制限
    set modules_folder to path to scripts folder from local domain
    find module "AppleScript Help.scpt" additional paths ¬
        {modules_folder} without other paths
end run

最新版(2.2.1)にしたら動きました。...すいませんでした。お手数をおかけいたしました。

「load script を置き換える」では ModuleLoader.osax の基本的な機能だけを使ってみました。しかし、ModuleLoader.osax が真価を発揮するのはここからです。

次のスクリプトを Value.scpt として ModuleLoader.osax の検索パスの通ったフォルダに保存しておきます。単純な値を保持しておくだけのスクリプトオブジェクトです。

Script Editor で開く

property _value : missing value

on set_value(val)
    if val is _value of me then return
    set _value of me to val
end set_value

on get_value()
    return _value of me
end get_value

そして、次のスクリプトを Value Wrapper.scpt として保存します。

Script Editor で開く

property value_object : module "Value"

on get_value()
    return value_object of me
end get_value

on set_value(val)
    if value_object's get_value() is val then return
    value_object's set_value(val)
end set_value

on _log()
    log (value_object's get_value())
end _log

たいして意味のないスクリプトです。Value.scpt をラップしただけで、メソッドは Value.scpt のメソッドを呼び出すだけのものです。最後にクライアント。次のスクリプトを Client.scpt としてデスクトップにでも保存しておきます。

Script Editor で開く

property value_object : module "Value"
property wrapper : module "Value Wrapper"
property loader : boot (module loader) for me

on run
    tell value_object
        log get_value()
        --> missing value
        set_value("Hello, world")
    end tell

    tell wrapper
        _log()
        --> "Hello,world"
        set_value("Hello, ModuleLoader")
    end tell

    tell value_object to log (get_value())
    --> "Hello, ModuleLoader"

    log (value_object is wrapper's get_value())
    --> true
end run

Value.scpt と Value Wrapper.scpt を読み込み、それぞれのメソッドを呼び出しています。このスクリプトのポイントとして次の点が上げられます。

  • 依存しているモジュールが常に最新の状態に保たれる
  • 同一のスクリプトから読み込まれた複数のスクリプトオブジェクトが同一のオブジェクトを参照している

これらは前回の load module では行えなかったことです。細かく見てみましょう。

Value.scpt は単純なスクリプトです。Value Wrapper.scpt は Value.scpt を読み込み、それを操作するスクリプトです。Client.scpt は Value.scpt を読み込み、直接それを操作し、同時に ValueWrapper.scpt も読み込み、オブジェクトを介して Value.scpt から読み込んだオブジェクトを操作します。

スクリプトの関係

load script でそれぞれのスクリプトをこのように読み込んだ場合、Value Wrapper.scpt で参照している value_object と Client.scpt で参照している value_object は異なったオブジェクトになります。しかし、ModuleLoader.osax を使って読み込んだ場合、両者の value_object は同じオブジェクトを参照します。

また、Value.scpt を再編集した場合、従来なら Value Wrapper.scpt、Client.scpt 共に再コンパイルしないと Value.scpt の変更は反映(再読み込み)されませんでしたが、ModuleLoader.osax は再コンパイルをせずとも最新の状態を反映してくれます。

これは、property の仕様(オブジェクトの属性、状態を記憶しておく)を覆すような動作ですので、問題といえば問題なのですが...。

AppleScript は基本的にイントロスペクションが弱い。スクリプトの内部からスクリプトについて調べることができません。例えば、グローバル変数の一覧を取得したり、スクリプトオブジェクト、ハンドラについて調べることもできません。この辺りのことができれば少しは違うのですが、現状ではスクリプトから AppleScript の内部にまで手を入れることができません。property の仕様を変更することでしか他の言語にあるようなモジュールの読み込みを実現できない...というのが AppleScript の問題点なのかもしれません。

まぁ...そういうことは置いておき、複数のスクリプトオブジェクトが同じオブジェクトを参照する...ということなら ModuleLoader.osax を使わずに行うことは可能です。方法は 2 通りあります。

  1. オブジェクトの参照をオブジェクトに渡す(オブジェクトコンポジション)
  2. オブジェクトをシングルトンとして設計する

オブジェクトコンポジションは問題なく行えますし、シングルトンは...厳密にはシングルトンにはできませんが、似たようなことはできます。どうするかは...またの機会にでも。

いずれにしても、どの方法を利用するかは作るスクリプト次第です。load script を用いるか、スクリプトバンドルと load script を組み合わせるか、ModuleLoader.osax を利用するか...。選択肢が増えることはいいことです。

スクリプトを見ていきます。

property value_object : module "Value"

この部分は property で指定した変数に(ここでは value_object)モジュールを読み込む指定を行っています。つまり、まだモジュールは読み込まれていないのです。モジュールが実際に読み込まれるのは boot 命令が評価されたときです。

property loader : boot (module loader) for me

まず、module loader 命令がスクリプトオブジェクトを読み込むスクリプトオブジェクトを生成します。次に boot 命令で module loader が返すオブジェクトを使ってスクリプトを読み込み、かつ読み込んだスクリプトオブジェクトが依存しているスクリプトを読み込みます。

つまり、boot 命令が評価されたときに Client.scpt は自身の属性 value_object と wrapper にスクリプトを読み込み、同時に Value Wrapper.scpt の属性 value_object にもスクリプトを読み込んでいるのです。

では、Value Wrapper.scpt は単体では動かせないのかというと...その通りです。

property value_object : module "Value"

この時点ではまだスクリプトは読み込まれていないため、Value.scpt のメソッドを実行することはできません。もちろん、単体でテストを行いたいときはあるでしょう。そういうときは run ハンドラなどで boot 命令を使用します。

Script Editor で開く

property value_object : module "Value"

on get_value()
    return value_object of me
end get_value

on set_value(val)
    if value_object's get_value() is val then return
    value_object's set_value(val)
end set_value

on _log()
    log (value_object's get_value())
end _log

on run
    -- Value Wrapper のテストを行うため、モジュールを読み込む
    boot (module loader) for me
    _log()
    --> missing value
    set_value("Hello, world")
    _log()
    --> Hello, world
end run

ところで... module loader 命令が返すスクリプトオブジェクトとはいったいなんなのでしょうか?

結論から書くと、ModuleLoader.osax が入っていたディスクイメージ内にある loader.applescript なのでした。だから loader.applescript のメソッドを呼び出そうと思えば呼び出せます。それがいいことかどうかはともかく。

ModuleLoader.osax をいろいろと触っていてふと、思ったのですが...同一のスクリプトファイルから個別のスクリプトオブジェクトを読み込むことはできないのでしょうか?

先にも見たように ModuleLoader.osax の boot 命令を使って同一のスクリプトファイルから読み込むと、依存しているスクリプト間では同一のスクリプトオブジェクトを参照するのでした。では、そうしたくないときは?

Script Editor で開く

property object_A : module "Value"
property object_B : module "Value"
property loader : boot (module loader) for me

on run
    object_A is object_B
--> true
end run

これだと同じスクリプトオブジェクトを参照してしまいます。load module 命令を使うのかな。

Script Editor で開く

property object_A : load module "Value"
property object_B : load module "Value"

on run
    object_A is object_B
    --> false
end run

しかし、これだと ModuleLoader.osax のモジュールが依存しているモジュールの自動アップデート機能が利用できません。boot 命令で個別のオブジェクトを作成するのはどうすればいいのでしょうか...?ちょっと、方法を思いつきません。

ModuleLoader.osax はここまで見てきた以外の機能も提供しています。プロジェクトごとの個別のライブラリの作成やモジュールを読み込んだときにフックをかけることもできます。また、モジュール間の依存関係を調べたり、検索パスにあるモジュールの検索を行うこともできます。それらは詳解しませんが、マニュアルと用語説明を参照すると理解できると思います。

最後に...。

AppleScript には他の言語のように共有されている、よく利用されている共通したライブラリというものがありません。例えば、Perl の CPAN のように。Python なんかは電池内蔵といわれるぐらいライブラリが充実しています。

AppleScript には足りない命令、メソッド、関数が多くあります。AppleScript の歴史は長いです。が、これが標準といわれるほどのライブラリは存在しません。歴史が長く、足りないものが多いにも関わらず、それを補うライブラリがない。

多くの人が同じようなハンドラを毎回、毎回作っているのが現状です。どうしてなのでしょうか?

パソコンなんて人が楽をするためのただの道具です。AppleScript のようなプログラム言語はそのパソコンを使うことさえも省力化してしまおうとする言語です。なぜ、同じようなハンドラを何人も何人も毎回、毎回作っているのでしょうか?

ModuleLoader.osax は共有のライブラリを作るための一つの手段です。

確かに ModuleLoader.osax は、個性的な OSAX です。作者の AppleScript の使い方や考え方を反映しています。そのため使う人を選ぶと思います。

例えば、次のような記述。

property object_A : module "Value"
property loader : boot (module loader) for me

なんだかお呪いのようで、従来からのユーザーには取っ付きにくいものがあります。また、コンパイル時に property にスクリプトオブジェクトを代入するのを好まない人もいると思いますが、ModuleLoader.osax は property と組み合わせたときに真価を発揮します。

module loader 命令がスクリプトオブジェクトを返す...という考えようによっては強引な面もあります。設定ファイルも保存しますし。しかし、このような形で AppleScript をハックしないことには property にスクリプトオブジェクトを読み込めないのでしょう。

こういった仕様のためでしょうか。ModuleLoader.osax はマニア向けの OSAX だな、という感じがします。ただ、AppleScript に知悉している Script factory さんが出した答えがこの OSAX なのだとしたら、AppleScript でモジュールを気軽に扱う方法は他にないのだろうとも思います。実際、思いついたことのほとんどを既に Script factory さんが試しているのです。Google で検索して Script factory さんのサイトにたどり着くことのなんと多いことか...。

ModuleLoader.osax は AppleScript の初心者には取っ付きにくい OSAX だと思います。しかし、load script を置き換える load module を使うだけでも価値はあると思います。AppleScript に慣れてくれば、より高度な機能を使ってみるといいのです。

ModuleLoader.osax を利用するようになると AppleScript やスクリプトオブジェクト、ライブラリ/モジュールに対する認識が変わると思います。もしかすると、それこそが ModuleLoader.osax の真価なのでは...。

これからスクリプトライブラリを作ってみようと思っているなら、ModuleLoader.osax を試してみてはいかがでしょうか。なにか、いろいろと考えてしまうこと請け合いですから。

追記(10/03/28 19:42:11)- Script factory さんがこの記事についての補足(Script factory : ちゃらんぽらんさんの「ModuleLoader.osax の真価」へのコメント)を書いてくださっています。勘違いしていた部分もあるので、ぜひご一読を。

load script を置き換える

お手軽にコメントをしてもらえるように Google ドキュメントのフォーム機能を使ってみたのですが、よくよく考えたら、一方通行なのでした。コメントを頂くことはできるのですが、ブログのように一覧として表示はされません。コメントでの会話というのができない。メールアドレスを書いていただければ、返信も(メールで)できるのですが...(もちろん、アドレスはなくても構いません)。そんなコメント機能なのですが、ご意見等々を頂ければ幸いです。

あ。きちんとご意見は届いています。ありがとうございます。励みになっています。

さて...。

load script やスクリプトオブジェクトを使って既存のスクリプトを再利用するようになると、不満なところがいくつか出てきます。いくつかの不満点は工夫すればなんとか解決できるものですが...もっと気楽に利用したいと思うのは人間の性。

そんな状況を改善しようと、Script factory さんが ModuleLoader.osax という OSAX を作成されました。

OSAX は、AppleScript の機能を拡張するためのもので、display dialog や choose file、load script 命令も OSAX が提供している命令です。OSAX を利用すると AppleScript で正規表現も扱えるようになりますし、ミリ秒単位で時間の計測も行えるようになります。AppleScript に足りない機能を追加するものとして Mac OS X 以前は重宝していたものです。

OSAX はローカルの /Library/ScriptingAdditions、もしくは ~/Library/ScriptingAdditions に置いておきます。前者に置いておくと全てのユーザーが利用できますし、後者ならインストールしたユーザーだけが利用できます。

...ところで、OSAX ってどうなのでしょうか?

Mac OS X 以降、OSAX はあまり普及していません。もしかすると OSAX で AppleScript の機能を拡張できるということを知らない人も多いかもしれません。

Mac OS X の AppleScript 固有の問題、多くの OSAX が Mac OS X に対応しなかったこと等の事情が重なり Mac OS X 以降、個人的にも OSAX は利用しなくなりました。OSAX を利用するなら、代替方法を模索します。さいわいなことに Mac OS X 以降なら代替方法が複数あるのでそれで困らなかったのです。

この姿勢は今後も変わらないと思います。正直なところ「このスクリプトには OSAX が必要です。こちらからダウンロードしてください」とユーザー(もちろん、ユーザーには未来の自分も含まれます)の手を煩わせるのがいやなのですが...。

OSAX を配布するスクリプトに同梱することも可能ですし、スクリプトバンドルの中に入れておくという手もあるのですが、そのための条件が面倒なものが多いのも事実。そのあたりがどうも...ねえ...。

と、いろんな理由で OSAX を使っている人って案外少ないのではないでしょうか?と、思うのです。特に開発者は OSAX に依存することを嫌います。だから、OSAX の話題はここでは取り上げていませんでしたし、そういう要望も皆無でした。

OSAX を紹介するのは複雑な胸中なのですが...ModuleLoader.osax はここ(ModuleLoader)からダウンロードできます。まずは、ModuleLoader.osax をインストールしておいてください。インストールしていないと以降に掲載されているスクリプトは利用できません。

試してみた環境は、以下の通り。

  • Mac OS X 10.5.8
  • Intel Core 2 Duo
  • AppleScript 2.0.1
  • Script Editor 2.2.1

ModuleLoader.osax はディスクイメージで配布されています。このディスクイメージを開くと、中に「ModuleLoader をインストール.app」という AppleScript アプリケーションがあるので、起動します。

起動するとローカル以下にインストールするか、ホーム以下にインストールするかを尋ねられるので、自分がインストールしたい場所を指定します。インストールが完了後、もし、Script Editor が起動しているなら、いったん終了させて、再起動します。これで OSAX が提供する命令を利用できるようになります(ここではユーザーのホーム以下にインストールしました)。

ModuleLoader.osax は再利用可能なスクリプトを手軽に扱えるようにすることを目的にしています。そのため、再利用可能なスクリプト(作者は「モジュール」と表現しているので、以降、それに倣います)を一定の場所に保管しておく必要があります。

モジュールの保管場所(検索パス)は /Library/Scripts/Modules か、~/Library/Scripts/Modules 以下になります。ModuleLoader.osax はこれらの場所からモジュールの検索を開始します。検索パスの追加は可能になっています。

ModuleLoader.osax の使い方は同梱のマニュアル(ReadMe.html というエイリアスファイル)を見てもらうのが一番手っ取り早いです。まず、目を通しておくのがいいでしょう。

ModuleLoader.osax の特徴として以下の点があります。

  1. スクリプトファイルをファイル名で指定して読み込める
  2. scpt、scptd、app の拡張子を持ったスクリプトファイル、もしくはこれらのエイリアスから読み込める
  3. スクリプトを常に最新の状態で読み込む
  4. 複数のスクリプトファイル間の依存を解決してから読み込む
  5. スクリプトオブジェクトの共有が行える

load script 命令を置き換える load module 命令を使ってみます。最初に次のスクリプトを ~/Library/Scripts/Modules に Value.scpt という名前で保存しておいて下さい。このスクリプトをモジュールとして読み込みます。

Script Editor で開く

property _value : missing value

on set_value(val)
    if _value of me is val then return
    set _value of me to val
end set_value

on get_value()
    return _value of me
end get_value

では、読み込んでみます。

Script Editor で開く

on run
    set value_object to load module "Value"
    tell value_object
        set_value("Hello, world")
        get_value()
        --> "Hello, world"
    end tell
end run

動きますね(当たり前です)。実際に使ってみると分かるのですが、スクリプトファイルをファイル名だけで読み込めるのはとても気持ちのいいものです。

ModuleLoader.osax は拡張子 app, scpt, scptd のスクリプトファイル(もしくはこれらのエイリアスファイル)をモジュールとして認識します。上記のスクリプトを見てもらうと分かるように拡張子の指定は必ずしも必要ではありません。

また、検索パスにあるサブフォルダ以下も再帰的に検索します。検索パスのサブフォルダ以下にあるモジュールを指定する場合は検索パスからの相対パスを命令の引数に与えます。

Script Editor で開く

on run
    -- ~/Library/Scripts/Modules/Utilities にある File Utility.scpt を読み込む
    set value_object to load module "Utilities:File Utility.scpt"
end run

拡張子や相対パスを指定しても同一名のスクリプトファイルがあった場合はどうなるのでしょうか?結論からいうと、先に見つかった方を読み込みます。ファイル名の競合を避けたり、特定の検索パスから特定のモジュールを指定したい場合は load module 命令のオプションで対処します。

Script Editor で開く

on run
    -- ~/Library/Scripts/Modules/Value.scpt を読み込む
    -- additional paths オプションで検索パスを指定
    -- 検索パスを other paths オプションで additional paths で指定したパスだけに制限
    set modules_folder to (path to scripts folder from user domain as text) & "Modules:"
    set value_object to load module "Value.scpt" additional paths ¬
        {file modules_folder} without other paths
end run

逆に additional paths で指定したパス(と ModuleLoader.osax のデフォルトの検索パス)を全て含めたいなら other paths オプションに true を指定します。

上記のスクリプトはこれで動くのですが...しかし、以下のスクリプトは動きません。

Script Editor で開く

on run
    -- /Library/Scripts 以下から AppleScript Help.scpt を検索
    -- additional paths オプションで検索パスを指定
    -- 検索パスを other paths オプションで additional paths で指定したパスだけに制限
    set modules_folder to path to scripts folder from local domain
    find module "AppleScript Help.scpt" additional paths ¬
        {modules_folder} without other paths
end run

指定しているのは Mac OS X 10.5 インストール時に最初から入っているスクリプトファイルです。自分で作成したスクリプトなら階層の深いところにあっても探し出してくれるのですが...。古い環境で作られたスクリプトファイルだからでしょうか。

上記のスクリプトが動かなかったのは ModuleLoader.osax のバージョンが古かったためでした。バージョン 2.2.1 ならこのエラーは発生しません。確認不足でした。申し訳ございませんでした。

モジュールの検索パスをオプションで毎回指定してもいいのですが、すでにスクリプトライブラリを作成していたりするなら set additional module paths to 命令で既存のスクリプトライブラリの場所を ModuleLoader.osax に記憶させておくといいと思います。

個人的には既に ~/Library/Application Support に ASKit という名前でスクリプトライブラリを作っているのでそれを追加してみます。

Script Editor で開く

on run
    -- ModuleLoader.osax の検索パスに既存のライブラリを加える
    set support_folder to path to application support from user domain as text
    set modules_folder to support_folder & "ASKit:"
    set additional module paths to file modules_folder

    -- ModuleLoader.osax の検索パス一覧を取得
    module paths
end run

set additional module paths to 命令は実行すると ModuleLoader.osax が記憶してます(Preferences に Scriptfactory.ModuleLoader.plist という名前のファイルを作って、そこにパスを保存しています)。

だから set additional module paths to 命令は一回だけ実行すればよく、ライブラリを場所を変更したときなどに利用します。この命令で追加したパスは上書きはできますが、削除はできません。削除するには Scriptfactory.ModuleLoader.plist を削除するしかないようです。

ModuleLoader.osax は ~/Library/Scripts/Modules(もしくは、/Library/Scripts/Modules)を検索パスとして利用していますが、Modules フォルダが存在しない場合は検索パス自体が設定されません。

Script Editor で開く

on run
    -- Modules フォルダを作成していない状態で検索パス一覧を取得
    module paths
    --> {}
end run

だから、~/Library/Scripts 以下に Modules フォルダを作成せず、set additional module paths 命令で既存のスクリプトライブラリを追加しておけば、そこだけを検索するようになります(もしかすると、推奨されないのかもしれませんが...)。

ところで...ModuleLoader.osax の特徴としてモジュールの自動的なアップデートとあるのですが...。次のようなスクリプトで Value.scpt をいくら変更しても自動的にアップデートしないのですが、load module 命令ではアップデートしないのでしょうか?

Script Editor で開く

property value_object : load module "Value"

on run
    tell value_object to get_value()
end run

再コンパイルするとアップデートされるのですが...、これは当たり前ですね。

とりあえず、ModuleLoader.osax の一部の機能だけを使ってみました。まだ、他にも機能があるのですが、長くなってしまうので次回に...と。