Ruby Module NameError - порядок имеет значение? - PullRequest
3 голосов
/ 17 ноября 2011

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 выдается ошибка NameEr - он не может найти M2.

Однако, , когда я перемещаю M2 к вершине M1 , ссылка разрешается без проблем. Так работает Ruby - интерпретатор не делает первый проход, чтобы получить все допустимые имена в области видимости? Вы можете объяснить это поведение?

Ответы [ 2 ]

5 голосов
/ 18 ноября 2011

Причина такого поведения заключается в том, что файлы 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 и пытается найти нужный файл, но это не так, как здесь).«Определение» отношения сохраняется, и только при последующем обращении к нему части будут соединяться вместе.

Модули в этом отношении абсолютно одинаковы.

1 голос
/ 18 ноября 2011

Да, вы правы. Когда Ruby интерпретирует код и не знает константу, возникает ошибка имени:

class ModuleA
  include ModuleB
end

module ModuleB
end

Однако, если код не запустится, он не будет повышен:

def some_method
  include ModuleC
end

module ModuleC
end

some_method
...