自動読み込みの仕組み
参考:
定数の自動読み込みと再読み込み | Rails ガイド [公式]
Rails の自動読み込みを支える技術 – SlideShare
クラスやモジュールの定義・オープンクラス
未定義のクラス・モジュールを定義 → 自動読み込みはトリガーされない
自動読み込みをしたクラスに対してオープンクラスを行うためには、class_eval
や module_eval
を使用する。
MyEngine::Engine.class_eval do config.paths.add "dev", load_path: true end MyEngine::SomeFeatures.module_eval do def hoge_method; end end
参考:
class および module キーワードの後に置かれる定数 | Rails ガイド [公式]
Reopening vs Overwriting a Class – Stack Overflow
トップレベルの定数
定義済みでない場合、自動読み込みがトリガーされる。
autoload_paths
を順に探索して、命名規則に合致するファイルを探す。
名前空間における探索
絶対的な名前空間で修飾していない参照 (相対参照) で定数を参照した場合
module Admin class BaseController < ApplicationController p Module.nesting @@all_roles = Role.all end end
定数 Role
の探索は、定数 Role
が参照された位置における Module.nesting
に従って探索される。
[Admin::BaseController, Admin]
ネストされた名前空間で見つからなかった場合、ネストした最初のモジュールの祖先モジュールについて定数が定義されているかどうかが探索される。Module.nesting
が空の場合には Object
に定義された定数 (トップレベルの定数) が探索される。
参考:
ネストしたモジュールを書く場合の注意点 | ninoseki’s blog
How You Nest Modules Matters in Ruby | theScore Tech Blog
修飾済み定数の解決
"NameSpace::MyConstant".underscore
によって、NameSpace::MyConstant
が定義されていると期待されているファイルへの相対パスを返す。(拡張子は含まない)
"NameSpace::MyConstant".underscore # => "name_space/my_constant"
autoload_paths
に含まれる各パスで、.rb
ファイルもしくは自動モジュールとなるディレクトリが探索される。
NameSpace.ancestors
に含まれるモジュールに定数が定義されていると仮定して .rb
ファイルもしくは自動モジュールが探索される。(Ruby 2.5 以降では祖先モジュールに含まれる途中の Object
は飛ばされるため、トップレベルの定数は検出されない。)
参考:
修飾済み定数を解決するアルゴリズム | Rails ガイド [公式]
ヒューリスティック
見つからない定数が、そのクラスまたはモジュールの親の名前空間にも存在しない場合、Rails はこの定数を相対参照であると仮定する。そうでない場合は修飾済み参照であると仮定する。
# qux.rb Qux = "I'm at the root!" # foo.rb module Foo end # foo/qux.rb module Foo Qux = "I'm in Foo!" end # foo/bar.rb module Foo class Bar def self.print_qux puts Qux end end end
Foo::Bar.print_qux
は何を出力するか?
通常の Ruby で読み込みを実行した場合
# Using normal Ruby loading > require "qux" > require "foo" > require "foo/qux" > require "foo/bar" > Foo::Bar.print_qux I'm at the root!
トップレベルの定数 Qux
が使われる。
Rails の自動読み込みの元で実行した場合
# Using Rails autoloading > Foo::Bar.print_qux I'm in Foo!
Foo
モジュールの定数 Qux
が使われる。
参考:
Rails autoloading – how it works, and when it doesn’t | Simon Coffey
concerns
参考:
lib ディレクトリの扱い (add_lib_to_load_path!)
参考:
add_lib_to_load_path! – rails/application.rb at v5.2.1 · rails/rails – GitHub
const_missing
参考:
require せずに勝手に読み込まれるとでも思ってるの? – Qiita
Rails の開発環境の自動再読み込みはどうやってるのか | Rubyリファレンス日記
How rails reloads your source code in development mode? | Codemancer
const_missing – rails/dependencies.rb at v5.2.1 · rails/rails – GitHub
ModuleConstMissing – rails/dependencies.rb at v5.2.1 · rails/rails – GitHub
load_missing_constant – rails/dependencies.rb at v5.2.1 · rails/rails – GitHub
const_missing (Module) – Rubyリファレンス
instance method Module#const_missing (Ruby 2.5.0)
ActiveSupport::Reloader
参考:
Rails の Reloader の仕組み | AnyType
リロード時に特定の処理を実行する | Oh My Enter!
ActiveSupport::Reloader | Ruby on Rails API [Official]
Watching files during Rails development | Robert Mosolgo
rails/reloader.rb at v5.2.2 · rails/rails – GitHub
rails/finisher.rb at v5.2.2 · rails/rails – GitHub
ActionDispatch::Reloader
参考:
Rails の Reloader の仕組み | AnyType
Rails Reloader: A Lesser Known Railtie Hook | Brandon Hilkert
ActionDispatch::Reloader | Ruby on Rails API [Official]
rails/reloader.rb at v5.2.2 · rails/rails – GitHub
Remove deprecated callbacks from ActionDispatch middlewares · rails/rails@3f2b7d6 – GitHub
コンソールで定数を再読み込みする
> reload!
参考:
console で reload! する場合の注意点 – Qiita
reload! – Rails::ConsoleMethods | Ruby on Rails API [Official]
rails/app.rb at v5.2.3 · rails/rails – GitHub
reload!
定数の再読み込みを実行する。
ActiveSupport::Reloader.reload!
参考:
console で reload! する場合の注意点 – Qiita
reload! – ActiveSupport::Reloader | Ruby on Rails API [Official]
Reload Class Cache on Demand – Stack Overflow
Why does code need to be reloaded in Rails 3? – Stack Overflow
reload-code
参考:
Class: Pry::Command::ReloadCode — Documentation for pry | RubyDoc.info
pry/reload_code.rb at v0.12.2 · pry/pry – GitHub
toplevel constant X referenced by A::X ワーニング
Ruby 2.5 より前のバージョンでは、
String::Hash
はHash
と評価されてインタプリタが「toplevel constant Hash referenced by String::Hash
」というwarning
を出力します。Ruby 2.5 以降ではObject
がスキップされるため、String::Hash
でNameError
がraise
されます。
参考: