ひとつのプロジェクトで複数のRailsアプリケーションを開発する際のお約束ふたつ

加藤です。

ひとつのプロジェクト内で複数のRailsアプリケーションを開発するケースが良くあります。例えば、サービス公開用のアプリケーションと管理用のアプリケーションを別けて開発するような場合です。今回は、そのような場合に最低限やっておくと良いお約束です。この下のような構成を例に紹介します。

タスクをRakefileにまとめる

開発中はテストを実行したり、サーバーを起動したりと色々なタスクを実行することになります。しかし、複数のアプリケーションを同時に開発する場合、それぞれのディレクトリを行ったり来たりしながら実行するのは面倒です。頻繁に実行するタスクは一ヶ所でまとめておこなえるよう myproject/Rakefile に書いておくと良いでしょう。

私の場合、まず初めにタスクを実行するためのユーティリティクラスをRakefile内に用意します。中身はディレクトリを移動してシェルコマンドを実行するだけのシンプルなものです。

class Cmd
  @@base_dir = File.dirname(__FILE__)

  def self.cd_sh(dir, cmd); cd dir; sh cmd; end

  def self.start(app_name, port = 3000)
    cd_sh "#{@@base_dir}/#{app_name}", "mongrel_rails start -d -p #{port}"
  end

  def self.stop(app_name)
    cd_sh "#{@@base_dir}/#{app_name}", "mongrel_rails stop"
  end

  def self.test(app_name)
    cd_sh "#{@@base_dir}/#{app_name}", "rake"
  end
end

そして、このユーティリティクラスを利用して、アプリケーションごとにテストの実行やサーバーの起動・停止をおこなうRakeタスクを作成します。開発中はサーバーの起動ポートを別けないといけませんので、アプリケーションごとのポート番号はここで決めておくと良いでしょう。

namespace :myapp do
  desc "run myapp"
  task :start do; Cmd.start("myapp"); end
  
  desc "stop myapp"
  task :stop do; Cmd.stop("myapp"); end
  
  desc "test myapp"
  task :test do; Cmd.test("myapp"); end
end

namespace :myadmin do
  desc "run myadmin"
  task :start do; Cmd.start("myadmin", 4000); end
  
  desc "stop myadmin"
  task :stop do; Cmd.stop("myadmin"); end
  
  desc "test myadmin"
  task :test do; Cmd.test("myadmin"); end
end

次に、作成したRakeタスクをまとめて実行するためのRakeタスクを追加します。

desc "run all applications"
task :start_all => ['myapp:start', 'myadmin:start'] do; end

desc "stop all applications"
task :stop_all => ['myapp:stop', 'myadmin:stop'] do; end

desc "test all applications"
task :test_all => ['myapp:test', 'myadmin:test'] do; end

最後にデフォルトタスクに test_all を設定して完了です。デフォルトタスクは、コミット前に必ず実行するように習慣づけたいですね。

task :default => :test_all do; end

svn:externalsでモデルとフィクスチャを共有する

この例のようなプロジェクトの場合、複数のアプリケーション間で同じデータベースを使うことがほとんどだと思います。そうするとアプリケーションごとにActiveRecordモデルを作成することは DRY ではないので共有する手段が必要になります。

理想を言えば、共通部分を括りだしてgemライブラリにするなりRailsのプラグインにしたりするのが良いのでしょうが、単一のプロジェクト内で使うだけの場合、ライブラリの設計や管理で多くの時間を取られるのはもったいないですね。Subversionでお手軽に共有しましょう。

ちなみに、モデルをどのアプリケーションで開発するかを決めなくてはいけませんが、今回の例のように管理用のアプリケーションがある場合はそれをベースにするのが良いと思います。管理用アプリケーションの開発を先にすすめると、その他のアプリケーションの開発中に便利につかえることが多いです。

ということで、まず、app/modelsを共有する設定をおこないます。今回の例では myproject/myadmin/app/models/ 以下に作成したモデルを myproject/myapp/app/models/ 側で取り込むように設定します。一旦、 myproject/myapp/app/models/ を削除した後、myproject/myapp/app/ に移動して、次の例のように svn:externals の設定をおこないます。

~/work/myproject/myapp/app% svn propset svn:externals "models svn+ssh://server/repo/myproject/trunk/myadmin/app/models" .

また、テスト用のフィクスチャも個別に作るのは面倒なのでこれも共有します。尚、ユニットテストはアプリケーションごとに追加しておこなうことが多いので共有しない方が良いでしょう。

~/work/myproject/myapp/test% svn propset svn:externals "fixtures svn+ssh://server/repo/myproject/trunk/myadmin/test/fixtures" .
Bookmark and Share