2011-01-03 19:30 追記
rack-config は現在では rack 本体の gem に含まれており, わざわざインストール必要はありませんでした.
(GitHub で送っていた Pull request も閉じてます)
ですが, この記事で紹介している Bundler についての説明は特に問題ありません.

お正月休みということでちょっとした Sinatra 製の Web アプリケーションをいじって遊んでいます.

ローカルでは Pow で開発して, Heroku へのデプロイを考えているんですが, 設定をどう管理するか迷いました.
いろいろ調べた結果, .powenv とか使う方法とかあったのですが, .powenv は Heroku 上では使えません.
どうしたものかとさらに探した結果, rack-config という gem があったのでそれをラップして何とかすることにしました.
(以降はエントリの内容から脱線するので省略. いい感じの方法があれば Twitter とかで教えていただけると幸いです.)

さっそく Gemfile を書いて必要な gem を用意しましょう.
趣味の開発ですし, rack は最新バージョンを指定して, あわよくば地雷を踏み, パッチなど書いて Pull request を送りたい, というのが人情というものでしょう.

これで bundle install を実行すると, 以下のようにエラーで中断してしまいます.

bundle install に失敗

bundle install に失敗

これは rack と rack-config の互換性による問題です.
rack-config は 2012 年 1 月 2 日現在で丸 3 年程メンテされていない状態で, 依存する rack のバージョンが ~> 0.4 と, かなり古い状態です.
(~> 0.4 というのは 0.4.0 以上 0.5.0 未満という意味です)

今回指定した rack 1.4.0 は, rack-config が依存するバージョンに含まれないため, 依存関係の解決に失敗し, エラーとなってしまいます.

といっても, これはメタ情報におけるバージョンの指定の問題で, 実際に互換性があるかどうかは別の問題です.
rack-config の実装は README よりも短くシンプルなもので, 目 rackup した限りでは間違いなく動きそうに見えます.

作者に依頼して依存関係の指定を修正してもらうという手もありますが, 今回はとりあえずの解決をするための方法を紹介します.

大まかな手順

  1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する
  2. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する
  3. 自分のリポジトリに git push する
  4. Gemfile で自分のリポジトリを指定する

Bundler は RubyGems.org からのインストールだけでなく, Git リポジトリからのインストールにも対応しています.
それを利用し, オレオレ Rubygems パッケージリポジトリを作って, そこからインストールしよう, というものです.

今回はたまたま元のプロジェクトが GitHub 上にあったので fork するだけで済みましたが, GitHub 上に無い場合も, 手動でリポジトリさえ作れば, あとは同様の手順で実行できます.

1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する

clone については省略しますが, ブランチを切るのがいいでしょう.
一時的なリポジトリということで, tmp-repo という名前にしてみました.

2. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する

*.gemspec というのは, Rubygems パッケージのメタ情報を定義するファイルです.
依存関係もこのファイルに記述されています.

今回の場合は rack-config.gemspec に以下のような修正を加えました.

rack-config は rack 0.4.0 以上 1.5.0 未満に依存する, ということになったので, rack 1.4.0 も含まれるようになりました.

3. 自分のリポジトリに git push する

特に説明は要らないと思いますが念のため.

4. Gemfile で自分のリポジトリを指定する

先ほどの Gemfile を以下のように書き換えました.

fork した自分のリポジトリの URL と, 今回のために作成した tmp-repo ブランチを指定しています.

あとは最初と同じように bundle install を実行するだけです.

bundle install に成功

bundle install に成功

無理矢理 bundle install することに成功しました!

蛇足: オレはこう思う

今回はオレオレリポジトリを作ってとりあえずの解決を試みる方法を紹介しましたが, せっかくなら作者にも修正を出しておくとよりいいでしょう.
今後その gem を使用するときにいちいちオレオレリポジトリを指定するのは面倒ですし, 他の人にとってもその gem がメンテされた状態で使える方が便利です.

今回の rack-config についても, 実際は以下のような手順を経ています.

  1. 元のプロジェクトを GitHub 上で fork してローカルに git clone する
  2. ローカルの develop ブランチ上で Bundler を使ってテストできるように修正する
  3. GitHub 上で Pull request を送る
  4. develop ブランチから tmp-repo ブランチを作成する
  5. ローカルの tmp-repo ブランチ上で *.gemspec の依存関係の指定を修正する
  6. 自分のリポジトリに git push する
  7. Gemfile で自分のリポジトリを指定する

