prefer:http://urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/
参考这篇文章,总结的已经很好了,有改动和补充。
Ruby Constant Lookup(注意这里是Ruby的)
Constant lookup in Ruby is reasonably simple, once you know the rules, but it isn’t always totally intuitive. When you refer to a constant in a given lexical scope, that constant is searched for in:
- Each entry in
Module.nesting
- Each entry in
Module.nesting.first.ancestors
- Each entry in
Object.ancestors
ifModule.nesting.first
is nil or a module.
关于Module.nesting
可以参考文档:http://ruby-doc.org/core-2.1.2/Module.html#method-c-nesting
翻译一下:
虽然汉语并不是一个精确的语言,但意境和直觉的表达,不论松本先生还是哪个程序猿,都是一样的。
1. 从当前作用域开始,逐层向上,寻找直接可视的目标
2. 从当前作用域的祖先们里寻找目标(2比1优先级低为什么?因为这点不符合第一直接可视要求,想想看很多的include进来的东西,哪里能看见呢!谁要是这么写程序,就是程序员公敌)
3. 寻找顶层作用域内的目标(这也符合直觉,因为顶层作用域是公用的,放最后也符合习惯;)
大大的缺陷
缺陷在第二步,从当前作用域的祖先们里寻找目标。
对一个程序员来说,最好的是能准确的写出自己用哪个名字空间内的哪个目标。
这个第二步中的默认规则显然违背了这个原理,因为如果我要用祖先的作用域内的目标,我可以写明使用这个目标,即多写几个字母,把目标的命名空间写上。
举例来说:
找A::B::C
就只应该找1. A::B空间内的C,或者3. 唯一默认空间内的::C
而不应该去找A::C,因为如果程序员想用A::C,则需要写明,只需要多写几个字母。
这个缺陷将会导致什么问题呢?
不明确的东西,就要有一个统一的默认值。而如果默认的寻找路径是多个,就必须保证不能交叉。否则就麻烦了。
找A::B::C,结果可能找到A::C和::C,这可不是我们愿意的,一旦程序大了,谁去理清楚这个玩意儿。最后还不是得程序员埋单,要么还得写清楚,要么就报错搞死人。
期待ruby后面改掉这个糟粕呀!!
测试代码改了下:
C = "At the top level" module A C = "In A" end module A module Y #none end end module A module B include A::Y module X end puts "----B----" puts "Module.nesting=#{Module.nesting.inspect}" # => [A::B, A] puts "Module.nesting.first=#{Module.nesting.first}" puts "Module.nesting.first.ancestors=#{Module.nesting.first.ancestors.inspect}" puts "Object.ancestors=#{Object.ancestors}" puts C # => "In A" puts "----B----" end end module A::B puts Module.nesting # => [A::B] puts C # => "At the top level" end
你要是能写对,可真不错。理解了这个,下面的问题才能更好理解。
----B----
Module.nesting=[A::B, A]
Module.nesting.first=A::B
Module.nesting.first.ancestors=[A::B, A::Y]
Object.ancestors=[Object, Kernel, BasicObject]
In A
----B----
A::B
At the top level
Process finished with exit code 0
Ruby's Autoload
http://fantaxy025025.iteye.com/blog/2098356 因为篇幅,拎出去写了分析,这里。
Rails Constant Autoloading
实现原理
使用了钩子方法:Module#const_missing
查找和加载步骤:
1. 使用ruby加载。
2. 如果报错,则用rails机制寻找并加载文件:MyModule::SomeClass # => my_module/some_class.rb
rails是跑在ruby上的。并没有修改ruby的寻找机制,只是出错了才处理加载,去加载常量对应的文件。
上文分析了ruby寻找常量时,在命名空间上一层一层的找法的缺陷。
rails也遵循了ruby的寻找方法,区别在于,rails会去加载对应的文件。
When Foo::Bar::Baz
is referred to, then, Rails will attempt to load the following constants in turn, until it finds one that is already loaded:
Foo::Bar::Baz | 对应autoload_paths/foo/bar/baz.rb
-
Foo::Baz
| 对应autoload_paths/foo/baz.rb -
Baz
| 对应autoload_paths/baz.rb
这样也会有加载的文件产生交叉的问题。
As soon as an already-loaded constant Baz
is encountered, Rails knows this cannot be the Baz
it is looking for, and the algorithm raises a NameError
.
这个是rails的机制,原因是什么?
因为ruby找不到的常量,rails只认为是因为没有加载对应的文件,其实这也是rails做的唯一工作。
而如果已经存在一个这样的常量了,而ruby找不到,那么rails也不能做太多的工作。
rails为什么不去掉中间这种不好的加载方法呢?
是可以去掉的。但很多人写的ruby代码,使用了2这种默认机制,如果去掉,就会导致自动加载不能用了。为了兼顾ruby,只能这样了。
Rails 自动加载的缺陷
# 同ruby一样,第二种加载容易导致交叉。
详细见上面分析。
# 依赖加载顺序
因为找不到相应的常量时,就会去找相应的文件。运行时,执行的不同顺序,会导致加载文件的顺序发生变化。这样可能引发很多问题,比如不同文件定义的常量同名了,但运行到不同的分支导致加载了不同的常量,这样的bug很难排查。
# 上下文信息丢失
ruby中的常量有多重,class, module, 字面常量比如Cache="MEM"这种。rails只收到const_missing,并没有收到上下文信息。
比如const_missing接收到Foo::Bar::Baz 这个常量有很多种表现形式,比如上面的简单的就3种了,还有不少变种,比如下面两种module的写法就是不同的,更别提module和class了。
module Foo module Bar #sth end end
module Foo::Bar #sth end end
class Foo class Bar #sth end end
rails接收不到上下文信息,所以导致rails只认为是第一种方式。
理解了文章的一些内容,再看 http://urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/
这个文章就好理解了。很多东西还是有其内在的原因的,死记只能解一时只用。
+
+
+
——
+
+
+
相关推荐
2. **lib目录**:存放自定义的Ruby模块和类,如连接Solr的适配器、Solr配置文件解析工具等。 3. **app/models/solr_document.rb**:定义了Solr文档对象,用于映射Rails模型到Solr索引。 4. **config目录**:配置文件...
2. Rails的控制器和视图:Rails端可能有修改过的控制器,用于在响应中返回CSRF令牌,或者更新后的视图,用于在页面加载时将令牌传递给Angular。 3. 配置文件:可能包括Rails的`config/application.rb`或`config/...
在标题和描述中提到的“举例理解Ruby on Rails的页面缓存机制”,主要是指通过具体的例子来阐述如何在实际项目中运用页面缓存。例如,IBM 官方技术文档中的内容可能包含以下示例: 1. **启用页面缓存**:在 Rails ...
### Ruby on Rails 指南 v5.0.1 中文版 #### Rails入门 - **前提条件**:为了能够顺利地开始Rails的学习之旅,读者需要具备一定的Ruby语言基础,并且对Web开发有一定的了解。 - **Rails是什么?**:Rails是一种...
Rails的插件定位和加载机制是可以扩展的,开发者可以创建自定义的定位器和加载器以适应特定的需求。 安装Rails插件通常使用内置的`script/plugin`工具,它支持多种命令如`discover`、`source`、`unsource`、`...
Ruby 2.3.1是该语言的一个稳定版本,它提供了许多性能优化和新特性,例如:语法糖的增强、新的垃圾回收机制以及对大型数据处理的支持,使得Rails应用的运行更为流畅。 2. **Passenger 5**: Passenger(又名Phusion ...
Rails,全称Ruby on Rails,是一个基于Ruby语言的开源Web应用程序框架,遵循MVC(模型-视图-控制器)架构模式。它以其简洁、高效和开发速度闻名,深受开发者喜爱。Rails最佳实践是提升代码质量和可维护性的关键,...
本压缩包"Rails确认替换为SweetAlert_Ruby_HTML_下载.zip"可能包含了一个名为"sweet-alert-rails-confirm-master"的项目,该项目可能是对Rails中默认确认机制的替换,用SweetAlert来实现确认对话框。接下来,我们将...
标题“api_rails_grape_1”暗示了我们即将探讨的是关于使用Ruby构建API的专题,特别是关于Rails框架中的Grape库。Rails是Ruby社区广泛使用的Web开发框架,而Grape是一个轻量级且模块化的库,用于创建RESTful API。 ...
在本书中,作者详细介绍了Ruby的基础知识,包括变量、常量、方法、类、模块等核心概念,以及Ruby的元编程特性,如对象的反射和自定义类加载机制。这些内容对于理解Rails框架的工作原理至关重要,因为Rails充分利用了...
在Ruby开发领域,Web开发框架Rails(Ruby on Rails)因其高效和简洁的代码风格而备受开发者喜爱。近年来,随着前端技术的发展,JavaScript库如React在构建用户界面方面展现了强大潜力。本示例将探讨如何在Rails应用...
- **Routing**:Rails 使用了一种基于正则表达式的路由机制,可以根据 URL 自动映射到对应的控制器方法。 - **Middleware**:中间件是 Rails 应用的一个重要组成部分,它们位于 HTTP 请求和响应的处理流程中,可以...
《Pro Active Record Databases with Ruby and Rails》一书由Kevin Marshall、Chad Pytel和Jon Yurek共同撰写,旨在帮助开发者掌握如何使用Ruby及Ruby on Rails(简称Rails)框架进行高级数据库应用开发的技术。...
首先,Ruby 2.2.1 是一个稳定版本的 Ruby 语言,引入了多项性能优化和语法改进,如细化常量查找、元编程的改进以及更好的垃圾回收机制。这些改进使得代码运行更高效,同时也提高了开发者的生产力。 Rails 4.2.6 是...
Rails是Ruby on Rails框架的一个重要组成部分,它是一个用于构建Web应用程序的强大工具。在这个场景中,我们关注的是Rails的自动完成、文件上传、分页以及上传进度管理相关的插件。让我们详细了解一下这些关键知识点...
4. **强大的社区支持**:由于 Ruby 和 Rails 的流行度不断提升,围绕这两种技术的社区也日益壮大,这为开发者提供了丰富的资源和解决方案,有助于解决开发过程中遇到的问题。 #### 三、Ruby 语言简介 **Ruby** ...
《Pro Active Record: Databases with Ruby and Rails》是一本由Kevin Marshall、Chad Pytel和Jon Yurek共同编写的关于Ruby on Rails(简称Rails)框架下的Active Record模式使用的专业指南。该书于2007年出版,提供...
12. **Asset Pipeline**:Rails的静态资源管理机制,允许合并、压缩JavaScript和CSS文件,提高页面加载速度。 13. **Railties**:Rails的核心组件,提供了初始化、命令行工具和插件系统等功能,使得Rails能够与其他...
Ruby on Rails是一款强大的Web开发框架,它以简洁和生产力为导向,深受开发者喜爱。在开发过程中,调试是必不可少的一部分,为了帮助开发者更有效地定位问题,Rails社区创建了多种辅助工具,其中之一便是Rails ...
Ruby Eclipse 插件是专为在Eclipse集成开发环境中(IDE)进行Ruby和Ruby on Rails开发设计的一款扩展工具。这款插件将Eclipse强大的功能与Ruby语言的灵活性结合在一起,为开发者提供了一站式的开发体验。 首先,让...