最好用一个例子来解释:
file1.rb:
def foo
puts 123
end
file2.rb:
class A
require 'file1'
end
A.new.foo
将出现错误“':私有方法'foo'调用”。
我可以通过A.new.send("foo")
解决这个问题,但有没有办法让导入的方法公开?
编辑:为了澄清,我并不困惑包含和要求。此外,我不能使用普通包含的原因(正如许多人正确指出的那样)是这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app --include file1.rb”,根据他在file1.rb中编写的代码,应用程序的行为会有所不同。对不起该解释清楚。
编辑:在阅读了Jorg的回答之后,我意识到我的代码并没有完全符合预期的行为,并且他完美地回答了我的(错误的)问题。我正在尝试做更类似于str=(entire file1.rb as string); A.class_exec(str)
的事情。
答案 0 :(得分:10)
这是在Ruby中执行此操作的不好方法。尝试通过模块使用mixins:
file1.rb:
module IncludesFoo
def foo
puts 123
end
end
file2.rb:
require 'file1.rb'
class A
include IncludesFoo
end
A.new.foo
# => 123
答案 1 :(得分:7)
Ruby中的全局过程不是真正的全局过程。他们是方法,就像其他一切一样。特别是,当您定义看起来像的全局过程时,您实际定义了Object
的私有实例方法。由于Ruby中的每一段代码都是在对象的上下文中进行评估,因此这允许您将这些方法用作全局过程,因为self
是默认接收器,而self
是一个对象其类继承自Object
。
所以,这个:
# file1.rb
def foo
puts 123
end
实际上相当于
# file1.rb
class Object
private
def foo
puts 123
end
end
现在您有一个名为foo
的“全局程序”,您可以这样调用:
foo
原因为什么你可以像这样调用它,这个调用实际上等同于
self.foo
和self
是一个在其祖先链中包含Object
的对象,因此它继承了私有的foo
方法。
[注意:确切地说,私有方法不能使用显式接收器调用,即使显式接收器是self
。所以,要真正迂腐,实际等同于self.send(:foo)
而不是self.foo
。]
A.new.foo
中的file2.rb
是一个红色的鲱鱼:您也可以尝试Object.new.foo
或[].foo
或42.foo
并获得相同的结果。
顺便说一句:puts
和require
本身就是这种“全局程序”的例子,它们实际上是Object
上的私有方法(或者更确切地说,它们是Kernel
上的私有方法{1}}混合到Object
)。
旁注:在类定义中调用require
是真的错误的样式,因为它看起来像require
d代码以某种方式作用或者在类中命名,这当然是错误的。 require
只是在文件中运行代码,仅此而已。
所以,而
# file2.rb
class A
require 'file1.rb'
end
是完全有效的代码,它也非常令人困惑。使用以下语义等效的代码更好:
# file2.rb
require 'file1.rb'
class A
end
这样,代码的读者就可以清楚地看到file1.rb
在A
内没有作用域或命名空间。
此外,通常最好不要使用文件扩展名,即使用require 'file1'
代替require 'file1.rb'
。这允许您将Ruby文件替换为例如本机代码(用于MRI,YARV,Rubinius,MacRuby或JRuby),.jar
或.class
文件中的JVM字节代码(用于JRuby), .dll
文件中的CIL字节代码(适用于IronRuby),依此类推,无需更改任何require
次来电。
最后一条评论:绕过访问保护的惯用方法是使用send
,而不是instance_eval
,即使用A.new.send(:foo)
代替A.new.instance_eval {foo}
。
答案 2 :(得分:0)
load("file1", A)
怎么样? (RDoc link)