Rails中autoload_paths目录下float.rb无法自动加载的问题
问题场景
你在Rails 5.1.5(Ruby 2.4.1)项目里创建了app/extensions目录,用来存放自定义异常和Ruby核心类的扩展文件,目前有exceptions.rb和float.rb两个文件。这个目录已经被加入ActiveSupport::Dependencies.autoload_paths(因为在app目录下会自动加载,同时你也在application.rb手动添加了config.autoload_paths << File.join(Rails.root, 'app', 'extensions/**'),所以路径列表里出现两次)。
但奇怪的是,exceptions.rb总能正常加载,float.rb却只有启用eager loading时才会生效。你怀疑是Spring的问题,已经把目录加入spring.rb的监听列表:
%w( .ruby-version .rbenv-vars tmp/restart.txt tmp/caching-dev.txt config/application.yml app/extensions ).each { |path| Spring.watch(path) }
多次重启Spring和服务器后问题依然存在。
你的float.rb内容是:
class Float def comma_sep self.to_s.gsub('.', ',') end end
在Rails控制台执行时出现错误:
irb> num = 29.1 irb> num.comma_sep NoMethodError: undefined method `comma_sep' for 29.1:Float from (irb):2
问题根源
这个问题的核心是Rails自动加载机制对Ruby核心类的扩展不生效:Rails的自动加载器只会在第一次引用某个常量时去加载对应的文件,但Float是Ruby核心类,在Rails启动初期就已经被加载完成了,所以自动加载器不会再去寻找并加载float.rb,只有开启eager loading时才会预加载所有指定路径下的文件,这时候float.rb才会被加载。
解决方案
这里有几个可行的解决办法,按推荐程度排序:
用初始化文件手动加载扩展
在config/initializers目录下创建一个新文件(比如load_core_extensions.rb),然后手动require你的扩展文件:# config/initializers/load_core_extensions.rb require Rails.root.join('app/extensions/float.rb')这样Rails启动时就会直接加载这个文件,不管是否开启eager loading,而且Spring重启时也会重新加载这个初始化文件。
用
require_dependency让Rails跟踪依赖
如果想更贴合Rails的依赖管理机制,可以在application.rb里添加一个初始化钩子,加载app/extensions下的所有扩展文件:# config/application.rb config.before_initialize do Dir.glob(Rails.root.join('app/extensions/*.rb')).each do |file| require_dependency file end endrequire_dependency会让Rails的依赖系统跟踪这些文件,当文件修改时,Spring会自动重新加载它们,比直接require更灵活。彻底清除Spring缓存
有时候Spring的缓存可能没有完全更新,你可以执行以下命令彻底停止Spring,再重启服务器:spring stop rails server做完这一步再测试控制台,看看问题是否解决。
验证方法
修改完成后,重启Rails服务器,打开控制台测试:
irb> num = 29.1 irb> num.comma_sep => "29,1"
如果返回这个结果,说明扩展已经成功加载了。
内容的提问来源于stack exchange,提问作者mityakoval

