Загрузка внешних файлов внутри класса / модуля - PullRequest
6 голосов
/ 23 января 2012

У меня есть внешний файл: path_to_external_file.rb с определением класса:

class A
  some_definitions
end

И я хочу загрузить это в модуле B, чтобы класс A, определенный выше, мог называться B::A. Я попробовал:

class B
  load('path_to_external_file.rb')
end

но A определяется в основной среде, а не в B:

A #=> A
B.constants # => []

Как я могу загрузить внешние файлы в некоторый класс / модуль?

Редактировать Стоит ли читать внешние файлы как строки и оценивать их в Class.new{...}, а include в этом классе в B?

Ответы [ 2 ]

4 голосов
/ 23 января 2012

Вы не можете. По крайней мере, используя load или require, файлы Ruby всегда будут оцениваться в верхнем контексте.

Вы можете обойти эту проблему двумя способами:

  • Определите class B::A напрямую (но вы, вероятно, пытаетесь избежать этого)
  • Используйте eval(File.read("path_to_external_file.rb")) в вашем B классе

Редактировать: Может быть, эта библиотека вам интересна: https://github.com/dreamcat4/script/blob/master/intro.txt

3 голосов
/ 23 января 2012

Как правило, плохая идея определять класс как «класс A», но затем «волшебным образом» включать его в модуль B. Если вы хотите сослаться на класс A как B :: A, вы должны определить его, используя либо :

module B
  class A
    # contents
  end
end

или

class B::A
  # contents
end

В противном случае любой, кто читает ваш код, будет сбит с толку. В этом случае вы ничего не получите в ясности, краткости или удобстве, используя «хитрости», так что простой код лучше. Здесь есть урок: возможности метапрограммирования в Ruby великолепны, но нет необходимости использовать их безвозмездно. Используйте их только тогда, когда вы действительно что-то получите. В противном случае вы просто усложняете понимание кода.

НО, прочитав ваш комментарий, похоже, в вашем случае есть действительно веская причина сделать что-то подобное. Я полагаю, что следующее решение будет даже лучше, чем вы предполагаете:

m = Module.new
m.module_eval("class C; end")
m.constants
=> [:C]
m.const_get(:C)
=> #<Module:0xfd0da0>::C

Видишь? Если вам нужно «гарантированное уникальное» пространство имен, вы можете использовать анонимный модуль. Вы можете хранить эти модули в хэше или другой структуре данных и извлекать из них классы по мере необходимости. Это решает проблему, о которой вы упоминали, что пользователи вашего приложения будут добавлять свои собственные классы, и вы не хотите, чтобы имена конфликтовали.

...