2012-07-28 18:59:15
シェルコマンドに関して、昨日は 2 つの記事が目につきました。
これらはそんなに難しい処理ではないものの、この辺の技量は普段どれぐらい黒い画面上で生活しているかによって大分違ってくる気がします。
こういうのを効率的に学習できツールがあれば便利なんじゃないかと思ったので、shellfish というツールを作りました。
yuya-takeyama/shellfish
Shellfish とは
与えられた問題を、コマンドを駆使して解決していくゲームです。
Shellfish のシェル上でコマンドを実行し、出力が問題ごとの期待結果に一致すれば正解です。
言ってみればターミナル上で動作する、ワンライナー専用の Anarchy Golf みたいなものです。
問題ファイルは DSL で定義できます。
インストール
GitHub 上の README.md で確認できるので詳細は割愛しますが、gem install でも bundle install でもインストールできます。
遊んでみる
遊ぶには問題ファイルが必要ですが、引数無しで実行すると標準添付の FizzBuzz 問題に挑戦できます。
[caption id="attachment_1923" align="aligncenter" width="532" caption="Shellfish で FizzBuzz 問題に挑戦"]
[/caption]
問題のタイトル、説明文 (あれば)、期待結果が表示されます。
後は出力が一致するまでコマンドを組み立てて、実行していきます。
間違えると diff が表示されます。
[caption id="attachment_1926" align="aligncenter" width="539" caption="FizzBuzz 問題に挑戦 (間違い)"]
[/caption]
少し直して、出力が一致すればクリアです。
[caption id="attachment_1929" align="aligncenter" width="598" caption="FizzBuzz 問題に挑戦 (正解)"]
[/caption]
もっと問題を解いてみる
先に紹介した id:Yamashiro0217 さんの記事の問題を、そのまま Shellfix 用の問題ファイルにしてみました。
(勝手にやってるので問題あればご連絡ください)
yuya-takeyama/learn-shell-commands-with-shellfish
こんな感じで問題だけをリポジトリにして配布するのも簡単なので、興味のある方は是非やってみてください。
エンジニアの新人教育とかにも使えるといいですね。
Shellfish の改善点
- 現状だと完全一致のチェックしかできないけど、問題によっては diff コマンドでいうところの --ignore-all-space とか --ignore-eol-style とかできるようにしたい。 (そういうライブラリが必要)
- モチベーションを保てるようにゲーム性を持たせたい。
2012-06-30 16:53:54
Espérance という BDD っぽくアサーションを行うライブラリを書いています。
とりあえず PHPUnit の中で使ってみるイメージはこんな感じです。
あと、前に中途半端に作った xSpec フレームワーク Speciphy と組み合わせるとこんなイメージにもできそうです。 (まだ作ってませんが)
なんで should でなく expect なのか
RSpec 3 からは expect がデフォルトになる、という記事が先日ありました。
RSpec's New Expectation Syntax
RSpec といえば "foo".should == "foo" といった具合に、全てのオブジェクトに should メソッドを生やしての アサーション (エクスペクステーション) が特徴でしたが、やっぱり Kernel モジュールに勝手にオブジェクト足されるのは気持ち悪いよね、ということで古き良き Test::Unit を使い続ける人も一定数いるように思われます。
should と比較して、expect は RSpec コンテキスト (多分 ExampleGroup とか Example とかの中だろうけど特に読んではいません) の中に存在するメソッドなのでグローバル空間に副作用がありませんし、それでいて英語としても読み下しやすい DSL だと言えます。
そして expect といえば、JavaScript の BDD ライブラリでは結構ベタだったりします。
Jasmine もそうですし、Socket.IO の作者による expect.js というものもあります。
Espérance は expect.js を PHP に移植して作っています。
PHPSpec について
PHP はオープンクラスでないので、全てのオブジェクトに should メソッドを生やす、なんてことはできません。
そこで PHPSpec では $this->spec("foo") とすることで、値を Interceptor オブジェクトでラップし、Interceptor がマジカルな DSL を実現する、という作りになっています。
PHPSpec の Interceptor は独立したライブラリではありませんが、Speciphy でも拝借しており、独立性の高いモジュールでもあります。
PHPSpec の Interceptor は凄いんですが、正直言って頑張り過ぎなんではないかと思っています。
PHPSpec 本体の spec を見ていると $this->matcher->getDescription()->should->be('be equal to 1'); なんてことをしていたりします。
$this->expect($this->matcher->getDescription())->to->be('be equal to 1'); とかで充分やん、とか思ってしまうわけです。
とはいえ Espérance も __get とか __call とか使ってますし、マジカルであることに変わりはないのですが、作りは大分シンプルになっていると思います。
expect.js の実装を真似ているので、expect.js が凄いというだけの話でもあるのですが。
expect.js の DSL の実装について
ところで expect.js では expect(1).to.be(1) といったプロパティのチェーンを、Assertion オブジェクトを再帰的に生成して to プロパティに新しい Assertion を差し込む、なんてことをしており、一度 expect(1) としただけで 40 数回ぐらいコンストラクタが呼ばれるています。
PHP では __call でプロパティ読み出しをオーバーライドすることができるので、Espérance では再帰的なコンストラクタは行われません。
JavaScript でも最近の処理形だと Object.defineProperty() とかで似たようなことができるような気がするのですが、それをしないのはより幅広い環境での使えるようにということなんでしょうか。 (JavaScript あんまり知らないのでよくわかりません)
名前について
Google 翻訳に expectation と入力してフランス語として出てきた単語から適当に選びました。
フランス語を名前として使うアイディアは、最近話題になった React (通称 node.php) でも使われているイベントディスパッチャライブラリ Événement のパクリです。
今後について
Speciphy もそうですが、ボクはライブラリやフレームワークを作ってもメンテが続かないタイプの人間だということだけ書いておきます。
でもまぁこういう感じでアサーションだけがライブラリと独立しているのは割とありなんじゃないかなーとは思いますし、Packagist への登録ぐらいはやっておきたいと思っています。思っているだけですが。
というわけでレバ刺しを食べに行きます。