關於 Ruby 的 autoload 與 Rails 的 autoload_paths 以及 reopen module / class

最近在實作一個特別的需求,做了一個 gem 搞這種事:

然後就搞了三天搞不定。

具體的現象是:

之後試了繼承(很難搞)和 module ,最後是用 ActiveSupport::Concern 包了 module ,把 association 之類的東西寫在 included do 裡面,解決。

今天讀到這篇文章 Rails autoloading — how it works, and when it doesn’t ,對於 Ruby 和 Rails 的 “autoload” 有粗略的瞭解了。簡單整理如下:

所以:

這也就是為什麼會有「在 gem 和在 app 裡面,同名的 model class 是 mutually-exclusive,除非手動 require 才能改寫其內容」。也就是說,想要在 gem 裡面定義一個 model ,然後在 rails app 裡面 reopen 它,是不可能的,必須要手動載入它的 reopening。

說得更 general 一點就是:如果該 class / module 已經在 Gem 裡面載入,則要在 Rails 裡面 reopen 它,就必須放在 autoload_paths 以外的地方,並且手動 require 之。


該文很推薦一讀,除了詳細說明了 Ruby 和 Rails 的 autloading 機制,還提到一些陷阱,例如說 Rails 的 autoloading 其實不會理 Module.nesting (lexical context of current line) ,這樣子某些情況下會變成「第一次可以成功 autoload ,但第二次卻說 NameError 找不到 const」這種問題。