#412 Fast Rails Commands
- Download:
- source codeProject Files in Zip (44.5 KB)
- mp4Full Size H.264 Video (21.7 MB)
- m4vSmaller H.264 Video (9.71 MB)
- webmFull Size VP8 Video (10 MB)
- ogvFull Size Theora Video (22.4 MB)
Railsコマンドの多くが実行時間が遅いということにお気づきの方もいるでしょう。例えばRailsアプリケーションのジェネレータを実行した場合、画面への出力が始まるまでに数秒の遅れがあります。その理由はこれらのコマンドが実行されたときにアプリケーションが起動されるからで、開発時にテストを頻繁に実行する場合にこれが最も顕著になります。アプリケーションが大きくなるにつれ、この待ち時間は長くなります。今回のエピソードでは、これらのコマンドの実行時間を短くするいくつかのツールを紹介します。
Zeus
最初に紹介するZeusは、Railsアプリケーションを事前にロードすることによって、コマンドをほとんど瞬時に実行することができます。ただし使用環境に対する要求は厳しく、最新のOSXあるいはLinuxと、ガーベッジコレクションのパッチが適用されたRuby 1.9.3が必要です。パッチがなくても動作するように見えますが、RAMの容量に制限がある場合は必要になるでしょう。インストールするにはgem install zeus
を実行します。zeus
コマンドはBundlerの外で実行するため、アプリケーションのgemfileには追加しません。インストールできたら、zeus start
コマンドを実行してアプリケーションをプリロードします。
Railsアプリケーションのロードが完了するとこの出力が変わります。今回のアプリケーションではほとんどがグリーンになりましたが、これはこれらのコマンドが使用可能であることを表しています。別のターミナルのタブを開いて、通常のRailsのジェネレータコマンドの代わりにzeus generate
(あるいは省略してzeus g
)コマンドで別のジェネレータを実行できます。
$ zeus g model comment content
同じように、rake db:migrate
(あるいはその他のRakeコマンド)を実行する場合もその前にzeus
を付けます。
$ zeus rake db:migrate
これも同じようにより短い時間で実行でき、テストもzeus rake test
を実行することで以前よりもずっと速く動作します。ファンクションテストのためにzeus test
で一部のテストファイルやディレクトリだけを実行する場合はzeus test test/functional
を使用します。
これらのコマンドを実行するたびにRailsアプリケーションを起動しなくてもいいので、出力は今までよりもずっと速く表示されます。ZeusはRailsをdevelopmentとtestのそれぞれの環境ごとにプリロードすることで動作します。このアプリケーションにCucumberが設定されている場合はそこでも同じように動作します。つまり、コマンドはこれらのプリロードされた環境に対して実行されるということです。Zeusは変更部分のリロードにも対応します。例えばモデルを修正した場合も自動的に反映されるので、Zeusをリロードする必要はありません。Zeusを使った場合と使わない場合で、コマンドの実行時間を比較してみましょう。データベースのマイグレーションで試してみます。
$ time bundle exec rake db:migrate real 0m4.054s user 0m3.409s sys 0m0.569s $ time zeus rake db:migrate real 0m1.246s user 0m0.553s sys 0m0.162s
このコマンドはZeusを使わなかった場合は4秒かかりましたが、使用した場合は1秒未満で完了しました。違いはアプリケーションのサイズに大きく依存するので、どのくらい違いが出るのかを実際に自分のアプリケーションを用いて試して見るのがいいでしょう。
この設定の便利な点の一つは、Railsコマンドを普段使っているテキストエディタに統合することによってすばやくフィードバックを得ることができることです。例えばTextmateでは、設定によってテストファイルにカーソルを置いた状態でキーボードショートカットでテストを実行し、その結果をツールチップとして表示することができます。これは、現在のプロジェクトに移動して現在のファイルの選択された行で zeus test
を実行するというカスタムのTextmate bundleを作ることによって実現できます。
cd "$TM_PROJECT_DIRECTORY" zeus test "$TM_FILEPATH:$TM_LINE_NUMBER"
Zeusを実行する場合にひとつ注意することとして、プロジェクトのルートに.zeus.sock
ファイルが作成されますが、これをソース管理の中にチェックインしない方がいいでしょう。Gitを使用しているのであれば、これを.gitignore
ファイルに追加しておきましょう。
SpringとCommands
ZeusはRailsコマンドを高速化させる一つの方法ですが、同じようなツールは他にもあります。他の選択肢もいくつか見てみましょう。その一つがSpringという名前のツールで、Railsアプリケーションをプリロードすることでコマンドを高速化するのですが、Zeusと違う点が2つあります。インストールをgem install spring
コマンドで行うと、spring
コマンドを利用できるようになり、zeus
コマンドと同じような形で使用できます。RailsとRakeタスクをspring
を介して実行できますが、一つの違いは1回目の実行時にバックグラウンドで自動的にRailsアプリケーションを起動するという点です。つまり最初に実行するコマンドでRailsアプリケーションが起動するので、1回目だけは完了までに時間がかかるということです。その後のコマンドは、すでに起動したプロセス上で動作するのでほとんど瞬時に実行されます。
Springを使う場合は、Railsアプリケーションを特別なコマンドで起動するという手間がかからず、停止したい場合はただspring stop
を実行するかターミナルセッションを閉じるだけです。Springのもう一つの特徴は、設定を変更しやすいという点です。プリロードの動作や、どのディレクトリを監視するかを簡単に変更することができます。ただし注意しなくてはいけない点として、ファイルの変更を検知するのにfseventは使わずに、1秒間に5回pollします。OSXでfsevent、あるいはLinuxでinotifyを代わりに使用する場合に、アプリケーションのgemfileに必要なgemを追加する方法について、READMEに記述があります。
最後に紹介するツールがCommandsです。これはRailsアプリケーションをプリロードしないとてもシンプルなアプローチで、RailsコンソールからRailsコマンドを実行する方法を提供します。他の2つのツールと違いアプリケーションのgemfileに追加する方式なので、gemfileを編集後にbundle
コマンドを実行してインストールします。
gem 'commands', group: [:development, :test]
インストールが終わったらアプリケーションのコンソールから各種のジェネレータやRakeタスクを実行できます。
>> generate "model author name" >> rake "db:migrate"
アプリケーションはすでにコンソールにロードされているのでコマンドは高速に実行されます。デフォルトではコンソールはdevelopment環境をロードするため、ここからテストを実行することはできません。そのためには、rails console test
を実行してtest環境でコンソールを開きます。
>> rake "db:migrate" >> test "functional"
テストを実行する前に、 rake "db:migrate"
でテストデータベースのマイグレーションを行ないます。その後にtest
コマンドで実行したいテストのディレクトリを指定して、テストを実行できます。デフォルトの出力は、実行されたSQLコマンドがすべて表示されているので情報が多すぎて見にくいでしょう。これは、テストの実行前にコンソールでActiveRecord::Base.logger = nil
を実行することで止めることができます。CommandsはまだRSpecコマンドをサポートしていませんが、今後対応が予定されています。
今回のエピソードの最後にエピソード285で紹介したSporkにも触れておきます。このツールは、Railsをプリロードすることによってテスト環境を高速化するためのもので、今回紹介したツールと同じ仕組みで動作します。今は少し古くなってしまい、その他のRailsコマンドはサポートしていませんが、もしやりたいことが他のツールでできないという場合、特にWindowsかJRubyを使用しているのであれば、検討する価値があります。