libディレクトリの使い方とオートロード機能

この関連ではまったので、自分用にメモしておきます、

libディレクトリの使い方

railsRAILS_ROOT/libディレクトリには、プロジェクトで使用するライブラリを置いておきます。
例えば、既存クラスに機能を追加したい場合、まず追加機能を書いたファイルを用意します。

  • hash_ext.rb
class Hash
  def only(*args)
    args = args.first if args.first.is_a?(Array)
    args.inject({}){|h, k| h.merge(k => self[k]) if self[k]}
  end
end


次に、このファイルをlib下に設置します。そして、config/enviroment.rbで、このファイルを読み込むようにします。

  • environment.rb
# Include your application configuration below
require 'hash_ext'


すると、このアプリケーション内では、Hash#onlyが使用できるようになります。また、Utilityクラスを置いておくこともできます。(本来このようなクラスはmodelディレクトリに方がいいらしい)

  • string_util.rb
class StringUtil
  def self.separate_by_delimiter(str, delimiter = ",")
    str.split(Regexp.new(delimiter))
  end
end


このファイルを置いておくと、アプリケーション内では、StringUtil.separate_by_delimiterが使えるようになります。

このlib使っている時にはまった

上のことは分かっていたつもりなのに分かってなかった。
モデルに評価機能をつけるための(ブログ拍手のような)、Evaluatableというモジュールを書きました。あと、そのアクション用に、EvaluatableActionというのも書いた。
で、以下の構成でlibに入れた。

  • lib/
    • evaluate.rb
    • evaluate/
      • evaluatable.rb
      • evaluatable_action.rb


evaluate.rbは必要なファイルをrequireしているだけです。

require 'evaluate/evaluatable'
require 'evaluate/evaluatable_action'


で、こんな感じで使えるかと思った。

  • article.rb
class Article < ActiveRecord::Base
  include Evaluatable
end


でも、見事にエラー。

NameError: uninitialized constant Evaluatable


なんでだろう。libはロードパス通っているから、そこにあるevaluate.rb内でrequireされているEvaluatableは参照できるんではないのか?


全然違った。というか、そもそもロードパスって言葉をきちんと理解してなかった。
ロードパスってのは、ファイル検索の対象になるディレクトリのパスのこと。つまり、libに置いてあるファイルでrequireしているから云々ってのは全くトンチンカン。ロードパスの通ったディレクトリにファイルを置いてあるってことは、そのファイルがどこからでもrequireできるってだけ。
つまり、さっきのエラーは以下のようにすることによって解決できる。

  • article.rb
class Article < ActiveRecord::Base
  require "evaluate"
  include Evaluatable
end


evaluatable.rbはevaluate.rb内でrequireされているから、evaluate.rbを読み込めばOK。というか、evaluatable.rbはロードパスの通ったディレクトリにはないので、requireできない。
でも、これだとEvaluatableモジュールを使うところでいちいちrequireしないといけない。それを回避する方法として、最初に示したように、environment.rb内でrequireしておく。

  • environment.rb
# Include your application configuration below
require 'evaluate'


これでおk。


む。じゃあ、なんで、StringUtilはどこでもrequireしてないのに使えるんだ??


そう。ここを忘れていたから、「libはロードパス通っているから、そこにあるevaluate.rb内でrequireされているEvaluatableは参照できるんではないのか?」という勘違いが起こった。
これは、ActiveSupportのオートロード機能のおかげ。ActiveSupportは、StringUtilというクラスを見みて、それが定義されていないクラスだと知ると、定義されているファイルを、ロードパスの通ったディレクトリに探しにいく。この時の、規約が、ファイル名が「string_util.rb」になっていること。


この機能によって、StringUtilはrequireしなくても使えるのでした。
なんか、超基本を忘れていてショックだった。