クリアコードはフリーソフトウェア開発で培った技術力を提供しています。特にMozilla製品(Mozilla FirefoxとMozilla Thunderbird)とRubyに関連した開発を得意としています。
先日開催された〜Ruby on Rails Technical Night〜 Ruby on RailsセミナーでActive Directoryと連携したRailsアプリケーションの作り方について話しました。
ActiveLdapという社内システムをRailsを使って実現するときに便利なライブラリをデモを交えながら紹介しました。
社内向けのシステムをWebアプリケーションとして実現することは驚くことではなくなりました。Webアプリケーションなので、もちろんRailsを使っても実現することができます。
そのときに避けて通れないのが既存の社内情報との連携です。社内向けのシステムなので、社内情報と密接に連携し、より便利に使えるものであるべきです。多くの組織では社内情報をActive Directoryを用いて一元管理しています。
Railsアプリケーションとして社内システムを実現する場合も、必要に応じてActive Directory内にある情報を利用します。それを助けてくれるライブラリがActiveLdapです。
Active Directoryをあまり知らない方も参加されるかたでもついてこられるように*1、前半でActive DirectoryとLDAPの基本的なところを説明しました。今回は「図での説明 + まとめ」というように、感覚的にわかってもらった後(なんとなくわかってもらった後)に要点を確認するという流れにしました。参加された方に感想を聞けなかったのですが、いかがだったでしょうか。
その後、実際にデモを行いながら、ActiveLdapがActiveRecordと同じようにActive Directory上の情報を操作できることを説明しました。ActiveRecordと同じように操作できるということは、いつもと同じようにコントローラ部分を書けるということです。つまり、今までのRailsアプリケーション開発の知識を活かしながらActive Directoryを操作するRailsアプリケーションを開発することができるということです。
今回は「コードがバンバン出るような内容を」ということで声をかけてもらったので、ここがメインの内容になっています。残念ながら公開されている資料ではデモを表現することができないので、デモで実行したコマンドなどを載せています。
最後に、実際にアプリケーションを開発するときにぶつかることが多い問題点についてふれました。Active Directoryとの接続の仕方、テストの仕方などです。
先日開催されたRailsセミナーでの内容を紹介しました。
ActiveLdapに興味をもたれた方はるびまのActiveLdap を使ってみよう(前編)やActiveLdapのチュートリアルも読んでみてください。次号のるびまでは後編が公開される予定です。そちらも楽しみですね。
今回の内容はActive Directoryに特化した内容もありますが、OpenLDAPなどLDAPサーバ一般に通用する内容も多いです。LDAPサーバと連携するRailsアプリケーションを開発している方も参考にしてみてください。
Active Directory関連のことだけではなくて、コードを書くことについても伝えたかったのですが、欲張りすぎました。話の中ではうまく伝えられませんでしたが、プログラマーの方であれば、グラデーションで繋がる世界: 札幌Ruby会議02に行ってみて初心に帰ったもぜひ読んでみてください。
*1 多くの方はご存知のようでしたが。。。
先週末開催された札幌Ruby会議02でライブコーディングしてきました。本州枠の1つで話す機会を作ってくれた実行委員長のしまださん、ありがとうございます。
今回の発表はスライドだけでは伝わらないはずです。いつも、話すことすべてをスライドに盛りこんでいませんが、今回は特にスライドに盛りこまれていないことが多いです*1。
仙台では考え方について話しましたが、札幌では技術的なことを話すつもりでした。札幌で何かを伝えることができるのなら、それは技術的なことであって欲しいし、Ruby 逆引きレシピをリリースしているRuby札幌がいる札幌でならそれができるはず、というのが理由です。
そのために、今回はライブコーディングをすることにしました。
自分ができる、一番、技術的なことを伝える方法はプログラムを書くことです。プログラミングに限ったことかもしれませんが、何かを伝えるためには、結果だけではなく、過程も一緒に伝えた方が、より伝わります。ペアプログラミングが成果をあげているという声を聞いたこともあるのではないでしょうか。
ライブコーディングは1対1ではなく、1対nの形になるので、それで本当に伝えることができるのかは不安でしたが、札幌でなら大丈夫だろうということで決行しました。
札幌Ruby会議02の模様はニコニコ動画にアップロードされています。
|
2009-12-22 |
レシピに書かれていないこと - 須藤 功平 (20:41) 札幌でたいやきってな w |
tDiaryの話をした柴田さんも録画してくれました。ありがとうございます。こちらはYouTubeにあります。
A: 2, 3回練習しました。
あまり練習すると本番で間違わなくなるのであまり練習しないようにしました。実際、ライブコーディング中にいくつか間違いました。間違ったときにどうやって直していくかという過程も見てほしかったのでよかったです。
実際の開発はデバッグの連続です。間違わずに5分で完成させる動画通りにはいかないものです。
A: 使い慣れたエディタを使ったからです。
自分ではあまり意識して使っていませんが、意識してみると、以下の機能を使っているようです。以下の機能を使うことにより通常より速くコードを書くことができるかもしれません。
動的略語展開: 入力途中の単語を補完します。補完候補は現在開いているすべてのバッファ内にある単語すべてです。デフォルトではM-/にバインドされていますが、補完といえばタブなのでC-c C-iにバインドしています。
(define-key global-map "\C-c\C-i" 'dabbrev-expand)
A: ラングバプロジェクトのSubversionリポジトリでAGPL v3+で公開しています。
当日利用したリビジョンはr874なので、以下のコマンドで同じソースコードを取得できます。
% svn co -r874 http://groonga.rubyforge.org/svn/examples/message-archiver/
仙台ともとちぎとも違う雰囲気でした。とてもとても居心地がよかったのが忘れられません。Rubyを使って気分よくプログラムを書いているときと少し似ている気がします。
最初から最後まで最前列で話を聞いていました。文脈は少し違うのですが、ちょうど興味のある話題や、前から考えていたことに関連することが多く、参考にできることがたくさんありました。
田中さんの、ユーザが余計なことを考えなくても簡単に使えるようにしたい、という話、sumimさんや谷口さんの、興味を持って学習するためにはという話、和田さんの、増加するテストに立ち向かう方法の話、高橋さんの、やめる勇気の話、角谷さんの、選んだらそれでハッピーではない、上を見てもキリがないし下は見てもしょうがないんだから自分ができることをやっていく、という話。文脈が違うので自分の中で変換しながら自分に合わせて聞いていたので、発表された方が一番言いたかったこととずれているかもしれません。
ツールでは、しだらさんの紹介していたJekyllも使ってみたくなりました。とちぎではそうでもなかったのですが、今回は使ってみたくなりました。とちぎでは駆け足だったからかもしれません。
技術的なことは本編とは別のReject Talksが楽しかったです。Reject Talksでは、単に発表者が話すのではなく、観客が随時コメントをしていくという形式に(結果的に)なっていました。その中で、このコードがこう悪い、こうした方がよい、という話をできたことがとても楽しかったです。
話す側と聞く側ではなく、両者が話しあっていることがとても印象的でした。
札幌Ruby会議02での発表内容について補足しました。また、1参加者から見た札幌Ruby会議02も簡単にレポートしました。
札幌Ruby会議02がすばらしかったのはスタッフのみなさん、発表者のみなさん、参加者のみなさんなど関係者のみなさんのおかげでしょう。でも、もう少し考えてみると、実行委員長のしまださんがいることが一番大きいのではないかと感じています。みなさんは、司会をしていたしまださんが想いがこもった発表者紹介をしていたのに気づいていましたか。想いのこもった感想を述べていたことに気づいていましたか。しまださんの発表枠はありませんでしたが、しまださんの想いを感じることができたすばらしい札幌Ruby会議02でした。
そんな札幌Ruby会議02に発表者・スポンサーとして参加できてとても光栄です。
今回はActiveLdapには触れられませんでしたが、12/14のRailsセミナーではActiveLdapに触れるので、興味のある方は参加してみてください。
*1 公開用に多少加筆してあります。
来月中旬の12/14(月)に〜Ruby on Rails Technical Night〜Ruby on RailsセミナーでActive Directoryと連携したRailsアプリケーションの開発方法について話します。セミナーの概要は以下の通りです。都合があう方はぜひお越しください。
さも1人で話すように書いていますが、そんなことはなく、株式会社ローハイド.の吉見さんと株式会社万葉の河野さんの3人で、3部構成です。運用のお話や開発時のお話をされるようなので、そちらに興味のある方も参加してみてください。
内容はタイトルの通りで、RailsアプリケーションからActive Directoryの情報を利用する方法、注意しなければいけない点などについて話します。社内システムなど、すでにActive Directoryを導入している環境で動作するRailsアプリケーションを開発する場合、独自にアカウントを管理するのではなく、Active Directory上のアカウント情報を利用する方が、利用者の使い勝手もよくなりますし、運用者の負担も減ります。そういった場合にどのようにActive Directory上のアカウント情報を利用するのがよいか、ということをコード例も示しながら説明します。
Active Directoryとの接続には、ActiveLdap(参考: Rubyist Magazine - ActiveLdap を使ってみよう(前編))を使います。つまり、Active DirectoryをLDAPサーバとして使った場合のRailsアプリケーションの作り方とも言えます。よって、この話の内容はOpenLDAPなどActive Directory以外のLDAPサーバにも応用できます。LDAPサーバと連携したRailsアプリケーションに興味がある方にも楽しんでもらえるのではないでしょうか。
来月のRailsセミナーでRailsとActive Directoryについて話すことになったので、それの告知をしました。参加費は無料なので興味のある方はお気軽に参加してみてください。現時点で定員の半分ほど埋まっているようなので、気になる方はお早めにどうぞ。
先日開催されたFSIJ月例会でテストとテスティングフレームワークについて話しました。声をかけてくれた上野さん、ありがとうございます。
これまでの経験から感じていることをまとめたものになっています。どうにかしたいけど、まだよいアイディアがなくて悩んでいる、といったものも含まれています。例えば、違いをわかりやすく示すことはいろいろ工夫できるけど、なかったことをわかりやすく示すことは難しいといったことです。
最後まで話しつづけるのではなく、随時ディスカッションをはさむ形で進みました。参加者のみなさんからもたくさんの意見がでてとても有意義でした。
GUIやDTP、ネットワーク・ハードウェア周辺などテストを自動化することが難しい領域のテストはみなさん悩んでいるところでした。今回の会で決定的な解決策がでたわけではないのですが、悩んでいる部分、他の人が持っているよいアイディアを共有できたことは成果と言えます。明日から劇的に変わるというものではありませんが、確実によくなっていけるというものです。
話の中に何度かでていますが、「完璧を求めない」ことが重要です。完璧を求めてテストがストレスになってしまうことよりも、自分たちのペースでよい状態を継続し、よいソフトウェアの開発を続けられるようにすることの方が重要です。このあたりの力の入れ具合、テストが目的ではなくよいソフトウェアを開発することが目的であることを忘れないことで「壁」を越えることができるでしょう。
今月末、FSIJ月例会でテスティングフレームワークについて話します。月例会の概要は以下の通りです。都合があう方はぜひお越しください。
(tokyo-emacs #x02)で話したときにお会いした上野さんに声をかけてもらいました。声をかけていただきありがとうございます。
事前に打ち合わせなどはないようなので、自由な形式でやる予定です。
2時間ありますが、ずっと話し続けるわけではありません*1。こちらから考えていることや技術的なことなどを紹介し、それを話題としてディスカッションを挟みながら進めていきます。テスティングフレームワークやテストについて思っていることがある方はぜひ参加してその考えを聞かせてください。
以下の3部構成にする予定です。ここでいう「テスト」とは実装をする開発者自身が書く「自動化されたテスト」のことです。
今では、「テストは書いた方がよい」というのはみんなわかっていることです。しかし、「テストのないコードは怖くてさわれない」という感覚はそうではありません。それがテストが当たり前の人とそうでない人の差です*2。では、どうすればテストが当たり前になるのか。まずは、それについて話し、ディスカッションします。
次に、テストが当たり前の人が価値のあるテストを継続して書くために、テスティングフレームワークが提供するべきものはなにかを考えます。
最後に、現在のテスティングフレームワークはどうなっているかを扱います。先日、C++用xUnitでのテストの書き方を紹介しましたが、他の言語用のテスティングフレームワークでの書き方や、もしかしたら、どのような実装で実現しているかまで紹介できるかもしれません。
随時、参加者からの意見を受け入れますが、それぞれの話題毎および最後にもまとまったディスカッションの時間をとる予定です。
今月のFSIJ月例会でテスティングフレームワークについて話すことになったので、それの告知をしました。参加費は無料なので興味のある方はお気軽に参加してみてください。
こんにちは。下田(Piro)です。
昨日2009年11月8日に開催されたFirefox Developers Conference 2009にて、トークセッション「Aza Raskin に一問一答!」にパネリストとして参加しました。また、懇親会でのライトニングトーク第2部にて発表を行いました。参加された皆様、お疲れ様でした。また、セッションにお越しいただいた皆様、誠にありがとうございます。
発表資料および映像は以下よりご覧いただけます。
また、ライトニングトーク中で紹介しているアドオン「システムモニター」の最新版であるVer.0.3は、以下からダウンロードできます。
以下、イベントの感想と、イベント中に語ることができなかった点について書いていきたいと思います。
このセッションでは、次期Firefoxでの標準搭載も視野に入れて開発が進められているJetpackや、新しいUIの可能性を模索するUbiquityの開発者として知られるAza Raskinさんに対して、現在Firefoxアドオンを開発している・開発した経験がある人の立場からの質問を私が、主にWebアプリケーションの開発を行っておりこれからJetpack用のスクリプト(Jetpack feature)の開発に携わる可能性がある人の立場からの質問を天野さんが行う形で、Firefoxの今後とWebアプリケーションの将来像について様々な話を聞かせていただきました。
Firefoxはアドオン(拡張機能)によるカスタマイズ性の高さが最大の特長ですが、自由度の高さゆえに、アドオンの開発には様々な準備と知識が必要になるという欠点もあります。Jetpackは、この欠点をカバーしてFirefoxの機能を拡張するスクリプトをより容易に配布・利用できるようにする物として開発されている、新しいアドオンのメカニズムです。「拡張機能」がテーマである今回のFirefox Developers Conference 2009においては、避けて通れない話題ですね。
今回のトークセッションにあたって事前にいくつかのJetpack featureを開発したのですが、その際に最も強く感じた事は、APIについての不満でした。現状ではAPIに関するドキュメントが圧倒的に不足しており、また、APIがドラスティックに変更されている最中であるため、安定したJetpack featureの開発および利用はまだまだ難しいのが実情です。セッション中でも質問を行いましたが、これについては、むしろ今が開発に口出ししてAPIをより良い物にしていくチャンスと捉えてほしいという回答をいただきました。とはいえ、APIドキュメントの重要性は彼らの間でも認識されており、そのための体制を整えているところであるとの話も聞けました。
Azaさんは、セカンドシステム症候群に陥ってはならないという事を再三述べていました。これまでのアドオンの仕組みに多くの不満があるからといって、その代替として新たに登場してきたJetpackに過剰な要求をして完璧な物を最初から求めると、議論が紛糾するばかりで実装が停滞し、Jetpackという仕組み自体がいつまで経っても完成しなくなってしまいます。ほどほどの所でまずは皆に使ってもらい、その上で実際の利用状況を見ながら少しずつ不足箇所を補っていく、という姿勢が重要ですね。
Jetpack featureで利用可能なライブラリとしては、現在はjQueryが標準添付されていますが、ゆくゆくはPerlにおけるCPANのように、様々なライブラリを簡単に利用できるようにする計画もあるそうです。また、現状のJetpackも将来のバージョンも、jQueryを使うことは必須というわけではなく、ライブラリの機能を使わずにJetpack featureを開発しても問題は無いとのことです。なお、私も天野さんも既存のライブラリをほとんど利用していない理由について、Azaさんより逆に質問をされるという場面もありました。(天野さんの場合は主に既存のライブラリのオーバーヘッドが気になるためで、私の場合はFirefox用アドオンの開発のみを行う都合上、ライブラリの最大のメリットである「ブラウザ間の差異の吸収」の恩恵を受けにくいというのが理由です。)
天野さんがWebアプリの開発者の観点から投げかけられた質問として、WebのUIを良くするにはどのようにすればよいか?という物もありました。これについてAzaさんは、作り手側が絶対に手出し・口出ししないようにして、目の前でエンドユーザに実際に使ってもらいその様子を観察すれば、WebのUIはもっと良くなるだろうとおっしゃっていました。ユーザビリティ・テストはUI開発の基本と言えますが、DTPなど視覚的デザインの文脈で捉えられることがまだまだ多いWebの世界においても、きちんと「インターフェース」としての作り方を意識する必要があるということですね。
国ごとの文化の違いがUIに与える影響について、Azaさんは欧米と中国のWebブラウズの仕方の違いを例に挙げてお答え下さいました。曰く、欧米や日本では1つの画面にかじりついて操作するのが一般的であるのに対し、中国では画面から少し離れて、画面内に非常に多くの機能・要素を詰め込んでそれらを平行して利用する場合が多いそうです(そのため、Googleのかつてのシンプルなトップページは不評だったそうです)。そういった文化ごとの利用形態の違いに合わせるために、末端にいる人達にUIのデザインを行う権限を持たせる必要があるのではないか、という意見も示されていました。
他のブラウザの隆盛、特にWebKitが多数のプロジェクトで採用されている事についての質問もありました。これについては、かつては止まったWebブラウザの進化をFirefoxが刺激して活性化させたように、今はWebKitがWebを活性化させているとして、その結果Webがより便利になるのであれば最終的な勝者は我々エンドユーザだ、との考えをお答えいただきました。
というように、Jetpackの話題のみにとどまらず、WebのUI一般の話にまで言及したトークセッションとなりましたが、皆様お楽しみいただけましたら幸いです。
ライトニングトークへの参加希望者が多かったため、過去に発表経験のある数人については懇親会の場で発表を行うこととなりました。今回はシステムモニターで行った、アドオンによるWebアプリケーション向けAPIの提供方法について発表しました。
「scriptable hardware」というキーワードで、基調講演でChris BlizzardさんがFirefoxの加速度センサー対応やカメラ用APIの実験について語っていらっしゃいました。クリアコードでも独自に同様の研究を行っており、システムモニター以外にも、指紋認証アドオン「Fingerprint-Auth」の実装を進めております。また、その開発の過程で浮上してきたアイデアとして、USB機器全般をJavaScriptから制御できるようにしてみてはどうかという話もあり、それについても「進行中のプロジェクト」としてお話しさせていただきました。Fingerprint-Authについては実際にお試しいただける形の物を近日中に公開する予定ですので、どうぞお楽しみに。
セッション「js-ctypes ~ネイティブコードを呼び出す新しいカタチ」では、JavaScriptからプラットフォームネイティブなバイナリの機能を呼び出すより平易な方法を提供するFirefox 3.6からの新機能js-ctypesを紹介されていましたが、システムモニターやFingerprint-AuthのようにJavaScriptのグローバルな名前空間にAPIを提供しようと思うと、現状ではXPCOMコンポーネントとして実装を行う必要があります。また、XPCOMコンポーネントはC++での開発となるため、各プラットフォーム向けのビルドやnsISecurityCheckedComponentインターフェースの実装などの作業が必要となります。残念ですね。
なお、発表ではすべての実装がJavaScriptによって行われているXUL/MigemoでのAPIの提供方法を例として紹介しました。JavaScript製XPCOMコンポーネントの場合は、比較的簡単に機能をWebアプリケーション向けのAPIとして公開することができます。作成手順についてはFirefox 3 Hacks等の解説と発表資料中の説明を併せてご覧下さい。
株式会社はてなさんのはてなブックマークFirefox拡張についての発表では、開発に使用したツールとして弊社開発のテスティングフレームワークUxUをご紹介いただけました。主にデータベース関連の機能等のユニットテストに利用されているとのことで、非同期処理が絡むテストの開発に役立ったとのお言葉をいただけたのは嬉しかったです。また、私が個人的に開発している各種のアドオンについても、ソースコードをアドオン開発の参考にしていただけたそうで、ありがたい限りです
なお、UxUでは最近のバージョンアップでより使いやすいユーザ操作エミュレーション用のAPIが追加されました。基本部分のユニットテスト以外にも、GUIが関係する機能テストやインテグレーションテストにもUxUを活用していただけたらと思っております。
UbiquityにもJetpackにも共通している1つの狙いとして、「より多くの人が開発を行えるようにする」という目標があるそうです。より簡単に開発できるようになり、より多くの人が開発できるようになれば、より多くのイノベーションが生まれる、というのは前回のFirefox Developers Conferenceの際にもAzaさんが語られていた話ですが、AutoPagerize、ひいてはその原点であるGoogleAutoPagerが、どちらもGreasemonkeyスクリプトという世界から登場したことは、まさにその象徴的な出来事だと思います。
これはFirefoxのアドオン開発のみに限られた話ではなく、Chris Blizzardさんが語ったキーワード「scriptable hardware」もそれに連なる話であると言えるでしょう。折しも、先日GoogleがClosure Toolsという、JavaScriptでのアプリケーション開発のためのツール集を公開しました。APIやツールなど様々な側面から、Webクライアント上で動作する高機能なアプリケーションを開発しやすくするための土壌が整いつつあります。Webアプリケーションがエンドユーザからただの「アプリケーション」として扱われるようになる、Web OSという未来が本当にすぐそこまで来ているのだということを、改めて意識させられたイベントでした。
注: 長いです。
スクリプト言語でのxUnit実装を使ったことがある方なら、テストを定義するだけでテストが実行されることが当たり前ではないでしょうか。c2.comのWikiによると、これはTest Collectorというそうです。定義したテストを自動的に集めてくる機能のことです。
一般的にTest Collectorの機能は言語が提供するリフレクションやメタプログラミングを使って実現されます。
例えば、Rubyのtest-unit 2.xでは、リフレクションを使う方法とメタプログラミングを使う方法の両方をサポートしています。リフレクションを使う方法ではObjectSpace.each_object(Class)ですべてのクラスを取得し、その中のTest::Unit::TestCaseのサブクラスを集めます。メタプログラミングを使う方法ではTest::Unit::TestCase.inheritedを定義して、サブクラスが定義された時のフックでそのサブクラスを集めます。
Pythonでもモジュールオブジェクトからモジュール内のオブジェクトにアクセスすることができるので、同様の方法でテストを集めることができます。JavaScriptでも、オブジェクトに定義されているプロパティを列挙することができるので、同様の方法でテストを集めることができます。(JavaScriptとXULで実装されているUxUも同様のことをしています。)
一方、CやC++ではリフレクションやクラスをファーストクラスオブジェクトとして扱えないため、自動でテストを集めるためには一工夫必要になります。一昔前のxUnitでは、定義したテストを手動で登録していました。
それでは、C/C++での一工夫の方法として以下の4つをxUnit実装と一緒に紹介します。C++用のxUnitを選択する時の参考にしてください。
最初はCppUnitのケースです。CppUnitではテストを定義するだけでは、自動でテストを集めてはくれません。しかし、便利マクロを用意して、手動でテストを登録する面倒さを減らしています。
以下はCppUnit Cookbookにあるソースコードをベースにしています。public内のテスト定義とは別にCPPUNIT_TEST_SUITEからCPPUNIT_TEST_SUITE_ENDの間でテストを登録しています。
// complex-number-test.cpp #include <cppunit/extensions/HelperMacros.h> class ComplexNumberTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( ComplexNumberTest ); CPPUNIT_TEST( testEquality ); CPPUNIT_TEST( testAddition ); CPPUNIT_TEST_SUITE_END(); private: Complex *m_10_1, *m_1_1, *m_11_2; public: void setUp() { m_10_1 = new Complex( 10, 1 ); m_1_1 = new Complex( 1, 1 ); m_11_2 = new Complex( 11, 2 ); } void tearDown() { delete m_10_1; delete m_1_1; delete m_11_2; } void testEquality() { CPPUNIT_ASSERT( *m_10_1 == *m_10_1 ); CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) ); } void testAddition() { CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 ); } }; CPPUNIT_TEST_SUITE_REGISTRATION( ComplexNumberTest );
実行する場合は以下のようなmain関数を定義する必要があります。
// main.cpp #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/ui/text/TestRunner.h> int main( int argc, char **argv) { CppUnit::TextUi::TestRunner runner; CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); runner.addTest( registry.makeTest() ); bool wasSuccessful = runner.run( "", false ); return wasSuccessful; }
上記の二つのファイル(とComplexNumberの実装)を使ってビルドします。
% g++ -o complex-number-test complex-number-test.cpp main.cpp \
-lcomplex-number -lcppunit
テストを実行するにはビルドしたバイナリを実行します。
% ./complex-number-test .. OK (2 tests)
マクロを使ってテスト登録を簡単にしている(自動化まではしていない)例としてCppUnitを紹介しました。
CPPUNIT_TEST_SUITEなどの便利マクロを使わない場合は定義したテスト名(上の例ではtestEqualityとtestAddition)以外のことも気にしなければいけなくなります。便利マクロを使うと、テスト名だけわかっていればよいので、それに比べるとだいぶテスト作成が楽になっています。
しかし、テストを定義だけして登録し忘れたということを回避することができません。また、テストケース定義とは別にmain関数も定義する必要があり、テスト以外のことにも気を配る必要があることにも注意が必要です。
C++で書かれたテストコードを直接C++コンパイラでコンパイルするのではなく、テストコードに必要なコードを追加したC++ソースコードを生成して、それをコンパイルする方法です。C++コンパイラでのビルドする前に一度変換処理を行えるので、テストコードへの記述が減ることが利点ですが、変換処理を行うのがやや面倒です。自動化されば気にならなくなるでしょう。
まずはCxxTestのケースです。CxxTestではソースコードを直接ビルドするのではなく、C++のソースコードからテスト登録処理などを付加したC++ソースコードを生成し、それをビルドします。
以下はCxxTest User Guideにあるソースコードをベースにしています。テストの定義だけでテスト登録処理は含まれていません。
// MyTestSuite.h #include <cxxtest/TestSuite.h> class MyTestSuite : public CxxTest::TestSuite { public: void testAddition( void ) { TS_ASSERT( 1 + 1 > 1 ); TS_ASSERT_EQUALS( 1 + 1, 2 ); } };
以下のようにビルドします。
% cxxtestgen --error-printer -o cxxunit-tests.cpp MyTestSuite.h % g++ -o cxxunit-tests cxxunit-tests.cpp
cxxtestgenでMyTestSuite.hにあるテスト定義にテスト登録処理などを加えてcxxunit-tests.cppを生成します。余談ですが、cxxtestgenはPythonスクリプトです。また、CxxUnitはライブラリを提供せず、ヘッダーファイルのみを提供します。
バイナリを実行するとテストが走ります。
% ./cxxunit-tests Running 1 test.OK!
テスト登録が完全に自動化されているのでCppUnitよりも新規テストの追加が容易です。テストの登録しわすれもありません。ただ、cxxtestgenとC++コンパイラで2回コンパイルする必要があることが少し手間だと言えます。
続いてQtが提供するQTestLibのケースです。QTestではQtが提供するスロットの仕組みを使って、定義されているテストを集めます。スロットがどのように定義されているかをプログラム中から扱うために、QtはC++のソースコードをプリコンパイルしますが、QTestでも同様にプリコンパイルする必要があります*1。
以下はQTestLibのチュートリアルにあるソースコードををベースにしています。
// test-qstring.cpp #include <QtTest/QTest> class TestQString: public QObject { Q_OBJECT private slots: void toUpper() { QString str = "Hello"; QCOMPARE(str.toUpper(), QString("HELLO")); } }; QTEST_MAIN(TestQString) #include "test-qstring.moc"
QTestLibでもmain関数は定義しなければいけませんが、QTEST_MAINという便利マクロが用意されています。
以下のようにビルドします。
% mkdir test-qstring % mv test-qstring.cpp test-qstring % cd test-qstring % qmake -project "QT += testlib" % qmake % make
バイナリを実行するとテストが走ります。
% ./test-qstring ********* Start testing of TestQString ********* Config: Using QTest library 4.5.3, Qt 4.5.3 PASS : TestQString::initTestCase() PASS : TestQString::toUpper() PASS : TestQString::cleanupTestCase() Totals: 3 passed, 0 failed, 0 skipped ********* Finished testing of TestQString *********
このようにQTestLibではテスト登録のために必要なコードはQTEST_MAINでクラスを指定している部分だけです。個々のテストは指定する必要がありません。
メタオブジェクト情報を生成すること、また、それを読み込んでいる#include "test-qstring.moc"のところはQTestLib独自のことではなく、Qt全般のことなので、Qtを利用している場合は追加で必要な作業とはいえないでしょう。つまり、QTestLibのテストを集める方法は完全には自動化されていませんが、Qt開発者にはそれほど負担もかからず自然に書けるようになっている使いやすいAPIといえます。一方、Qt開発者でない場合は、面倒に見えるでしょう。
CppUnitでもマクロでテストを登録していますが、それをもう一歩進めたのがこの方法です。CppUnitでは、テスト定義は通常の関数定義でしたが、この方法ではそこでマクロを使い、テスト定義と同時にテストを登録します。
まずは、Google Testです。Google Testではテスト定義時にTESTマクロを使います。以下はGoogleTestSamplesにあるソースコードをベースにしています。
// test-factorial.cpp #include <gtest/gtest.h> int Factorial(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } TEST(FactorialTest, Negative) { EXPECT_EQ(1, Factorial(-5)); EXPECT_EQ(1, Factorial(-1)); EXPECT_TRUE(Factorial(-10) > 0); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
プリコンパイル方式でもテストの登録忘れはありませんが、この方法でも登録を忘れることがありません。テスト定義の方法が通常の関数定義とは異なる書式になることに慣れることができるのであれば、この方式で負担が少なくテストを書けるようになるでしょう。
以下のようにビルドします。
% g++ -o test-factorial test-factorial.cpp -lgtest
バイナリを実行するとテストが走ります。
% ./test-factorial [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from FactorialTest [ RUN ] FactorialTest.1 [ OK ] FactorialTest.1 (0 ms) [----------] 1 test from FactorialTest (0 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (0 ms total) [ PASSED ] 1 test.
上記の例では触れていませんが、フィクスチャ(setup/teardown)を使う場合は、テストクラス名を揃える必要があるなど、同じグループのテストを作る場合は重複する部分がでてしまいます。例えば、QTestLibのようにクラス内にテストを定義する方法では以下のようになります。
class MyTest { void setup() {...} void teardown() {...} void test1() {...} void test2() {...} void test3() {...} }
一方、Google Testの場合は、スコープが使えず、以下のようにクラス名を複数回書く必要があります。
class FooTest : public testing::Test { protected: virtual void SetUp() { b_.AddElement(3); } Foo a_; Foo b_; }; TEST_F(FooTest, InitializesCorrectly) { EXPECT_TRUE(a_.StatusIsOK()); } TEST_F(FooTest, ReturnsElementCountCorrectly) { EXPECT_EQ(0, a_.size()); EXPECT_EQ(1, b_.size()); }
マクロを使っている場合は、間違ったテストクラス名を指定するなどコンパイルエラーになったときに意味の分からないエラーメッセージを目にすることがあるというのも注意しなければいけないポイントです。エラーメッセージを使えないと問題を発見することが難しくなります。
次に、Boost Test Libraryです。やり方はGoogle Testとだいたい同じで、Boost Test LibraryではBOOST_AUTO_TEST_CASEを使います。
// test-add.cpp #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE AddTest #include <boost/test/unit_test.hpp> int add( int i, int j ) { return i+j; } BOOST_AUTO_TEST_CASE( add_test ) { BOOST_CHECK_EQUAL( add( 2,3 ), 5); }
最初にBOOST_TEST_DYN_LINKとBOOST_TEST_MODULEを定義しておくと、Boost Test Libraryではmain関数を定義する必要はありません。
以下のようにビルドします。
% g++ -o test-add test-add.cpp -lboost_unit_test_framework
バイナリを実行するとテストが走ります。
% ./test-add Running 1 test case... *** No errors detected
Google Testと同じくマクロが気にならない場合やBoostに慣れている場合はテストが書きやすいでしょう。
マクロを利用する方法は言語の構文を工夫してテストを集めています。プリコンパイル方式では言語の構文はそのままで、コンパイル前に付加情報を加えることでテストを集めています。
一方、最後の共有ライブラリから探す方法ではコンパイル後の共有ライブラリから情報を取得してテストを集めます。この方式では、テストを共有ライブラリとして作成し、テスト実行用のコマンドからその共有ライブラリを読み込み、テストを実行します。こうすることにより、テスト側にテスト登録処理を埋め込む必要がなくなります*2。共有ライブラリの中からテストを見つける処理はテスト実行コマンドが頑張るからです。
最初はWinUnitです。やり方は共有ライブラリからテストを集める方式なのですが、書き方はマクロを使う方式です。テストを定義するときはBEGIN_TESTとEND_TESTで囲みます。
#include "WinUnit.h" BEGIN_TEST(AddTest) { WIN_ASSERT_TRUE(3 == add(1, 2)); } END_TEST
すでにGoogle TestやBoost Test Libraryで見たように、この使い方であれば、共有ライブラリにする必要はありません。マクロの中で一工夫することでテストの自動登録を実現できるからです。
WinUnitの利点はVisual C++で使いやすいことでしょう。マクロを使ったAPIが気にならないVisual C++開発者には有力な選択肢です。
最後はCutterです。CutterはC言語用の単体テストフレームワークとして開発されていましたが、先日リリースされた1.1.0で大きくC++対応を強化しています。
CutterではWinUnitとは違いマクロを利用しません。通常通り関数を定義するとテストとして認識されます。ただし、すべての関数がテストとして認識されるのではなく、test_からはじまる名前の関数だけをテストとして認識します。
#include <cppcutter.h> namespace calc { void test_add() { cppcut_assert_equal(5, add(2, 3)); } }
マクロを利用してテストを自動登録する方式では、フィクスチャ定義時に名前を揃える必要がありましたが、Cutterでは以下のようにnamespace内にsetup()/teardown()を定義するだけです。namespaceでグループ化されたテスト全体でフィクスチャを共有します。
#include <cppcutter.h> namespace calc { void setup() {...} void teardown() {...} void test_add() // calc::setup()/calc::teardown()が呼び出される { cppcut_assert_equal(5, add(2, 3)); } void test_sub() // calc::setup()/calc::teardown()が呼び出される { cppcut_assert_equal(5, add(8, 3)); } }
この方式では通常のC++プログラムと同様にテストを書くことができるため、新しくテストを書くことの敷居が低くなります。しかし、tes_などタイプミスをしてしまった場合に、どうしてテストが実行されないのかに気づきにくいという問題点があります。
テストも通常のプログラムと同様に開発したい場合はマクロを使わないこの方式がオススメです。
C++用の各種xUnitでのテストの書き方を、方式毎に分類して紹介しました。どんなバックグラウンドを持っているかにより、選びやすいxUnitは変わるでしょう。Visual C++開発者であればWinUnitを選ぶことが多いでしょうし、Qt開発者であればQtTestLibを選ぶことが多いでしょう。しかし、バックグラウンドから選ぶだけではなく、テストの書きやすさも判断材料に加えてみてはいかがでしょうか。
継続して開発すればそれに伴ってテストも増えていきます。しかし、テストは面倒くさがって飛ばしてしまいがちです。新しくテストを書く敷居が下がれば、テストを面倒くさがることが少なくなり、安心して開発を続けていくための土台を固めることができます。新しくテストを書く敷居を下げることは開発を継続するのであれば割に合うということです。
今回は「新しいテストの書きやすさ」を軸に様々なxUnitのやり方を紹介しました。C++用xUnitを選択する時の参考にしてみてください。
念のため書きますが、オススメはCutterです。
2009年10月30日付で、テスティングフレームワークUxUのバージョン0.7.5をリリースしました。
UxUはこれまで「Firefoxアドオン開発用テスティングフレームワーク」と銘打っていましたが、Thunderbird用アドオンの開発にも利用されていることと、バージョン0.7.0以降からXULRunnerベースのアプリケーション一般に対してインストール可能なようになったことから、現在のプロジェクトページ上では「Firefox/Thunderbird用アドオン・XULRunnerアプリケーション開発用テスティングフレームワーク」と表記しています。
バージョン0.7.0以降で、UxUはデータ駆動テストの記述に対応しました。今回はUxUでのデータ駆動テストの記述方法の解説を通じて、データ駆動テストの利便性についてご紹介したいと思います。
このエントリ内の目次:
データ駆動テストとは、簡単に言えば、「テストのロジックとデータを分離した自動テスト」の事です。データ駆動テストの利点を理解していただくために、データ駆動テストではないテストの例もいくつか挙げながら、それぞれの利点と欠点を見ていきましょう。
以下は、XUL/Migemoという「ローマ字入力で普通の日本語の検索を行う」アドオンの中の、半角英数字によるローマ字入力をひらがなに変換するモジュールのテストの一部です。
function test_roman2kana() { assert.equals('あいうえお', transform.roman2kana('aiueo')); assert.equals('aiueo', transform.roman2kana('aiueo')); assert.equals('がぎぐげご', transform.roman2kana('gagigugego')); assert.equals('にほんご', transform.roman2kana('nihongo')); assert.equals('ぽーと', transform.roman2kana('po-to')); assert.equals('きゃっきゃ', transform.roman2kana('kyakkya')); assert.equals('うっうー', transform.roman2kana('uwwu-')); assert.equals('\\(\\)\\[\\]\\|', transform.roman2kana('\\(\\)\\[\\]\\|')); assert.equals('\\(\\[', transform.roman2kana('\\(\\[')); assert.equals('\\)\\]', transform.roman2kana('\\)\\]')); assert.equals('\\|', transform.roman2kana('\\|')); }
このモジュールのroman2kana()メソッドは、半角英数字のローマ字入力をひらがなに変換し、それ以外の入力はそのまま返すという仕様になっています。この仕様通りに動作するかどうかを検証するため、ここでは11種類の引数を渡し、戻り値をassert.equals()で検証しています。検証しないといけないパターンが増えた時には、行をコピーして引数と期待値の部分を書き換えることになります。
パッと見て分かるかと思いますが、このテスト用コードには以下のような問題があります。
「テストのロジック」と「テストしなければいけないデータ」が一緒に記述されているため、メンテナンス性が低いコードになってしまっていると言えます。
このようなテストのメンテナンス性を高めるための方法の1つとしては、アサーションを行う部分を関数としてまとめておくというやり方が考えられます。例えば以下の要領です。
function test_roman2kana() { function oneTest(aExpected, aInput) { assert.equals(aExpected, transform.roman2kana(aInput)); } oneTest('あいうえお', 'aiueo'); oneTest('aiueo', 'aiueo'); oneTest('がぎぐげご', 'gagigugego'); oneTest('にほんご', 'nihongo'); oneTest('ぽーと', 'po-to'); oneTest('きゃっきゃ', 'kyakkya'); oneTest('うっうー', 'uwwu-'); oneTest('\\(\\)\\[\\]\\|', '\\(\\)\\[\\]\\|'); oneTest('\\(\\[', '\\(\\['); oneTest('\\)\\]', '\\)\\]'); oneTest('\\|', '\\|'); }
コードの冗長さが減りました。また、メソッド名や引数の取り方が変わった時も、変更が必要な箇所は1箇所だけになりました。
これでも悪くはないのですが、実際にテストを繰り返し走らせていると、以下のような問題が浮き彫りになってきます。
例えば以下のようなシナリオが考えられます。
「3の段階で行った修正で5や8のバグが発生した」という可能性もありますが、最初から2や5や8のバグがあったのであれば、まとめて直せていたかもしれません。これでは、バグを直しても直してもきりがないという、モグラ叩きのような感覚に陥ってしまいます。
メンテナンス性を高める別の手法として、アサーションに使う期待値とメソッドに渡す引数だけを配列で別途定義しておくというやり方も考えられます。
function test_roman2kana() { var patterns = [ ['あいうえお', 'aiueo'], ['aiueo', 'aiueo'], ['がぎぐげご', 'gagigugego'], ['にほんご', 'nihongo'], ['ぽーと', 'po-to'], ['きゃっきゃ', 'kyakkya'], ['うっうー', 'uwwu-'], ['\\(\\)\\[\\]\\|', '\\(\\)\\[\\]\\|'], ['\\(\\[', '\\(\\['], ['\\)\\]', '\\)\\]'], ['\\|', '\\|'] ]; for each (var pattern in patterns) { assert.equals(pattern[0], transform.roman2kana(pattern[1])); } }
これにもやはり問題があります。
1つ目の問題点については前述したとおりです。
2つ目の問題は、この例のように2次元配列を使った場合に現れます。上記の例では配列の0番目の要素が期待値、1番目の要素が入力となっていますが、これではどっちがどっちなのかを常に意識する必要があります。
3つ目は、ループに特有の問題です。UxUではテストにfailした時にスタックトレースが表示され、べた書きした場合や関数を使った書き方の場合であれば、スタックトレースを辿れば「どのパターンで失敗したのか」の情報に辿り着くことができます。しかし、ループを使っていると、スタックトレースの行き着く先はループの中になってしまうため、どのパターンに対して失敗したのかが一目では分からなくなってしまいます。
この対策として、UxUのアサーションでは最後の引数として任意のメッセージを渡せるので、以下のようにして「どのパターンで失敗したのか」を表示させることは可能です。
assert.equals(pattern.expected, transform.roman2kana(pattern.input), pattern.input+'に対するテスト');
しかし、実際にたくさんテストを書くようになってくると、これが地味に面倒です。これが4つ目の問題点です。
べた書きした場合や関数を使った書き方の場合であれば、このような配慮なしに淡々とテストを書いていても、テスト実行時にはスタックトレースを辿ればデバッグに必要な情報を得られます。それなのに、ループに対してはこのような配慮をしなければいけないわけです。この面倒さによって、テストを新しく書いたり過去に書いたテストをメンテナンスしたりする意欲がじわじわと削がれてしまう、というのが一番の問題点だと言えます。
UxU 0.7.0以降で導入されたデータ駆動テストの仕組みを使うと、上記のテストはこのように書くことができます。
test_roman2kana.parameters = utils.readParametersFromCSV('patterns.csv'); function test_roman2kana(aParameter) { assert.equals(aParameter.expected, transform.roman2kana(aParameter.input)); }
テスト用のコードにはロジックだけを書き、データは外部ファイルで定義します(後述しますが、テストケース内にデータを埋め込むこともできます)。データを定義しているファイルの形式はCSVです。
| input | expected | |
| 半角英数 | aiueo | あいうえお |
| 全角英数 | aiueo | aiueo |
| 濁音のみ | gagigugego | がぎぐげご |
| 濁音混じり | nihongo | にほんご |
| 音引き | po-to | ぽーと |
| 拗音 | kyakkya | きゃっきゃ |
| 撥音 | uwwu- | うっうー |
| paren | \\(\\)\\[\\]\\| | \\(\\)\\[\\]\\| |
| parenOpen | \\(\\[ | \\(\\[ |
| parenClose | \\)\\] | \\)\\] |
| pipe | \\| | \\| |
この時、UxUは「test_roman2kana」という1つのテストではなく、「test_roman2kana (半角英数)」「test_roman2kana (全角英数)」……という名前の11個のテストを実行するようになります。
このように、データ駆動テストには多くのメリットがあります。単純な入出力のパターンを数多く検証しなければいけない場面で、データ駆動テストは威力を発揮します。
UxUでは、テスト関数のparametersプロパティに配列またはハッシュを代入すると、そのテストをデータ駆動テストとして実行するようになります。この時テスト関数には引数として、parametersプロパティの配列またはハッシュの要素が1つずつ渡されます。
以下は、配列を指定した場合の例です。
test_someFunc.parameters = [ { expected : '29', input : 'niku' }, { expected : '2929', input : 'nikuniku' }, { expected : '029', input : 'oniku' } ]; function test_someFunc(aParameter) { /* 1回目: aParameter = { expected : '29', input : 'niku' } 2回目: aParameter = { expected : '2929', input : 'nikuniku' } 3回目: aParameter = { expected : '029', input : 'oniku' } */ ... }
ハッシュを指定した場合は、以下のようになります。ハッシュのキーはテスト関数には渡されず、結果を表示する時のテスト名として表示されます。
test_someFunc.parameters = { simgle: { expected : '29', input : 'niku' }, double: { expected : '2929', input : 'nikuniku' }, o: { expected : '029', input : 'oniku' } }; function test_someFunc(aParameter) { /* 1回目: aParameter = { expected : '29', input : 'niku' } 2回目: aParameter = { expected : '2929', input : 'nikuniku' } 3回目: aParameter = { expected : '029', input : 'oniku' } */ ... }
データ駆動テストのサポートに併せて、いくつかの新しいヘルパーメソッドが追加されています。これらを使うことで、データ駆動テストをより簡単に作成・メンテナンスすることができます。
前述の例で使用しているutils.readParametersFromCSV()は、CSVファイルの内容を読み込み、最初の行のカラム名をキーとしたハッシュとして返します。例えば前述の例のCSVは、以下のようなハッシュとして解釈されます。
// test_roman2kana.parameters = utils.readParametersFromCSV('patterns.csv'); // これは、以下のように書くのと同じ test_roman2kana.parameters = { '半角英数': { input: 'aiueo', expected: 'あいうえお' }, '全角英数': { input: 'aiueo', expected: 'aiueo' }, '濁音のみ': { input: 'gagigugego', expected: 'がぎぐげご' }, '濁音混じり': { input: 'nihongo', expected: 'にほんご' }, '音引き': { input: 'po-to', expected: 'ぽーと' }, '拗音': { input: 'kyakkya', expected: 'きゃっきゃ' }, '撥音': { input: 'uwwu-', expected: 'うっうー' }, paren: { input: '\\(\\)\\[\\]\\|', expected: '\\(\\)\\[\\]\\|' }, parenOpen: { input: '\\(\\[', expected: '\\(\\[' }, parenClose: { input: '\\)\\]', expected: '\\)\\]' }, pipe: { input: '\\|', expected: '\\|' } };
CSVファイルはRFC4180準拠の形式の読み込みに対応しています。カンマ区切りではなくタブ区切りのファイルを使用したい場合は、utils.readParametersFromTSV()を使用して下さい。どちらも、読み込みたいファイルのパス(相対パスも利用できます)を第1引数に、ファイルのエンコーディングを第2引数に指定します。エンコーディング指定を省略した場合はUTF-8として読み込みます。
以下のリンク先に、各メソッドの詳しい説明があります。
また、JSON形式で保存した外部ファイルを読み込むためのutils.readJSON()というメソッドもあります。
test_roman2kana.parameters = utils.readJSON('patterns.json');
こちらも、読み込みたいファイルのパス(相対パスも利用できます)を第1引数に、ファイルのエンコーディングを第2引数に指定します。エンコーディング指定を省略した場合はUTF-8として読み込みます。詳しい説明は以下のリンク先をご覧下さい。
データ駆動テストの仕組みを利用すると、テストのロジックとデータを分離できるため、メンテナンス性が高まることが期待できます。様々なパターンの入力を受け付ける機能を開発する時は、データ駆動テストをぜひ一度試してみて下さい。
先日開催されたとちぎRuby会議02で社長の一人として話しました。声をかけてくれたtoRubyのみなさん、ありがとうございます。
Debian GNU/Linux sidが動いているMacBookを持っていきました。事前の接続テストでうまくプロジェクターに出力できなかったので、ワイクル株式会社のkdmsnrさんのMacBook経由で出力しました。突然のお願いにもかかわらず快く貸してくれました。ありがとうございます。

Rabbit本体はDebian上で動いていて、携帯電話からのリモート操作もDebian上のRabbitに対して行っています。しかし、画面表示は別マシンのMac OS X上のX11で行っています。プロジェクターへの出力も別マシンのMac OS Xが行っています。
dRubyなど咳プロダクツを用いたプレゼン環境の1つのパターンのデモとして、上記のような構成を用いました。かっこいいですね。
8割ほどRubyととちぎをまじえた発表者紹介・会社紹介をし、最後にかるく現在の仕事の内容、これから向かおうとしている方向を紹介しました。他の方たちのように具体的な方法は提示していません。私たちも続けられるしくみを模索しているのが現状です。
とちぎRuby会議02はとても楽しく居心地のよい雰囲気に満ちていました。どうしてなのかはわかりません。まだ体験していない人はぜひ一度体験してみることをおすすめします。toRubyに参加すると体験できるでしょう。
地域Ruby会議2ndシーズン最初のとちぎRuby会議02に参加しました。
お昼ご飯をゆっくり食べていたおかげで開始時刻が遅れてしまい、すみませんでした。あのよい雰囲気の中でtoRuby勉強会を味わう時間が減ってしまったことが残念です*1。
とちぎRuby会議にはRuby札幌の方も参加していましたが、12月には札幌Ruby会議02があります。こちらにも発表者として参加する予定です。とちぎRuby会議02のようにとてもすばらしい地域Ruby会議になりそうな予感がします。都合のあう方は参加してみてはいかがでしょうか。
札幌Ruby会議02までには関西Ruby会議02とTokyuRuby会議01もあります。こちらの内容もとてもおもしろそうですね。
*1 お昼ご飯タイムもとてもよい雰囲気でした。
CPUの使用率をFirefoxのツールバー上に表示するアドオン「システムモニター」のバージョン0.2をリリースしました。以下のリンク先からダウンロードできます。
前のバージョンからの変更点は以下の通りです。
インストールすると、初回起動時に以下のイメージのようなダイアログが表示されます。

「はい」を選択すると、メニューバーの右端(Mac OS Xではナビゲーションツールバーの右端)に、以下のイメージのようにCPU使用率のグラフが表示されます。

プラットフォームは、WindowsXP SP1以降、Mac OS X、Linuxをサポートしています。Linuxではlibgtop2を使用していますので、別途インストールしてください。
このアドオンを導入した環境では、システムモニターが提供するAPIを通じて、Webページ上のスクリプトからシステムモニターと同じ情報を取得できるようになります。以下は、Webページ内にCPU使用率のグラフを埋め込む例です。実際に上記リンク先からアドオンをインストールした状態で、このページを表示してみて下さい。
<div id="system-monitor-demo"></div> <script type="text/javascript"><!-- var container = document.getElementById("system-monitor-demo"); if (!window.system || !window.system.addMonitor) { container.innerHTML = "システムモニターがインストールされていません"; container.style.color = "red"; } else { container.innerHTML = "<div><canvas id='system-monitor' width='300' height='60'></canvas>"+ "<br /><span id='system-monitor-console'></span></div>"; var width = 300; var interval = 1000; var CPUTimeArray = []; var arrayLength = width / 2; while (CPUTimeArray.length < arrayLength) { CPUTimeArray.push(undefined); } // リスナとして登録する関数には、CPUの使用率が割合として渡される。 function onMonitor(aUsage) { var console = document.getElementById("system-monitor-console"); console.textContent = aUsage; CPUTimeArray.shift(); CPUTimeArray.push(aUsage); var canvasElement = document.getElementById("system-monitor"); var context = canvasElement.getContext("2d") var y = canvasElement.height; var x = 0; context.fillStyle = "black"; context.fillRect(0, 0, canvasElement.width, canvasElement.height); context.save(); CPUTimeArray.forEach(function(aUsage) { var y_from = canvasElement.height; var y_to = y_from; if (aUsage == undefined) { drawLine(context, "black", x, y_from, 0); } else { y_to = y_to - (y * aUsage); y_from = drawLine(context, "green", x, y_from, y_to); drawLine(context, "black", x, y_from, y_to); } x = x + 2; }, this); context.restore(); } function drawLine(aContext, aColor, aX, aBeginY, aEndY) { aContext.beginPath(); aContext.strokeStyle = aColor; aContext.lineWidth = 1.0; aContext.lineCap = "square"; aContext.moveTo(aX, aBeginY); aContext.lineTo(aX, aEndY); aContext.closePath(); aContext.stroke(); return aEndY - 1; } // リスナを登録する。 window.system.addMonitor("cpu-usage", onMonitor, interval); window.addEventListener("unload", function() { window.removeEventListener("unload", arguments.callee, false); // ページのアンロード時にリスナの登録を解除する必要がある。 window.system.removeMonitor("cpu-usage", onMonitor); }, false); } // --></script>
上の例をご覧いただいても分かるとおり、このアドオンは、Webアプリケーション(Webページ上のスクリプト)から各種ハードウェアデバイスへアクセスする技術のデモンストレーションとして開発されています。
Webカメラとの連動によるAR(拡張現実)や、指紋認証、Felica認証といった特殊なハードウェアを必要とする認証技術など、この技術を応用することにより今までのWebアプリケーションではできなかった様々なことが可能となります。
クリアコードでは、このようにWebとハードウェアを直結する技術の開発に取り組んでおります。特殊なデバイスを搭載したハードウェアへのWebブラウザエンジンの組み込みや、特殊なデバイスの利用を前提としたWebベースのシステム開発など、ご用命がございましたらぜひinfo@clear-code.comまでお問い合わせください