2012-01-23 172 views
5

我有一个外部文件中的外部文件: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{...}对其进行评估,并includeB这门课吗?

+0

到底是什么?为什么你不能直接使用A类?你是否从模块化中获得了一些好处? 'load'和'require'实际上不会将一个类加载到模块中,它们只是加载源代码,因此您的类将按照它们在文件中的定义。不知道你为什么想这样做? – brad

+1

@brad由于这些外部文件要由用户编写,并且可以任意命名。如果我在主环境中定义这些类,它们会弄乱命名空间。 – sawa

+0

小心通过'ObjectSpace#each_object'篡改其他名称空间。 – Reactormonk

回答

4

你不能。至少使用loadrequire,Ruby文件将始终在顶级上下文中进行评估。

您可以解决这个问题有两种方法:

  • 直接定义class B::A(但你可能想避免)
  • 使用eval(File.read("path_to_external_file.rb"))B类中

编辑:也许这个对你有帮助:https://github.com/dreamcat4/script/blob/master/intro.txt

3

通常,将一个类定义为“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 

你看?如果你想要一个“保证唯一”的名字空间,你可以使用匿名模块。您可以将这些模块存储在散列或其他数据结构中,并根据需要将这些模块拉出。这解决了您提到的问题,即您的应用的用户将添加他们自己的类,并且您不希望名称发生冲突。