Пара основных вещей, о которых вы, наверное, уже знаете:
Ruby интерпретируется, а не компилируется, поэтому вы не можете выполнить любой код, который не был замечен интерпретатором.
require
просто вставляет код из файла в эту точку программы, иными словами, require
в верхней части программы будет интерпретироваться перед require
внизу.
(Примечание: отредактировано для учета поведения операторов require) Так что если вы должны были сделать: ruby a.rb
это то, что интерпретатор ruby будет видеть и выполнять:
#load file b.rb <- from require './b.rb' in 'a.rb' file
#load file a.rb <- from require './a.rb' in 'b.rb' file
#this runs because a.rb has not yet been required
#second attempt to load b.rb but is ignored <- from require './b.rb' in 'a.rb' file
#finish loading the rest of a.rb
module A
def self.do_something
puts 'doing..'
end
end
#finish loading the rest of b.rb
module B
def self.calling
::A.do_something
end
end
B.calling
#Works because everything is defined
Если вместо этого вы сначала наберете b, ruby b.rb
, переводчик увидит:
#load file a.rb <- from require './a.rb' in 'b.rb' file
#load file b.rb <- from require './b.rb' in 'a.rb' file
#this runs because b.rb has not yet been required
#second attempt to load a.rb but is ignored <- from require './a.rb' in 'b.rb' file
#finish loading the rest of b.rb
module B
def self.calling
::A.do_something
end
end
B.calling #NameError, ::A.do_something hasn't been defined yet.
Надеюсь, это объясняет хорошие ответы, которые дали вам другие, и если вы думаете,об этом, почему трудно ответить на ваш последний вопрос о том, где поставить требуемые заявления.В Ruby вам требуются файлы , а не модули, поэтому от того, где вы поместите требование в свой код, зависит, как организованы ваши файлы.
Если вам абсолютно необходимо иметь возможность иметьОпределенные модули и методы выполняются в случайном порядке, затем вы можете реализовать что-то вроде этого, чтобы собирать вызовы для модулей, которые еще не существуют, и затем вызывать их, когда они появляются.
module Delay
@@q = {}
def self.call_mod(*args) #args format is method_name, mod_name, *args
mod_name = args.shift
method_name = args.shift
#remaining args are still in args
mod = Object.const_get(mod_name.to_sym)
mod.send(method_name.to_sym, *args)
end
def self.exec(mod_name, *args)
begin
args.unshift(mod_name)
self.call_mod(*args)
rescue NameError, NoMethodError
@@q[mod_name] ||= []
@@q[mod_name] << args
end
end
def self.included(mod)
#get queued methods
q_list = @@q[mod.name.to_sym]
return unless q_list
#execute delayed methods now that module exists
q_list.each do |args|
self.call_mod(*args)
end
end
end
Обязательносначала определите модуль задержки, а затем вместо вызова B.calling
вы будете использовать Delay.exec(:B, :calling, any_other_args)
.Так что если у вас есть это после модуля задержки:
Delay.exec(:B, :calling) #Module B is not defined
module B
def self.calling
::A.do_something
end
include Delay #must be *after* any callable method defs
end
module A
def self.do_something
puts 'doing..'
end
include Delay #must be *after* any callable method defs
end
Результат:
#=> doing..
Последний шаг - разбить код на файлы.Один из подходов может состоять в том, чтобы иметь три файла
delay.rb #holds just Delay module
a.rb #holds the A module and any calls to other modules
b.rb #holds the B module and any calls to other modules
Пока вы убедитесь, что require 'delay'
- это первая строка файлов модуля (a.rb и b.rb) и Задержка включена в концемодуль, все должно работать.
Заключительное примечание: эта реализация имеет смысл, только если вы не можете отделить свой код определения от вызовов выполнения модуля.