5

最好用一个例子来解释:

文件 1.rb:

def foo
  puts 123
end

文件2.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).

4

3 回答 3

10

这是在 Ruby 中执行此操作的一种不好的方法。尝试通过模块使用 mixins:

文件 1.rb:

module IncludesFoo
  def foo
    puts 123
  end
end

文件2.rb:

require 'file1.rb'

class A
  include IncludesFoo
end

A.new.foo
# => 123
于 2012-01-10T07:08:03.940 回答
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.fooor [].fooor42.foo并获得相同的结果。

顺便说一句:puts并且require它们本身就是这种“全局过程”的例子,它们实际上是私有方法Object(或更准确地说,它们Kernel是混入的私有方法Object)。

附带说明:将调用放在类定义中确实是一种糟糕的风格,因为它使d 代码require看起来像是在类中以某种方式限定了范围或命名空间,这当然是错误的。只需运行文件中的代码,仅此而已。requirerequire

所以,虽然

# 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)、文件中的 CIL 字节码.dll(对于 IronRuby)和等等,而无需更改您的任何require呼叫。

最后一条评论:规避访问保护的惯用方法是使用send,而不是instance_eval,即使用A.new.send(:foo)而不是A.new.instance_eval {foo}

于 2012-01-10T12:45:05.757 回答
0

怎么样load("file1", A)?(RDoc 链接

于 2012-01-15T15:49:52.640 回答