Relative Path プラグインの Rails 2.0 対応

Relative Path プラグインRails 2.0 で動かないという苦情がメールであったので、それに対応しました。

ついでに、CodeReposSVN レポジトリーを移動しました。

http://svn.coderepos.org/share/lang/ruby/relative_path

最新版を使いたい方は、

 ./script/plugin install http://svn.coderepos.org/share/lang/ruby/relative_path

を実行してください。

Rails 2.0 対応で苦労したのは、Routing Optimisation という仕組みに対応するのと、Rails 1系 と Rails 2系に両対応するのにどうするかでした。

Rails 2.0 には edit_user_path などで、url_for(:controller => :user, :action => :edit) と同じ URL を生成できるという方法がある。もともと Rails では url_for の実行は結構重たかったのです。重たい理由は、config/routes.rb での :requirements などによる複雑な仕組みがあって、ややこしいことをしないといけないからです。Rails 2.0 ではこの反省をふまえて、 edit_user_path などと指定すると高速に動作するように最適化されています。

この処理が Routing Optimisation です。

Routing Optimisation は実装としては、事前に ActionController::Routing::Optimisation で edit_user_path メソッドなど
のメソッドを自動的に定義することで高速な URL 生成を可能にしています。 edit_user_path を使えば url_for よりもずっと単純で高速なアルゴリズムで url やパスの生成ができるのです。

しかし、これが Relative Path プラグイン作成の上で障害になりました。 Relative Path プラグインでは、url_for で URL やパスの生成が行われるということが前提になっている。この前提を覆すような動作をしている上、edit_user_path などが一体全体どこで定義されているかがなかなか理解できず実装がてまどりました。

方法は2つあってメソッド定義のためのコード片を改変する方法と、定義されたメソッドの動作を後から変更する方法がありえました。

両方の実装を書いたのですが、明らかに見通しが良かった定義されたメソッドの動作を後から変更する方法を採用することにしました。 alias_method を使って、 absolute_edit_user_path などに退避して、 edit_user_path メソッドを再定義するという Ruby on Rails ではよく使われる方法です。

あと、Relative Path プラグインRails 2.0 対応で手間取ったのは、Rails 1系の対応と、 Rails 2.0 系の対応をまったく同じファイルで行おうと思ったのですが、その差をどこで吸収させるかという点でした。

結局、差がある部分を module として切り出して、振る舞いを変えることにしました。
具体的には次みたいなかんじ。

module A
  def self.append_features klass
     super
     if RAILS_GEM_VERSION < "2"
       klass.__send__ :include Module_for_RAILS1
     else
       klass.send! :include Module_for_RAILS2
     end
  end

  module Module_for_RAILS1
    ...
  end

  module Module_for_RAILS2
    ...
  end
end

append_features の中で include メソッドを呼んでいる理由は、Module_for_RAILS2 の append_features を呼ぶ場合に klass を大元のクラスにするための工夫です。module A が Module_for_RAILS2 を include するようにすると、klass が A になってしまって、困る場合があります。

ではでは。