修正を依頼しつつ, それとは別のブランチをオレオレリポジトリにしています.
(この Pull request が取り入れられるのかはまだわかりませんが…)

ついでに Travis CI で 0.4.0 以上 1.5.0 未満の全ての rack でテストが通ることも確認済みです.

Travis CI 用の設定ファイルは以下のようなスクリプトで, .travis.ymlgemfiles を生成しました.
Travis CI では複数の gemfiles を指定することで, それぞれで bundle install し, 複数のバージョンの gem に対してテストを行うことができます.

なお, rack は 0.4.x のあといきなり 0.9.x にバージョンが飛んでいます.

これで, 見事に全てのバージョンでテストが通ることが確認できました.
(ここまで必死になることも無いとは思いますが…)

, ,

最近 PHP ばっかりやってましたが, 年末で時間もあったので久々に Ruby で作ってみました.
2011 年最後の日にして, 初の Rubygems パッケージリリースです.

これは何?

まぁタイトルの通りです.

Ruby には Kernel.#autoload というメソッドがあります.
これを利用すればいちいち手動で require しなくても, 対象となるモジュールやクラス名が参照されたタイミングで自動的に require してくれるというものです.

autoload は便利ですが, ライブラリ内のファイル数が増えてくるといちいち手動で書くのは大変になってくるので, じゃあ自動生成すればいいじゃない, ということで作ってみました.

使い方

プロジェクトの Gemfile に適当に設定します.

あとは bundler 経由でコマンドを実行するのみです.

これで ./lib ディレクトリ内を自動的に走査し, ./autoloader.rb というファイルを生成します.

これぐらいの規模だとそんなにありがたみは無いですが, 例えば Cucumber のような規模のプロジェクトで使用すると, 以下のようなファイルが生成されます.

ただ, これが実際に Cucumber プロジェクト内で正常に使えるかは試しておらず, よくわかりません.
理由は以下に.

仕組み

仕組みはすごく簡単で, ファイルパスからクラス・モジュール名をルールに従って変換し, それをもとに Ruby スクリプト化しているだけです.

例えば, ./lib/foo/bar/baz/foo_bar.rb というファイルがあるとすると, 中には Foo::Bar::Baz::FooBar というクラス (またはモジュール) があるに違い無い, という前提で生成しています.
ファイルの中身は一切参照していません.

よって, algerb が正常に動作する条件として, ファイルとクラス・モジュールの命名がルールに従っている必要があります.

課題

今のところひとつの ./lib ディレクトリしか対象にできないので, ./vendor ディレクトリ内のライブラリなどは相変わらず require する必要があります.

また, 1 ファイルに複数のクラス・モジュールが存在するなどといった場合にも対応できません.
これを実現するとなると, Ruby パーサなどを用いてもっとちゃんとファイル解析する必要があるでしょう.

あと, 情けない話なんですが, gem コマンド経由でインストールしたときに正常に動作しません…
gem install した時点で bundle install されてないので, 依存関係を解決することができずに, 起動すらできません.
これはただ単にやり方をしらないだけなので, 調べて直すつもりです.

ちょこちょこ修正するつもりではありますが, 普段 Ruby 開発をやっているわけではないので, どういう仕様にすれば役に立つのかよくわかってなかったりします.
Twitter などでご意見いただけると幸いです.

皆さん, ユニットテスト書いてますか.

TDD (テスト駆動開発) によるプログラミングは本当に楽しいものですが, コマンドをいちいち手動で実行するのは面倒ですよね.
テストを自動化しているんだから, その実行も自動化したいですよね.

この記事では, 私が仕事や趣味で使っている PHPUnit を例に, テストの実行の自動化について紹介します.
PHPUnit の, としてはいますが, 他の言語で使えるテクニックもあります.

なお, ここでの自動化は開発しながらの自動実行のことで, CI (継続的インテグレーション) の話は出てきません.

その前に…

私の開発時のターミナルは以下のようになっています.

開発時のターミナル

開発時のターミナル

GNU screen での画面分割を利用して, 左半分をソースコードに, 右上をテストコードに, 右下をテストの実行に使っています.
これは作業の途中でいろいろと変動しますが, 右下ではほぼ常にテストが自動実行されるようになっています.
(ターミナルの透明化を利用してバックグラウンドでニコ動を観ることもできますね)

この小さい画面での実行を前提に, 紹介していきます.

