Railsのプラグインを作る基礎テク

developmentモードとproductionモード

今日チームのメンバーに聞かれて気付いたことですが、developmentモードが便利過ぎるからか、リクエストが来るたびにコントローラのクラスがロードされるように勘違いする人もいるみたいです。

プラグインを作るときにはproductionモードとdevelopmentモードの違いを把握しておかないと、プラグインを作るのに困るので、念のため書いておきます。

考え方としては、基本はproductionモードです。productionモードで各ファイルは基本的に一度しかロードされません。それだとコードを変更するたびに反映させるためにはにいちいちサーバーを再起動させなければならないので、開発時はあまりにも大変なので、Railsのdeveloppmentモードでは特殊な仕組みで、リクエストが来るたびに必要なファイルをロードします。

const_missingと命名規則

Rubyではファイルをロードするためには、通常requireあるいはloadメソッドを使いますが、通常Railsアプリではモデルやコントローラの定義してあるファイルをrequireする必要はありません。

これは、どうなっているかというとRubyにはModule#const_missing( http://www.ruby-lang.org/ja/man/html/Module.html#const_missing )というメソッドありまして、参照しようとした定数が存在しないときに呼び出されるようになっています。

Railsではこのconst_missingメソッドを拡張して、参照しようとした定数がないと、命名規則からそれが定義されているはずのファイル名を求めそれをロード(developmentモードではload、productionモードではrequire)します。

この辺の実装は、activesupportのlib/active_support/dependencies.rbにあります。

alias_method_chain

通常のプラグインでは、モジュール(クラスを含む)で宣言されているメソッドの振る舞いを変更する場合、alias_method_chainを使います。これはactivesupportのlib/active_support/core_ext/module/aliasing.rbに定義されているメソッドで、以下のようなことができます。

require 'rubygems'
require 'active_support'
class A
  def foo
    "foo"
  end

  def foo_with_hoge
    foo_without_hoge + " with HOGE"
  end

  alias_method_chain :foo, :hoge
end
a = A.new
a.foo
# => "foo with HOGE"
a.foo_with_hoge
# => "foo with HOGE"
a.foo_without_hoge
# => "foo"

実はalias_method_chainを使わなくてもこんな書き方でも同じようなことができます。

  # alias_method_chain :foo, :hoge
  alias_method :foo_without_hoge, :foo
  alias_method :foo, :foo_with_hoge

alias_methodは元々Rubyにあるメソッドです。http://www.ruby-lang.org/ja/man/html/Module.html#alias_method

要は元々あるメソッドに_without_hogeを後ろにくっつけた名前で別名を付けて、拡張するメソッドを元々のメソッドの名前の別名を付けて上書きしちゃいます。