Ruby Module NameError - 订单有关系吗?

时间:2011-11-17 19:40:21

标签: ruby-on-rails ruby module mixins

Ruby 1.9.2 p290和Rails 3.0.9

我有一个.rb文件,结构如下:

module M1
  # .... some method defs ...

  # Code in the middle, outside of any def:

  if Rails.version >= '3'
    class Railtie < ::Rails::Railtie

      ActiveSupport.on_load :action_controller do

        ActionController::Base.send :include, ::M1::M2 # <- throws an error..

      end
    end
  end

  module M2
  # ... method defs ...
  end
end

ActionController::Base.send :include, ::M1::M2抛出一个NameError - 它找不到M2。

但是,当我将M2移动到M1的顶部时,它会毫无问题地解析引用。这是Ruby的工作原理 - 解释器不会在第一遍中获取范围内的所有有效名称吗?你能解释一下这种行为吗?

2 个答案:

答案 0 :(得分:5)

这种行为的原因是Ruby文件是从上到下读取的。类的主体是可执行代码。因此,名称错误的简单原因是Ruby解释器还没有达到代码的那部分。

所以,这实际上是完全合法的Ruby代码:

class Foo

  puts "hello from inside a class"

end

类的定义只是另一种表达方式。并且,就像Ruby中的每个表达式一样,它具有返回值,因此以下工作:

two = class Foo

  def bar
  end

  1 + 1

end

当您使用替代语法创建类时,这一点变得更加明显:

Foo = Class.new do
  puts "Hello"
end

唯一的区别是,当你以这种方式编写时,你不会输入命名空间。

您已在ActiveRecord中看到此行为:

class Post < ActiveRecord::Base
  has_many :comments
end

此处,has_many只是ActiveRecord::Base上存在的方法调用。它将在加载文件时直接执行。这就是为什么has_many和其他关系的某些参数作为字符串传递的原因。

class Post < ActiveRecord::Base
   belongs_to :author, :class_name => "User"
end

如果您要提及课程User本身,则会引发NameError,因为加载User时可能无法加载Post。 (这在Rails中实际上并非如此,因为Rails拦截了NameErrors并试图找到所需的文件,但除此之外还有其他内容)。 &#39;定义&#39;存储关系,并且只有在稍后访问关系时,才会将这些部分组合在一起。

模块在这方面完全相同。

答案 1 :(得分:1)

是的,你是对的。当Ruby解释代码并且不知道常量时,它会引发名称错误:

class ModuleA
  include ModuleB
end

module ModuleB
end

但是,如果代码未运行,则不会引发:

def some_method
  include ModuleC
end

module ModuleC
end

some_method