Все или ничего не требуется в Ruby? - PullRequest
2 голосов
/ 11 июля 2009

Существует ли версия require в ruby, которая либо загружает весь файл, либо вообще ничего?

Проблема заключается в том, что require начинает загрузку сверху, и, если возникают проблемы, вы, например, получаете незавершенные определения, следующее все равно будет загружать class A, даже если module C не определено:

class B
  include C
end

В моем конкретном случае у меня есть большой набор взаимозависимых файлов и загрузчик, который загружает эти файлы. В качестве примера я приведу просто набор файлов из 4 файлов (a.rb, b.rb, c.rb и w.rb). Ниже приведен список этих файлов:

# In file a.rb
class A
  @foo = []
  @foo.push("in A")

  def self.inherited(subclass)
    foo = @foo.dup
    subclass.instance_eval do
      @foo = foo
    end
  end

  def self.get_foo
    @foo
  end
end

# In file b.rb
class B < A
  include C # if C is not already defined, the following line will not get executed although B will be defined.
  @foo.push("in B")
end

# In file c.rb
module C
end

# In file w.rb
class W < B
  @foo.push("in W")
end

Загрузчик работает, получая список текущих файлов, пытаясь запросить их один за другим. Если какой-либо файл завершается ошибкой, он остается в списке и повторяется позже Код выглядит примерно так: (удалено много деталей для простоты)

# In file loader.rb
files = Dir["*.rb"].reject { |f| f =~ /loader/ }
files.sort! # just for the purpose of the example, to make them load in an order that causes the problem
files.reject! { |f| require(f) rescue nil } while files.size > 0

В конечном счете, я бы хотел, чтобы он загрузил A, затем обнаружил, что B не может быть загружен в одиночку (поэтому пропустите его), затем загрузите C, затем найдите, что W еще не может быть загружен (поэтому пропустите его), затем вернитесь к B то W.

В этом случае вывод p W.get_foo будет ["in A", "in B", "in W"], что я и хочу.

На самом деле происходит то, что он загружает A, затем частично загружает B, затем C, затем, когда дело доходит до W, он полагает, что может загрузить его (поскольку B уже определено). Это вызывает код self.inherited в неправильное время и копирует значение еще не готового @foo, давая неправильный вывод p W.get_foo, равный ["in A", "in W"].

Наличие "все или ничего" require решит это.

Есть идеи?

1 Ответ

5 голосов
/ 11 июля 2009

Если один файл зависит от другого, этот файл должен требовать самой зависимости. Например, b.rb должно выглядеть так:

require 'a'
require 'c'

class B < A
  include C # if C is not already defined, the following line will not get executed although B will be defined.
  @foo.push("in B")
end

и w.rb должны выглядеть так:

require 'b'

class W < B
  @foo.push("in W")
end

После этого порядок внешней загрузки больше не имеет значения, и «все или ничего» * ​​1010 * не требует подхода. Когда b загружен, он сначала увидит требование для a и поймет, что он уже загружен, затем потребуется c, потому что он понимает, что еще не загрузил его. Когда снова потребуется c, он пропустит его из внешнего цикла.

Примечание: будьте осторожны с $ LOAD_PATH и путями, переданными в require. Ruby распознает только дубликаты, если пути совпадают. Лучше использовать относительные пути (относительно пути в $ LOAD_PATH) вместо абсолютных путей; в противном случае файл может быть загружен дважды.

...