`
fantaxy025025
  • 浏览: 1247586 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

ruby的加载机制和rails_自动加载机制_对比

 
阅读更多

 

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:

  1. Each entry in Module.nesting
  2. Each entry in Module.nesting.first.ancestors
  3. Each entry in Object.ancestors if Module.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/

这个文章就好理解了。很多东西还是有其内在的原因的,死记只能解一时只用。 

 

+

+

+

——

+

+

+

分享到:
评论

相关推荐

    trust:Rails的授权机制

    相信信任是用于Ruby On Rails的授权控制的重要框架。 为什么还要问另一个授权框架? 好吧,我们使用了了一段时间,但是在命名空间和继承方面陷入了困境。 因此,我们研究了使用和的可能性,发现CanCan可能很慢,因为...

    localeapp:发送和检索您的ruby i18n本地化版本到语言环境翻译服务

    localeapp gem将您的Rails应用程序连接到上的Locale服务。 语言环境使您无需手工编辑翻译文件。 gem挂接到i18n异常机制中,以将缺少的翻译内容发送到应用程序。 添加翻译内容后,它会自动下拉,因此您可以立即看到...

    基于MVC的JavaScript Web富应用开发

    作者 Alex MacCaw 是一名Ruby/JavaScript 程序员,在开源社区中很有名望,是Spine框架的作者,同时活跃在纽约、旧金山和柏林的各大 Ruby/Rails 论坛。除了作为一名工程师,他还喜欢带着他的尼康D90和冲浪板环游世界...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

     国内知名的高端IT技术作家,已出版《Spring 2.0宝典》、《基于J2EE的Ajax宝典》、《轻量级J2EE企业应用实战》、《Struts 2权威指南》、《Ruby On Rails敏捷开发最佳实践》等著作。 目录: 第0章 学习Java...

    nosql 入门教程

    15.3.1 Rails和NoSQL 247 15.3.2 Django和NoSQL 248 15.3.3 使用Spring Data 250 15.4 从RDBMS迁移到NoSQL 254 15.5 小结 254 第16章 性能调校 256 16.1 并行算法的目标 256 16.1.1 减少延迟的含义 256 ...

    java开源包1

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包11

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包2

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包3

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包6

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包5

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包10

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包4

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包8

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包7

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包9

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    java开源包101

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

    Java资源包01

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

Global site tag (gtag.js) - Google Analytics