2009年8月22日土曜日

[Rails][CodeReading] Rails::Initializer (3) set_load_path

Initializer.process で3つめに呼ばれる set_load_path メソッド。名前から、重要メソッドと推測できます。

def set_load_path
load_paths = configuration.load_paths + configuration.framework_paths
load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
$LOAD_PATH.uniq!
end

Rails::Configuration.load_paths と Rails::Configuration.framework_paths を、Ruby組み込み変数 $LOAD_PATH に追加しています。

Configuration.load_paths, はConfigurationのインスタンス変数で、 Configuration初期化時にデフォルトにセットされます。

def initialize
~~ 略 ~~
self.load_paths = default_load_paths
~~ 略 ~~
end


def default_load_paths
paths = []

# Add the old mock paths only if the directories exists
paths.concat(Dir["#{root_path}/test/mocks/#{environment}"]) if File.exists?("#{root_path}/test/mocks/#{environment}")

# Add the app's controller directory
paths.concat(Dir["#{root_path}/app/controllers/"])

# Followed by the standard includes.
paths.concat %w(
app
app/metal
app/models
app/controllers
app/helpers
app/services
lib
vendor
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }

paths.concat builtin_directories
end

def builtin_directories
# Include builtins only in the development environment.
(environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : []
end


ここで、アプリの app/ 以下がロードパスに追加されています。
# app/controllers が 重複して追加されてる??
また、development環境の場合は、最後に rails/builtin がパスに追加されます。

processメソッドにブロックが与えられた場合(config/environment.rbから呼ばれたとき)は、load_path が environment.rb で指定した値で上書きされているはずです。

Configuration.framework_paths のほうは、変数ではなくメソッドになっています。

def framework_paths
paths = %w(railties railties/lib activesupport/lib)
paths << 'actionpack/lib' if frameworks.include?(:action_controller) || frameworks.include?(:action_view)

[:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework|
paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include?(framework)
end

paths.map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
end

ここで railties, activesupport, actionpack, activerecord, actionmailer, activeresource, actionwebservice と、すべてのRailsフレームワークがロードパスに追加されます。frameworks変数でロードするモジュールをフィルタしているので、そのデフォルト値をみてみると

def default_frameworks
[ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
end

となっています。

なお、environment.rb で config.frameworks を編集して、

config.frameworks -= [ :active_record ]

たとえばこのように activerecordモジュールを frameworks から除いても、activerecord がロードパスから外れることはありません。config/boot.rb 内で、一度デフォルトのConfigurationで set_load_path が実行され、そのときにロードパスに追加されるためです。
(ただし、ロードはされません。当然か ^^;)

↑の設定で script/console を実行すると、
>> p ActiveRecord  # 例外発生
NameError: uninitialized constant ActiveRecord
from /usr/lib/ruby/ruby-1.9.1/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:443:in `load_missing_constant'
from /usr/lib/ruby/ruby-1.9.1/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:80:in `const_missing_with_dependencies'
from /usr/lib/ruby/ruby-1.9.1/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:92:in `const_missing'
from (irb):1
from /usr/lib/ruby/ruby-1.9.1/bin/irb:12:in `<main>'
>> require 'active_record' # activerecord をロード(成功)
=> ["ActiveRecord"]
>> p ActiveRecord # 今度は見つかる
ActiveRecord
=> ActiveRecord


という状態。


set_load_path で $LOAD_PATH にパスを追加するときのコードを見ると、Railsでのパスの検索順がみえてきます。

load_paths = configuration.load_paths + configuration.framework_paths
load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }

reverse して $LOAD_PATH に unshift しているので、最初に検索されるのはアプリの app ディレクトリ(のはず)。

[Rails][CodeReading] Railsの初期化コードを読む (イントロ&目次)

0 件のコメント:

コメントを投稿