libディレクトリの使い方とオートロード機能
この関連ではまったので、自分用にメモしておきます、
libディレクトリの使い方
railsのRAILS_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しなくても使えるのでした。
なんか、超基本を忘れていてショックだった。