Ruby导入的方法总是私有的吗?

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)


Jör*_*tag 7

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[].foo42.foo并且得到同样的结果.

顺便说一句:putsrequire本身的这种"全球性程序",这实际上是对私有方法的实例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}