シェルスクリプトを利用する

まずはとにかく, 決められたコマンドを定期的に実行しましょう.

コマンドを定期的に実行, というと watch コマンドを思い浮かべる人が多いと思いますが, これは使いません.
何故か, ターミナル上の色が無効化されてしまい, レッドやグリーンがわかりづらいからです.

色の使える watch は無いか, と探した所, superuser という Q&A サイトにちょうどいいものがありました.
bash watch command with colors preserved

ここへの書き込みを参考に, 以下のようなシェルスクリプトを用意しました.

適当な場所に保存して, chmod +x して実行権限を与えたら, 以下のように使用することができます.

$ ./watch2 phpunit --colors

これで, 出力の色づけを維持したまま, PHPUnit を 2 秒間のインターバルで起動し続けることができるようになりました.

プロジェクトの規模によっては全てのテストを実行するのに時間がかかることもあるので, そういうときは特定のファイルを指定しましょう.

$ ./watch2 phpunit --colors tests/Foo/BarTest.php

今自分が作業しているファイルだけを指定することで, フィードバックを素早く的確に得られるようになります.
コマンドの指定は手動ですが, 環境の用意はとても楽なのでよしとしましょう.

Stagehand_TestRunner を利用する

Stagehand_TestRunner は, PHPUnit だけでなく Lime や PHPSpec にも対応したテストランナーです.
(2011-08-16 14:00 追記: Lime への対応は勘違いでした. @heavenshell さんご指摘ありがとうございました.)

インストールについては本家の Wiki を参照しましょう.
テスト駆動開発のためのテストランナー

PHPUnit 本家よりも優れた表示が特徴です.

Stagehand_TestRunner による実行結果

Stagehand_TestRunner による実行結果

この出力形式は testdox と呼ばれるもので, テストメソッド名を英文っぽく表示してくれます.
testdox を表示する機能自体は PHPUnit にもあります (--testdox オプション) が, Stagehand_TestRunner を使うと色づけされるので, よりわかりやすくなります.

Stagehand_TestRunner には他にも様々な機能がありますが, ここで紹介するのはディレクトリの監視によるテストの自動実行です.

これを phpunit.xml として保存して, 同じディレクトリ上で以下のコマンドを実行します.

$ phpunitrunner --phpunit-config=phpunit.xml -cRa

c が色づけ, R が指定ディレクトリ (phpunit.xml で指定していますね) 以下を再帰的に, a が自動実行のためのオプションです.

これで, ディレクトリに変更のあったときだけテストが実行されるようになりました.

実行対象のテストを正規表現で絞り込んだりすることもできますが, それらの詳細については以下の Wiki が参考になります.
Stagehand_TestRunner ユーザーガイド

watchr を利用する

最後に紹介するのは, 最近一番よく使っている方法です.
watchr はファイルの変更を監視するツールで, 検出時にあらゆる処理をフックさせることができます.

watchr は Ruby 製のツールなので, 処理は Ruby で書く必要があります.
といっても, 簡単な正規表現が書ければ充分なので, Ruby 未経験でも問題無いでしょう.

インストールについては Rubygems をインストールして gem install watchr するだけです.

watchr の設定ファイルとして, 以下のようなものを用意します.

これは src ディレクトリのソースコードの変更時に対応するテストを実行すること, テストコードの変更時にはそのファイル自身のテストを実行すること, を設定しています.

そして以下のようなコマンドを実行することで, 監視状態に入ります.

$ watchr phpunitrunner.watchr

前提として, ソースコードが src/Foo.php であればテストコードは tests/FooTest.php というように, 命名規則にそったファイル名になっていることが必要です.

変更のあったときにだけ, そのファイルだけテストが実行されるので, 素早く確実にフィードバックを得ることができます.

また, Ctrl + \ を押すことで, 全てのテストを実行することもできます.

注意としては, 監視対象のファイルの読み込みが watchr の起動時なので, 例えば途中で新規に追加したファイルは監視対象に入っていないことです.
ある程度開発が進んで, 作業のメインが既存ファイルの編集になったときに, より高い効果を発揮するでしょう.

まとめ

テストだけでなく, その実行まで自動化することで開発にリズムが生まれ, フロー状態に入りやすくなります.
開発効率を上げ, 楽しい TDD ライフを送りましょう.

その他, オススメのツールややり方などありましたら, 是非 @yuya_takeyama まで教えてください.

, , , ,