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の組合せ自体がまずいのかもしれないが、また後で調べてみよう。