短期集中連載 速報!Ubiquityのポテンシャルを探れ

第2回Ubiquityでオリジナルコマンドを作ろう

第2回目の今回は、実際に自分のコマンドを作ってみます。まずは、チュートリアルからのサンプルを参考にし、基本的な記述について見てみましょう。

Ubiquity 0.1チュートリアル

Ubiquityコマンドを自作する場合、Mozilla Labsで公開されているチュートリアルLabs/Ubiquity/Ubiquity 0.1 Author Tutorialが一番最初に読むべき資料となります。Ubiquityコマンドの定義方法や考え方がまとまっており、JavaScriptの経験があればこのチュートリアルを読むだけでサクサク開発できるようになります。

コマンド定義関数

Ubiquityプロンプトでcommand-editを実行してエディタを開きリスト1を入力します。

この関数でhello-worldコマンドが作成されます。Ubiquity 0.1ではcmd_から始まる関数は自動的にUbiquityコマンドとして解釈されます。cmd_hello_worldでhello-worldコマンドになります。displayMessage()は指定された内容をOSの通知システムに通知する関数で、Ubiquity 0.1ではメッセージを出力する手段として多用されています。

リスト1 hello-worldコマンドの定義例 - 短縮形式版
function cmd_hello_world() {
  displayMessage( "新大陸へようこそ" );
}

リスト1は短縮形式による定義で、フルで記述するとリスト2のようになります。nameにコマンド名、executeに処理内容を関数で作成します。JSON形式になっておりnameとexecuteのほかにもurl、icon、description、preview、takes、modifiers、locale、homepage、author、license、helpなどが指定できます。これ以外に独自の変数や関数を定義しても問題ありません。

コマンド名は英語に限らず日本語でも指定できます。リスト3のように指定すれば「挨拶」というコマンドが作成されます。ただし、かな漢字変換の確定であるエンターキーがUbiquityコマンドの入力キーとして処理されてしまうUbiquity 0.1.1では、日本語によるコマンドはそれほど便利ではありません。

リスト2 hello-worldコマンドの定義例 - フル形式版
CmdUtils.CreateCommand({
  name: "hello-world",

  execute: function() {
    displayMessage( "新大陸へようこそ" );
  }
})
リスト3 hello-worldコマンドの定義例 - コマンド名には日本語も使える
CmdUtils.CreateCommand({
  name: "挨拶",

  execute: function() {
    displayMessage( "新大陸へようこそ" );
  }
})

コマンドにハイフンが含まれているのは、Ubiquity 0.1の自然言語パーサの出来がそれほどよくないためとされており、将来のバージョンでは文脈を加味して空白が含まれたコマンドも作成できるようになると見られます。またUbiquity 0.1にはほんとうに初期段階ではあるものの、日本語文章を解析しようとするコードも含まれており、使えるレベルで現実可能かどうかは別として、将来的に「選択した文章を日本語に翻訳せよ」といった日本語による自然言語でのコマンド入力を可能にしたいという狙いがあるとみられます。今のところUbiquityのコードからは英語と日本語に対する取り組みが伺えます。

コマンド情報

