全ての model で module を取り込む方法。
一つ一つの model で include とか extend とか書くのはかっこ悪いので、全ての model で自動で取り込むように設定する。
使用感
自宅でも spec が動作するようになった!
Park Place 動作も十分速い!!
手順
必要な gem を入れる
merb が動作している環境では、sqlite3-ruby, activerecord, activesupport, camping があれば良いらしい。
http://d.hatena.ne.jp/rabbit2go/20100427/1272376003 を参照して、必要な gem を install する。なお、markaby については、本来必要なバージョンは 0.5 であるが、依存関係の問題でより新しいバージョンがインストールされてしまう。これにより parkplace が起動しなくなってしまう。これは、0.5 以外のバージョンを削除して対応する。
インストール
入手したソースの TOP に移動して、
$ ruby setup.rb config $ ruby setup.rb setup $ ruby setup.rb install
※ 最近のものは、最新の activerecord, activesupport で動作しなかったりするので、bin/parkplace でそれらを読み込んでいるところを編集して、バージョンに依存しないように変更する。
MacBook 再起動後に初期化するための設定
$ ln -s /tmp ~/.parkplace
Park Place の起動
$ parkplace
起動するときに初期ユーザー admin が自動作成される。
S3 クライアントの設定
- ホスト: 127.0.0.1
- ポート: 3002
- Key: 44CF9590006BF252F707
- Secret: OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV
Key と Secret については、最初に作成されるユーザーについては、どの環境であっても上記の値で固定されているらしいので、これを使わせてもらう。
さらに spec の実行を速くする
MySQL のデータを RAM Disk 上に置いて 3 倍の速度を達成したわけだが、さらなる高速化を考えた。
遅いところ
経験上、fixture を何度も読込んでいると遅いことがわかっている。fixture を before(:each) ではなく、before(:all) で読み込むようにすれば、回数が減るので速くなる。
ちなみに fixture は指定されたディレクトリ配下のファイル郡が
MySQL 上の一つの DB の各テーブルに対応させた状態にして管理している。
読込の単位は、このディレクトリ毎としている。
fixture の読込を減らす
同じ fixture を何度も読み込んでいることが問題なわけで、最初に一回読み込んだら、その snapshot を取って、再読み込みの際は snapshot まで戻せばよい。
MySQL に snapshot 機能は存在しない。
よって、 fixture を登録しているところで次のような処理を行うことで、これを実現した。
- 過去に fixture が読込済みで無い場合
- 通常の fixture の登録処理を行う
- mysqldump で DB 全体をバックアップする
- fixture が読込済みの場合
- mysql コマンドでバックアップからリストアする
速度
さらに 1.5 倍の速度が達成された。
ちなみに mysql & mysqldump を TCP/IP 経由ではなく、UNIX DOMAIN Socket で接続すると 10 秒程度の向上がみられた(上記はこれを反映している)。
Ram DISK : 366 sec
fixture を dump & restore : 232 sec
さらにさらに速くする
以下は未検証。
MySQL には snapshot 機能は無いわけだが、 Transaction は条件を限定すればそれっぽく使えるはず。
普通だったら処理の完了後は commit され、再度テストに使える状態ではなくなる。でも commit せずに rollback すれば、テストデータ投入時と同じ状態になるため、再度テストに使うことができるようになるはずである。
今作っているシステムは Transaction の「入り」、「出(commit)」を独自の Class によって管理しているので、「出(commit)」の部分では何も行わないようにして、fixture の再読込みで行うようにすれば、これは実現可能であると思う。
また、多分 MySQL の commit は「commit 後に実際に DB のデータを書き換える」構成のようなので、この方法によってかなりの速度向上が見込めるかと思う。
Mix-in の中で before :save を仕掛ける
Mix-in を include しても before :save などに、 Hook を仕掛けることができない。
before は module が定義される時に通常の method として実行されるが、そのモジュールには :save というメソッドは存在しないためである。
module が対象の model に include された後に、対象の model の instance 上で before :save を実行する必要がある。次のように記述すると、これを実現できる。
module TargetModule def self.included(base) base.before :save do : : end end end
これで、 include しただけで Hook の設定が行われる。
以上は、 dm-optlock module のソースコードを読んでいて気がついた。
self.included 自体は ruby の機能の一つだ。