Spika Hackathon に参加してきた
久々のブログです。
夕方に3DS LL とポケモンのセットが届いたのですが、電源がついてなくてあんまり遊べてません。
今週末は @kuzuha さん主催での Spika Hackathon というイベントに参加してきました。
Spika がどういうものなのかはこの辺の記事を見ればいいと思います。
- Spika - world first opensource messenger for ios/android
- 世界初 メッセンジャーアプリ「Spika」を完全オープンソースで公開、フロントからバックエンドまで提供
TechWave に記事が紹介されて直後から、コードの品質がヤバいと話題になっていました。
僕自身も以下のような印象を持ちました。
(アッ、メッセンジャーの綴りが違う...)
試みは面白いと思うけどコード品質酷過ぎる気がする。 PHP のバックエンドしか見てないけどテストとか全くないし。ある意味「アプリ界のWordpress」感はすごいある。 http://t.co/V20USW78bz #messanger #spika #ios …
— Yuya Takeyama (@yuya_takeyama) October 8, 2013
という具合に割と強めに Dis ったりはしたものの、ちょうど同じ頃に以下のようなツイートを見ていたこともあって、せっかくだからいろいろなおしてみるかという気になったのでした。
(これはまた別の会社のゲームフレームワークについての文脈だったと思うけど)
まぁ、本当に実力ある人は小馬鹿にしたり皮肉言う前に「こんくらいやっとけバカ!」ってコミットメッセージとともにプルリク投げるんじゃないかと思いますよー
— ぼうくん (@VoQn) September 26, 2013
この記事では送ったプルリクの詳細について残しておこうと思います。
#6 added CI base
これは参加者 3 名による複数の修正が含まれていて、以下のような内容を含みます。
- Travis CI の導入 (@kuzuha さん)
- Silex の WebTestCase を使った機能テスト (@NAKANO_Akihito さん)
- Coveralls の導入
Travis CI はそろそろ説明不要の感もありますが、日本 (というか PHP コミュニティ) ではあんまり流行ってないのかな、と思われる Coveralls について簡単に説明すると、コードカバレッジレポートを残すための PaaS です。
Coveralls に GitHub の OAuth を使ってサインインし、計測対象とするリポジトリを登録しておき、適宜設定を行うと Travis CI のビルド後にレポートが送られるようになります。
Ruby だと Gemfile に coveralls の gem を追加するぐらいで使えて便利です。
PHP だと @satooshi_jp さんによる php-coveralls というライブラリにより利用できますし、その他の言語でも大体使えるようになってきていることと思います。
このプルリクにより、ユニットテスト・継続的インテグレーションの基盤がとりあえずできました。
[caption id="attachment_2201" align="alignnone" width="557" caption="プルリクを送ると自動的にカバレッジレポートが送られるようになる"][/caption]
[caption id="attachment_2204" align="alignnone" width="533" caption="テストが実行されている場所・されていない場所が一目でわかる"][/caption]
WebTestCase を使ったユニットテストについては、前提として Spika の中の人が develop ブランチで Silex への移行を予めやっていた、というのが大きかったです。
#7 Change directory structure
PSR-0 の規約に従ってディレクトリ構造の変更を行い、 composer.json にもオートローディングの設定を行いました。
[caption id="attachment_2211" align="alignnone" width="604" caption="規約に沿った命名にすることで require_once が不要になる"][/caption]
これは地味ですがやっぱり重要だと思います。
require_once を書かなくてよくなる、というだけでも QOL が向上しますね。
#11 データベースのインターフェイスを追加
Spika はバックエンドのストレージとして CouchDB を使っていて、それを隠蔽した SpikaDBHandler クラス (現 CouchDb クラス) というのがあるんですが、その public メソッドだけを抜き出して DbInterface というインターフェイスを定義しました。
インターフェイスを定義することで、それに沿った実装さえ用意すればバックエンドのストレージを容易に置き換えられる、というのもありますが、とりあえずはユニットテストを書く上で便利、というのもあります。
データベースに依存したクラスであっても、インターフェイスを元にしたモックオブジェクトさえあればオンメモリで動作するユニットテストが書けるようになります。
#15 Dependency Lookup ではなく Dependency Injection を使用する
Silex による開発で割とやってしまいがちな間違いは Dependency Lookup により依存オブジェクトを取得するようなモジュールを書いてしまうことだと思います。
どういうことかというと、大体以下の記事に書かれているのでした。
この記事では Service Locator という言葉が使われていますが、Service Locator による依存性の解決は Dependency Lookup です。
#19 $app['beforeTokenChecker'] のモジュール化 & ユニットテスト
いくつかの API では、事前処理としてアクセストークンによる認証を行っているのですが、その認証機構が無名関数により実装されていて、ユニットテストが全くかかれていない状態だったので、クラスとして再実装しました。
前提として、#11 においてデータアクセス部分をインターフェイス化していたからこそテストが書きやすかった、というのがあります。
ところで、Silex の Before/After Middleware という機構はちょっと使い辛いんじゃないかなーと...
その辺を解決する仕組みとして、WSGI/Rack/PSGI あたりを意識した Stack というフレームワークがあって、これを使えば大分いい感じになりそうなんですが、要求される PHP のバージョンが 5.3 から 5.4 に上がってしまうので、とりあえずやめました。
まとめ
レガシーな PHP アプリケーションをリファクタリングする上で、それなりに汎用性の高い知見が得られた気がするので、まとめておきます。
- テスタビリティを考慮したフレームワークを使うといろいろ楽 (Silex の WebTestCase 便利)
- パッケージの管理は Composer 一択
- PSR-0 を守ると require_once とか書かなくてよくなって楽
- Travis CI による継続的インてグレーションは基本
- ついでに Coveralls によるカバレッジレポートの自動化もやっておくと便利だしカッコいい
- モジュールを作るときはクラスではなくインターフェイスに依存するようにするユニットテストが楽
- Dependency Lookup じゃなくて Dependency Injection を使った方がユニットテストが楽
人生楽して生きたいものですね。