コマンドに、他の属性も加えた場合の例をリスト4に示します。homepageには参照すべきURL、iconにはプロンプトに表示するFaviconのURL、authorには作者の名前や電子メール(email:で指定⁠⁠、contributorsにはコントリビュータ、licenseにはコマンドのライセンス、descriptionにはコマンドの動作説明、helpにはさらに追加情報を記載します。

リスト4 コマンドの情報を記載した例

CmdUtils.CreateCommand({
  name: "hello-world",

  homepage: "http://gihyo.jp/",
  icon: "http://gihyo.jp/favicon.ico",
  author: { name: "Daichi GOTO" },
  contributors: ["Hiroaki TOMIDA"],
  license: "BSD",
  description: "メッセージを表示します",
  help: "メッセージを出力する以外、とくに機能はありません。",
  
  execute: function() {
    displayMessage( "新大陸へようこそ" );
  }
})
図1 command-listに表示される情報の例
図1 command-listに表示される情報の例

ここで記載した情報は、作成したコマンドをほかのユーザと共有する場合に使われるようになる他、command-listコマンドで表示されるコマンド一覧に表示されます。

プレビュー

Ubiquityプロンプトの下にはコマンド補完一覧が表示される他、そのコマンドのプレビューを表示させることができます。プレビューをうまく活用するとプロンプトを開いた状態ですでに作業が完了するようになるため、ぜひとも活用したい機能です。

プレビューはリスト5のようにpreviewにHTML文字列を指定する他、関数を定義することでも操作可能です。

リスト5 プレビューに文字列を表示させる例 - HTML文字列を直接指定
CmdUtils.CreateCommand({
  name: "hello-world",

  preview: "<cite>新大陸へようこそ</cite>",

  execute: function() {
    displayMessage( "新大陸へようこそ" );
  }
})
リスト6 プレビューに文字列を表示させる例 - 関数を使って表示する場合
CmdUtils.CreateCommand({
  name: "hello-world",

  preview: function( view ) {
    view.innerHTML = "<cite>新大陸へようこそ</cite>";
  },

  execute: function() {
    displayMessage( "新大陸へようこそ" );
  }
})
図2 プレビューも表示するようにした場合のUbiquityプロンプト
図2 プレビューも表示するようにした場合のUbiquityプロンプト

関数を使う場合はリスト6のように記述します。引数からアクセスできるinnerHTMLがプレビュー画面に相当しており、innerHTMLの内容を変更することでプレビューに表示する内容を変更できます。関数を使えば動的に処理を実施することができ、より柔軟な処理が可能になります。

引数と名詞型

Ubiquityコマンドで引数を処理する例をリスト7に示します。Ubiquityで最も特徴的なのが引数の扱いです。⁠takes: {"argument": noun_arb_text }」が引数を定義している部分で、任意の文字列で構成されたもの(Arbitrary text Noun-type)を引数としてとるという指定です。

takesで引数を宣言したら、preview: function()の第2引数やexecute: function()の第1引数で引数を指定できるようになります。引数はオブジェクトにラッピングされ、.text、.html、.data、.summaryなどの属性としてアクセスできます。引数に個数という概念はなく、1つのオブジェクトとして扱われます。

リスト7 引数を使ったコマンド
CmdUtils.CreateCommand({
  name: "echo",
  takes: { "argument": noun_arb_text },

  preview: function( view,  argv ) {
    view.innerHTML = "メッセージ: <cite>" + argv.text + "</cite>";
  },

  execute: function( argv ) {
    displayMessage( "メッセージ: " + argv.text );
  }
})
図3 引数をそのままプレビューやシステム通知に表示する
図3 引数をそのままプレビューやシステム通知に表示する

この名詞型(Noun-type)という発想がUbiquityの特徴的なもので、任意の文字列をとるnoun_arb_text以外にもデータを引数としてとるnoun_type_date、パーセンテージを引数にとるnoun_type_percentage、アドレスを引数にとるnoun_type_address、言語を引数にとるnoun_type_language、タブ名を引数にとるnoun_type_tabなどの名詞型があります。

名詞型は自分で定義することも可能です。Ubiquityは型の定義にしたがって補完候補表示を実施します。つまり名詞型の充実がそのままUbiquityにおける補完能力の高さにもつながる仕組みになっているわけです。Ubiquity 0.1.1で定義されている名詞型はまだ不完全で、今後の開発でさらに多くの型や厳密な実装が進められることになります。

名詞型のカスタム作成

自分で名詞型を作成して使う例をリスト8に示します。ここでは"ichibanshibori", "superdry", "kurolabel", "malts"の4つのどれかをとるnoun_type_beerという名詞型を作成し、drinkコマンドの引数の型として指定しています。このためdrinkコマンドを実行しようとすると、noun_type_beer型でとりうる値が補完候補に表示されるようになります。

リスト8 ビールの銘柄を引数にとる名詞型 noun_type_beerを作成して使う例
noun_type_beer = new CmdUtils.NounType( "Beer",
  ["ichibanshibori", "superdry", "kurolabel", "malts"]
  );

CmdUtils.CreateCommand({
  name: "drink",
  takes: {"argument": noun_type_beer },

  preview: function( view,  argv ) {
    view.innerHTML = "ビール: " + argv.text;
  },

  execute: function( argv ) {
    displayMessage( "ビール: " + argv.text );
  }
})
図4定義した名詞型の候補が補完候補一覧に表示されていることがわかる
図4 定義した名詞型の候補が補完候補一覧に表示されていることがわかる

カスタム名詞型を用意することでコマンド入力を大幅に短縮できるようになります。ここでは静的に名詞型でとりうる値が規程されていますが、関数を通じてより動的に作成することも可能です。Ubiquityコマンドを作成する場合にはこの機能を把握し活用すると便利です。

複数引数として処理する方法

引数を複数引数として処理する方法はUbiquityのなかでさらに特徴的な部分です。Ubiquityは最終的な目標として自然言語での入力を処理することを目指しているため、Webアプリケーション開発でよく使われる言語とは引数の概念が違っています。入力がどの言語であれどの文法であれ、Ubiquityが解析して対象となる引数をコマンドへ渡します。

これは翻訳を実施するtraslateコマンドの実装を見るとよくわかります。⁠traslate this to Japanese」といった命令は第1引数が対象、第2引数がtoまたはfrom、第3引数が言語、といったように規程されているわけではなく、解析した結果として「選択した文書⁠⁠、⁠Japanese」という2つのキーワードが取り出されているだけです。つまりいずれは「Get that in Japanese」とか「選択した文章を日本語に翻訳して表示」といったどの命令でも「選択した文書」「Japanese」が引数として取り込まれるということになります。

コマンド側での実装はリスト9のようになります。takesに加えて「modifiers: {to: noun_type_language, from: noun_type_language}」という指定を追加します。Ubiquityはこの指定にしたがって引数を分析し、解析された結果がそれぞれto, fromオブジェクトのtext属性に保持されるようになります。preview: function()の第3引数およびexecute: function()の第2引数でモディファイアを指定します。

リスト9 複数引数を処理する例

CmdUtils.CreateCommand({
  name: "honyaku",
  takes: { "argument": noun_arb_text },
  modifiers: {to: noun_type_language, from: noun_type_language},

  preview: function( view,  argv, mods ) {
  },

  execute: function( argv, mods ) {
    var text = argv.text;
    var from = mods.from.text || "英語";
    var to = mods.to.text || "日本語";

    displayMessage( "「" + text + "」を" + from+ "から" + to + "へ翻訳" );
  }
})

この実装では、たとえばhonyaku this-word from Japanese to Englishと実行すれば図5のようにメッセージが表示され、テキストを選択した状態でhonyaku this from Japanese to Englishと実行すれば図6のように表示されます。

"Read the command authoring tutorial to learn more."を選択しているため、ここではthisが"Read the command authoring tutorial to learn more."に展開され引数として扱われ、モディファイアを使って分析された結果としてfromにEnglish、toにJapaneseが保持されていることがわかります。

図5honyaku this-word from Japanese to Englishの実行結果例
図5 honyaku this-word from Japanese to Englishの実行結果例
図6honyaku this from Japanese to Englishの実行結果例
図6 honyaku this from Japanese to Englishの実行結果例

かなり特徴的な部分ですが、Ubiquity 0.1.1での実装はまだ甘いもので、予想しているどおりには動作しなかったり、バグの原因にもなります。今後の開発でもっとも力を入れて開発される部分がこの処理になると見られます。

どのレベルまで実現されるか現状では未知数である他、指定方法やAPIが大幅に変更となることも予想される部分です。よくわからない場合は使わないでおき、今後のアップグレードででてくるサンプルコードや組み込みコマンドを参考にして実装につなげていけばよさそうです。

jQuery

Ubiquity 0.1.1はjQuery 1.2.3を同梱しているため、jQueryの機能を使ってプログラミングも可能です。ここまでに紹介した機能でUbiquityコマンドを使ってさまざまなUbiquityコマンドが作成できます。

チュートリアルにも注意書きがありますが、現在公開されているUbiquityはバージョンが0.1.1で、APIはまだ確定されていないとされています。将来のリリースでAPIが変更されたり概念が変わる可能性があります。APIや概念が変更された場合には、同時に発表されることになるとみられる新しいチュートリアルやChangeLogをチェックして変更に追随してください。

おすすめ記事

記事・ニュース一覧