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

发布于 2024-12-26 10:38:41 字数 601 浏览 3 评论 0原文

这最好用一个例子来解释:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

他是夢罘是命 2025-01-02 10:38:41

在 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

This is a bad way to do this in Ruby. Try using mixins via modules instead:

file1.rb:

module IncludesFoo
  def foo
    puts 123
  end
end

file2.rb:

require 'file1.rb'

class A
  include IncludesFoo
end

A.new.foo
# => 123
燃情 2025-01-02 10:38:41

Ruby 中的全局过程并不是真正的全局过程。它们是方法,就像其他一切一样。特别是,当您定义看起来全局过程时,您实际上定义了Object的私有实例方法。由于 Ruby 中的每一段代码都是在对象的上下文中计算的,因此您可以像使用全局过程一样使用这些方法,因为 self 是默认接收者,而 self< /code> 是一个对象,其类继承自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

] file2.rb 中的 >A.new.foo 是一个转移注意力的东西:您也可以尝试 Object.new.foo[ ].foo42.foo 并得到相同的结果。

顺便说一句:putsrequire 本身就是此类“全局过程”的示例,它们实际上是 Object 上的私有方法(或更准确地说,它们是 Kernel 上的私有方法,混合到 Object 中)。

旁注:在类定义中调用 require 确实是一种不好的风格,因为它使它看起来像 required 代码在类内部以某种方式限定作用域或命名空间,这当然是错误的。 require 只是运行文件中的代码,仅此而已。

因此,虽然

# 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 文件(对于 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, since self is the default receiver, and self is an object whose class inherits from Object.

So, this:

# file1.rb

def foo
  puts 123
end

is actually equivalent to

# file1.rb

class Object
  private

  def foo
    puts 123
  end
end

Now you have a "global procedure" called foo, which you can call just like this:

foo

The reason why you can call it like this, is that this call is actually equivalent to

self.foo

and self is an object that includes Object in its ancestry chain, thus it inherits the private foo 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 to self.send(:foo) and not self.foo.]

The A.new.foo in your file2.rb is a red herring: you could just as well try Object.new.foo or [].foo or 42.foo and get the same result.

By the way: puts and require are themselves examples of such "global procedures", which are actually private methods on Object (or more precisely, they are private methods on Kernel which is mixed into Object).

On a sidenote: it is really bad style to put calls to require inside a class definition, because it makes it look like the required 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

# file2.rb

class A
  require 'file1.rb'
end

is perfectly valid code, it is also very confusing. It is much better to use the following, semantically equivalent, code:

# file2.rb

require 'file1.rb'

class A
end

That way it is perfectly clear to the reader of the code that file1.rb is in no way scoped or namespaced inside A.

Also, it is generally preferred to leave off the file extension, i.e. to use require 'file1' instead of require '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 your require calls.

One last comment: the idiomatic way to circumvent access protection is to use send, not instance_eval, i.e. use A.new.send(:foo) instead of A.new.instance_eval {foo}.

愁杀 2025-01-02 10:38:41

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

What about load("file1", A)? (RDoc link)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文