IISサイト/マルチアプリケーション環境での相対パス
IIS7によるRuby on Rails(以降RoR)のホスティングだが、困った問題が発生してしまった。
IISのサイトにRoRのアプリケーションをマップする場合、大きく以下の方法があると前回のエントリで書いた。
- サイトを単一のRoRアプリケーションにマップ (便宜上 1:1と呼ぼう)
- サイト下の複数の(仮想)ディレクトリをRoRアプリケーションにマップ (こちらは1:Nと呼ぼうか)
1:1は非常に単純であり、WEBRickで動作させる時と比べても何も変わらないので混乱も無いが、IISでは同一ポートを持つサイトは複数起動できない(当たり前だ)ので、サイト下に同一ポートを割り当てた複数のアプリケーションを配置できない。
一方、1:Nは、複数のアプリケーションを一つのサイトとして管理できるためアブケーション単独で更新、配置が出来る。ただし、この方法ではRoRアプリケーションの一般的なURIである
http://hostname:port/コントローラ名/アクション名
ではなく(RoRのURLはパス上にアプリケーション名は持たない)、
http://hostname:port/アプリケーション名(又は仮想ディレクトリ名)/コントローラ名/アクション名
となるため、/config/routes.rbを修正する必要がある。
例) アプリケーション"addressbook"で使用されるroutes.rb
ActionController::Routing::Routes.draw do |map| map.resources :people map.connect 'addressbook/:controller/:action/:id' map.connect 'addressbook/:controller/:action/:id.:format' end
前回のエントリで修正した通りだが、この状態では単純なアクションだけなら問題無くとも、scaffoldを生成するとビューテンプレート上にlink_toで作られたURLが期待通りにならないことが解ったのである。
例えば、scaffold personでpersonモデル(peopleテーブル)用に生成されたビューである、index.heml.erb上にて、personモデルに対応したデータを新規に登録するためのリンク"NewPerson"だが、URLを見てみると、期待された
http://localhost/addressbok/people/new
ではなく、
http://localhost/people/new
となってしまっている。
これでは404(Not Found)という結果になってしまうだろう。:(
さて、これを回避するためにはどうすれば良いかを考えてみた。
1. アプリケーション名を付加した絶対パスでリンクを生成する
2.相対パスでリンクを生成する
RFC的には全て絶対パスで表示するのがベストだが、現時点ではこの方法が解らない。2.の相対パスは、別件で見つけたcuzic氏のRelativePathプラグインが使えそうだ。
Railsのプラグインを単独でインストールするのは初めてだが、早速同プラグインを落としてインストールしてみた。
仕事場のネットワークはプロキシを介しているので、直接インストールできないため、SVNリポジトリからコードをダウンロード、所定のディレクトリを作成し、以下のコマンドを実行した。
E:\www\addressbook>ruby ./script/generate plugin relative_path E:\www\addressbook\vendor\plugins/relative_path/lib/relative_path.rb:61:in `append_features': undefined method `send!' for ApplicationController:Class (NoMethodError) from E:/www/addressbook/app/controllers/application.rb:5:in `include' from E:/www/addressbook/app/controllers/application.rb:5
ぐお、いきなり失敗。
relative_path.rbのソースの該当行(line:61)を見ると、以下のようになっている
if RAILS_GEM_VERSION < "2" klass.__send__ :include, RelativePath_for_Rails1 else klass.send! :include, RelativePath_for_Rails2 end
RAILS_GEM_VERSIONは2.2.2のはずなんでelseブロックに入る。そしてこの"klass.send!"メソッドが見つからないということらしい。
んん? send!メソッドって?
とここで時間切れ。
最悪、Ruby 1.8.7(ms-win32)とRails 2.2.2の組合せ自体がまずいのかもしれないが、また後で調べてみよう。