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

时间:2012-01-10 07:03:32

标签: ruby visibility require public access-specifier

最好用一个例子来解释:

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)的事情。

3 个答案:

答案 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[].foo42.foo并获得相同的结果。

顺便说一句:putsrequire本身就是这种“全局程序”的例子,它们实际上是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.rbA内没有作用域或命名空间。

此外,通常最好不要使用文件扩展名,即使用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