5

これは、次の例で最もよく説明されています。

ファイル1.rb:

def foo
  puts 123
end

ファイル2.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)

4

3 に答える 3

10

これは、Ruby でこれを行うには悪い方法です。代わりにモジュールを介してミックスインを使用してみてください:

ファイル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ます。selfObject

したがって、この:

# file1.rb

def foo
  puts 123
end

実際には同等です

# file1.rb

class Object
  private

  def foo
    puts 123
  end
end

これで、 という「グローバル プロシージャ」fooができました。これは、次のように呼び出すことができます。

foo

このように呼び出すことができる理由は、この呼び出しが実際には

self.foo

祖先チェーンにself含まれるオブジェクトであるため、プライベートメソッドを継承します。Objectfoo

[注: 正確には、プライベート メソッドは明示的なレシーバーで呼び出すことはできませself。したがって、本当に詳しく言うと、実際には同等でself.send(:foo)あり、そうではありませんself.foo。]

A.new.fooあなたのfile2.rb場合はニシンです:Object.new.fooまたは[].fooまたは42.fooを試しても同じ結果が得られます。

ところで、putsrequireはそれ自体がそのような「グローバル プロシージャ」の例であり、実際には のプライベート メソッドですObject(より正確には、Kernelが に混在するプライベート メソッドですObject)。

補足:への呼び出しをクラス定義内に置くのは本当に悪いスタイルです。これは、 d コードが何らかの形でクラス内でスコープまたは名前空間化されているように見えるためです。これはもちろん false です。ファイル内のコードを実行するだけで、それ以上は何もありません。requirerequirerequire

だから、その間

# 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 に答える