2012-01-10 3 views
5

Rubyのインポートメソッドは常にプライベートですか?これは、最良の例で説明されて

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で書いたコードに基づいて異なる動作をします。申し訳ありませんが明らかに説明する必要があります。

編集:ジョルグの答えを読んだ後、私のコードは意図したとおりに正しく動作しないと分かり、彼は私の(誤った)質問に完全に答えます。私はstr=(entire file1.rb as string); A.class_exec(str)のような何かをしようとしています。

+0

「A.new.instance_eval {foo}」を試しましたか?それは私のためには機能しません(ruby 1.9.2)。 – knut

+0

'requireと' include'を混同していますか? –

+0

@knutでした。できます。 – alexloh

答えて

7

グローバル手順は本当にグローバルな手順ではありません。彼らは他のすべてのもののような方法です。特に、がグローバルプロシージャのように定義されている場合は、となり、実際にObjectのプライベートインスタンスメソッドを定義しています。 Rubyのコードはすべてオブジェクトのコンテキストで評価されるため、selfはデフォルトの受信者であり、selfはクラスがObjectから継承されるオブジェクトであるため、それらのメソッドをグローバルプロシージャのように使用できます。

ので、この:

# file1.rb 

def foo 
    puts 123 
end 

は今、あなたはちょうどこのように呼び出すことができますfooいわゆる "グローバルな手続き" を、持っている実際に

# file1.rb 

class Object 
    private 

    def foo 
    puts 123 
    end 
end 

と同等です:

foo 

理由なぜこのように呼び出すことができますか?この呼び出しは、実際

self.foo 

selfと同等であることであること、したがって、それはプライベートfooメソッドを継承し、その祖先鎖にObjectを含むオブジェクトです。

[注:具体的な受信者がselfのであっても、明示的な受信者でプライベートメソッドを呼び出すことはできません。だから、本当に知識をひけらかすように、それは実際にself.send(:foo)に相当ないself.fooである]で

A.new.fooあなたfile2.rbが赤ニシンです。あなただけのようにもObject.new.fooまたは[].fooまたは42.fooを試してみて、同じ結果を得ることができます。

ところで

putsrequireは実際Object(またはより正確には、それらはObjectに混合するKernelのプライベートメソッドである)上でプライベートメソッドであるような「グローバルプロシージャ」の一例そのものです。追記で

:それは、それが何らかの形でスコープやもちろん偽のクラス、内部で名前空間さrequire Dコードのように見えるので、が本当に悪いスタイルは、クラス定義内requireへの呼び出しを置くことです。 requireはファイル内のコードを実行するだけです。

ので、しばらく

# file2.rb 

class A 
    require 'file1.rb' 
end 

が、それはまた、非常に混乱して、完全に有効なコードです。それはfile1.rbがスコープまたはAの内側に名前空間では決してないことをコードの読者に完全に透明である

# file2.rb 

require 'file1.rb' 

class A 
end 

その方法:次の、意味的に等価で、コードを使用することがはるかに優れています。

また、ファイル拡張子を残しておくこと、すなわちrequire 'file1.rb'の代わりにrequire 'file1'を使用することが一般に好ましい。例えば、ネイティブコード(MRI、YARV、Rubinius、MacRuby、JRubyの場合)、JVMバイトコードが.jarまたは.class(JRubyの場合)、CILバイトコードが.dllの場合(for IronRuby)など、あなたのrequireコールを変更する必要はありません。

最後のコメント:アクセス保護を回避するための慣用的な方法は、の代わりにA.new.send(:foo)を使用して、instance_evalではなくsendを使用することです。

+0

ありがとう!この回答は本当に完全であり、私が持っていた誤解を解消しました.Cのように、コピーしたテキストを含める必要がありました。クラスAのメソッドとしてfile1.rbの内容を動的に追加する方法はありますか?file1の内容と場所は実行時にのみ分かり、ユーザーはAについて知りません。ファイルに定義されているメソッドやモジュールのリストを見つけることはできますか? – alexloh

10

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

file1.rb:

module IncludesFoo 
    def foo 
    puts 123 
    end 
end 

file2.rb:Rubyで

require 'file1.rb' 

class A 
    include IncludesFoo 
end 

A.new.foo 
# => 123 
+0

'file1.rb'とその内容は両方ともユーザー入力から得られ、動的にロードされます。基本的に、ユーザは実行時にパスを入力することができ、アプリケーションは適切なルビファイルをロードし、再起動せずに既存の機能を変更します。 ごめんなさい。 – alexloh

関連する問題