第9回 helper :all

2008/05/05

あまり目立たない変化ですが、Rails 2.0 からコントローラ定義の中で helper :all と書けるようになりました。

Rails 1.2 までは app/helpers ディレクトリに foo_helper.rb, bar_helper.rb, baz_helper.rb という 3 個のヘルパーモジュールがあれば、次のように書く必要がありました。

class ApplicationController < ActionController::Base
  helper :foo, :bar, :baz
  
  ...
end

Rails 2.0 からは、簡潔にこう書けるわけです:

class ApplicationController < ActionController::Base
  helper :all
  
  ...
end

しかし、ApplicationController で helper :all と宣言してしまうことには、デメリットもあります。この宣言は、事実上、すべてのコントローラですべてのヘルパーモジュールを共有するという宣言になります。それは、Rails 1.x 時代の一つのテクニックを捨てることを意味します。

Rails 1.x では(helper :all 宣言をしなければ Rails 2.0 でも)、コントローラ名に '_helper.rb' を加えた名前のヘルパーモジュールが自動的にロードされるという性質を利用して、ちょっとした工夫が可能です。すなわち foo_helper.rbbar_helper.rb で同名のメソッド(例えば、page_title)を定義しておいて、foo コントローラと bar コントローラで同じメソッドを利用しつつ表示を切り替える、といった工夫です。

helper :all と宣言すると、すべてのヘルパーモジュールがインクルードされてしまうので、もはやコントローラごとにインクルードするヘルパーモジュールを制御できません。foo コントローラでも bar コントローラでも page_title ヘルパーはまったく同じ振る舞いをします。しかも、困ったことに foo_helper.rbbar_helper.rb のどちらの定義が採用されるかは予測できません。内部的には、

Dir["#{HELPERS_DIR}/**/*_helper.rb"]

というコードでヘルパーモジュールファイルのリストを生成して、順番に読み込んでインクルードしているに過ぎません。

Rails 2.0 で helper :all が導入され、しかもアプリケーション作成時に生成される ApplicationController に helper :all という宣言が書かれるようになった、ということは、Rails の設計者たちがコントローラごとにヘルパーモジュールを用意するという初期のアイデアを捨てる方向に向かっていることを意味するのかもしれません。

依然として controller ジェネレータはヘルパーモジュールを一緒に生成しているので、そうではないのかもしれません。しかし、私はこれまでコントローラごとに生成されたヘルパーモジュールを持て余していました。実際には、ほとんどが空のまま放置されていたのです。私が Rails を学習していた当時は「コントローラごとのヘルパーモジュール」というアイデアを気に入っていましたが、現実にはあまり役に立ちませんでした。多分、世の中の多くの Rails 開発者が同じように考えているのではないでしょうか。

さて、ヘルパーに関連して Rails 2.0 でもう一つのささやかな変更がありました。従来はコントローラ名に対応したヘルパーモジュールが存在しない場合にログに警告が記録されていました。実害はありませんが、何となく気持ちが悪いと思っていたところです。今後はこの警告は記録されません。helper :all が導入されたのなら当然の仕様変更であり、歓迎すべきことです。
--
黒田努