Ruby 导入的方法总是私有的吗?
这最好用一个例子来解释:
file1.rb:
def foo
puts 123
end
file2.rb:
class A
require 'file1'
end
A.new.foo
将给出错误“':调用了私有方法'foo'”。
我可以通过执行 A.new.send("foo")
来解决这个问题,但是有没有办法使导入的方法公开?
编辑:澄清一下,我没有混淆 include 和 require。另外,我不能使用正常包含的原因(正如许多人正确指出的那样)是因为这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app --include file1.rb”,应用程序将根据他在 file1.rb 中编写的代码表现不同。抱歉,应该解释得更清楚。
编辑:阅读 Jorg 的答案后,我意识到我的代码的行为并不完全符合预期,他完美地回答了我的(误导的)问题。我正在尝试做一些更类似于 str=(entire file1.rb as string); 的事情A.class_exec(str)
。
This is best explained with an example:
file1.rb:
def foo
puts 123
end
file2.rb:
class A
require 'file1'
end
A.new.foo
will give an error "': private method 'foo' called".
I can get around this by doing A.new.send("foo")
but is there a way to make the imported methods public?
Edit: To clarify, I am not confusing include and require. Also, the reason why I cannot use normal inclusion (as many have rightly pointed out) is that this is part of a meta-programming setup. I need to allow the user to add functionality at run-time; eg he can say "run-this-app --include file1.rb" and the app will behave differently based on the code he wrote in file1.rb. Sorry should have explained clearer.
Edit: After reading Jorg's answer I realized my code does not behave exactly as intended, and he answers my (misguided) question perfectly. I am trying to do something more akin to str=(entire file1.rb as string); A.class_exec(str)
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在 Ruby 中这是一个糟糕的方法。尝试通过模块使用 mixins:
file1.rb:
file2.rb:
This is a bad way to do this in Ruby. Try using mixins via modules instead:
file1.rb:
file2.rb:
Ruby 中的全局过程并不是真正的全局过程。它们是方法,就像其他一切一样。特别是,当您定义看起来全局过程时,您实际上定义了
Object
的私有实例方法。由于 Ruby 中的每一段代码都是在对象的上下文中计算的,因此您可以像使用全局过程一样使用这些方法,因为self
是默认接收者,而self< /code> 是一个对象,其类继承自
Object
。所以,这
实际上相当于
现在你有一个名为
foo
的“全局过程”,你可以像这样调用它:原因为什么你可以这样调用它,这个调用实际上相当于
,并且
self
是一个在其祖先链中包含Object
的对象,因此它继承了私有的foo
方法。[注意:准确地说,私有方法不能用显式接收者调用,即使显式接收者是
self
。因此,说实话,它实际上相当于self.send(:foo)
而不是self.foo
。]
file2.rb
中的 >A.new.foo 是一个转移注意力的东西:您也可以尝试Object.new.foo
或[ ].foo
或42.foo
并得到相同的结果。顺便说一句:
puts
和require
本身就是此类“全局过程”的示例,它们实际上是Object
上的私有方法(或更准确地说,它们是Kernel
上的私有方法,混合到Object
中)。旁注:在类定义中调用
require
确实是一种不好的风格,因为它使它看起来像require
d 代码在类内部以某种方式限定作用域或命名空间,这当然是错误的。require
只是运行文件中的代码,仅此而已。因此,虽然
这是完全有效的代码,但它也非常令人困惑。最好使用以下语义上等效的代码:
这样,代码的读者就可以非常清楚
file1.rb
绝不在A< 内进行作用域或命名空间。 /代码>。
另外,通常最好不要使用文件扩展名,即使用
require 'file1'
而不是require 'file1.rb'
。这允许您将 Ruby 文件替换为本机代码(对于 MRI、YARV、Rubinius、MacRuby 或 JRuby)、.jar
或.class 文件(对于 JRuby)、
.dll
文件中的 CIL 字节代码(对于 IronRuby)等等,而无需更改任何require
调用。最后一点:规避访问保护的惯用方法是使用
send
,而不是instance_eval
,即使用A.new.send(:foo)
> 而不是A.new.instance_eval {foo}
。Global procedures in Ruby aren't really global procedures. They are methods, like everything else. In particular, when you define what looks like a global procedure, you are actually defining a private instance method of
Object
. Since every piece of code in Ruby is evaluated in the context of an object, this allows you to use those methods as if they were global procedures, sinceself
is the default receiver, andself
is an object whose class inherits fromObject
.So, this:
is actually equivalent to
Now you have a "global procedure" called
foo
, which you can call just like this:The reason why you can call it like this, is that this call is actually equivalent to
and
self
is an object that includesObject
in its ancestry chain, thus it inherits the privatefoo
method.[Note: to be precise, private methods cannot be called with an explicit receiver, even if that explicit receiver is
self
. So, to be really pedantic, it is actually equivalent toself.send(:foo)
and notself.foo
.]The
A.new.foo
in yourfile2.rb
is a red herring: you could just as well tryObject.new.foo
or[].foo
or42.foo
and get the same result.By the way:
puts
andrequire
are themselves examples of such "global procedures", which are actually private methods onObject
(or more precisely, they are private methods onKernel
which is mixed intoObject
).On a sidenote: it is really bad style to put calls to
require
inside a class definition, because it makes it look like therequire
d code is somehow scoped or namespaced inside the class, which is of course false.require
simply runs the code in the file, nothing more.So, while
is perfectly valid code, it is also very confusing. It is much better to use the following, semantically equivalent, code:
That way it is perfectly clear to the reader of the code that
file1.rb
is in no way scoped or namespaced insideA
.Also, it is generally preferred to leave off the file extension, i.e. to use
require 'file1'
instead ofrequire 'file1.rb'
. This allows you to replace the Ruby file with, for example, native code (for MRI, YARV, Rubinius, MacRuby or JRuby), JVM byte code in a.jar
or.class
file (for JRuby), CIL byte code in.dll
file (for IronRuby) and so on, without having to change any of yourrequire
calls.One last comment: the idiomatic way to circumvent access protection is to use
send
, notinstance_eval
, i.e. useA.new.send(:foo)
instead ofA.new.instance_eval {foo}
.load("file1", A)
怎么样? (RDoc 链接)What about
load("file1", A)
? (RDoc link)