Rails Guide を見てみますが、いまいちピンとこないのでソースを見てみました。 http://railsguides.jp/autoloading_and_reloading_constants.html#require-dependency
先にまとめ
require_dependency とは、production環境では require
し
development環境では Kernel.load
する。
Kernel.load
は呼ばれるたびにロードされるので、開発時には効率が良い。
通常は、autoload でことが足りそうな気がしますが、
何かautload_pathsに含まれないものをrequireする際には使うと良いでしょう。
ソース見てみるよー
require_dependency
activesupport-5.0.0.1/lib/active_support/dependencies.rb
def require_dependency(file_name, message = "No such file to load -- %s") file_name = file_name.to_path if file_name.respond_to?(:to_path) unless file_name.is_a?(String) raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}" end Dependencies.depend_on(file_name, message) end
depend_on を呼んでます。
depend_on
def depend_on(file_name, message = "No such file to load -- %s.rb") path = search_for_file(file_name) require_or_load(path || file_name) rescue LoadError => load_error if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1] load_error.message.replace(message % file_name) load_error.copy_blame!(load_error) end raise end
require_or_load を呼んでいます。
別件ですが、なんかdefaultのmessageが呼び出し元と若干違いますね。
少し違和感があります(後でもう少し見てみるかも)。
require_or_load 前半
少し長いので分割。
def require_or_load(file_name, const_path = nil) file_name = $` if file_name =~ /\.rb\z/ expanded = File.expand_path(file_name) return if loaded.include?(expanded) Dependencies.load_interlock do # Maybe it got loaded while we were waiting for our lock: return if loaded.include?(expanded) # Record that we've seen this file *before* loading it to avoid an # infinite loop with mutual dependencies. loaded << expanded loading << expanded
ActiveSupport::Concurrency::ShareLock を使って安全にロックを掛けつつ
loaded
に読み込み済みのリスト(Set)を保持しています。
こんな感じ。
#<Set: {"/Users/rochefort/work/rails5_example/app/controllers/articles_controller", "/Users/rochefort/work/rails5_example/app/controllers/application_controller", "/Users/rochefort/work/rails5_example/app/helpers/application_helper", "/Users/rochefort/work/rails5_example/app/helpers/tags_helper", "/Users/rochefort/work/rails5_example/app/helpers/user_sessions_helper", "/Users/rochefort/work/rails5_example/app/helpers/users_helper", "/Users/rochefort/work/rails5_example/app/helpers/articles_helper", "/Users/rochefort/work/rails5_example/app/models/article", "/Users/rochefort/work/rails5_example/app/models/application_record", "/Users/rochefort/work/rails5_example/app/uploaders/image_uploader"}>
require_or_load 後半
begin if load? # Enable warnings if this file has not been loaded before and # warnings_on_first_load is set. load_args = ["#{file_name}.rb"] load_args << const_path unless const_path.nil? if !warnings_on_first_load or history.include?(expanded) result = load_file(*load_args) else enable_warnings { result = load_file(*load_args) } end else result = require file_name end
load? がtrueの場合は、load_file を呼んでいますが、falseの場合は require しているだけのようです。
load_file
まずは load_file を見てみると Kernel.load しています。
def load_file(path, const_paths = loadable_constants_for_path(path)) const_paths = [const_paths].compact unless const_paths.is_a? Array parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object } result = nil newly_defined_paths = new_constants_in(*parent_paths) do result = Kernel.load path end autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) autoloaded_constants.uniq! result end
load?
続いてload?
def load? mechanism == :load end
mechanism という値を参照しているだけです。
mechanism
こいつはどこから来るのか?
mattr_accessor :mechanism self.mechanism = ENV['NO_RELOAD'] ? :require : :load
一見、環境変数で出しわけているだけのようですが
おそらくconfigのファイルで出しわけてそうなので、config/production.rb を見てみます。
しかし、直接 mechanism を設定している箇所はありません。
ですが、以下が怪しそうです。
config.cache_classes = true
railties
railties-5.0.0.1/lib/rails/application/bootstrap.rb
module Rails class Application module Bootstrap # Sets the dependency loading mechanism. initializer :initialize_dependency_mechanism, group: :all do ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load end
ありました!
cach_classes の値を見て require / load を設定していました。