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