2017-12-17 8 views
1

TL; DR;
Rubyのコアクラスからメソッド(どこにも書かれていない)をどのように呼び出すことができますか?Rubyコアクラス内からカスタムメソッドを呼び出す


私はテキストファイルを管理するスクリプトを作成しています。私はこの方法を導入することで、コードをクリーンアップする

File.open("file.txt", "w").each do |f| 
    f.puts "Text to be inserted" 
    f.puts text_generated 
    f.puts "Some other text" if some_condition? 
    f.puts "" 
end 

:ここに私のコードです

File.open("file.txt", "w").each do |f| 
    f.puts_content(text_generated, some_condition? 
    # -> Generates an error: private method called for <File> (NoMethodError) 
end 

def puts_content(text, cond) 
    puts "Text to be inserted" 
    puts text 
    puts "Some other text" if cond 
    puts "" 
end 

しかし、実際には、この方法は、理由のプライベートメソッドへのアクセスのFileクラス内から呼び出すことはできません。

誰もがそのエラーを説明することができますか、どのようにそれを行うことができますか?


私の回避策はFileから継承したカスタムMyFileクラス内のこれらのメソッドを記述することです:私はFileで直接その原料を置くことができ

MyFile.open("file.txt", "w").each do |f| 
    f.puts_content # Seems to work 
end 

class MyFile < File 
    def puts_content(cond) 
    puts "Text to be inserted" 
    puts text_generated_elsewhere 
    puts "Some other text" if cond 
    puts "" 
    end 
end 

、それが触れることになると私は臆病です言語コアライブラリ。

これが良い方法かどうかを知りたいのですが。


他のコアモジュール/クラスからRubyコアメソッドを呼び出すことは可能です。すべてのコアモジュール/クラスが相互に含まれているか、または相互に要求していることを意味しますか?どのようにそれはボンネットの下で動作するのですか?

答えて

1

あなたはトップレベルのメソッドを定義する場合、それはオブジェクトのインスタンスメソッドを追加し、従って、クラス(他のコアクラスのほとんど)

def foo 
    1 
end 
method(:foo) 
# => #<Method: Object#foo> 

を子孫にアクセス可能これがアクセスレベルメソッドは、スクリプトを実行しているときとIRB/pryが異なるようです。IRBでは

:もちろん

puts [].foo 
# => NoMethodError (private method called...) 

が、あなたはいつもちょうどsendを使用してプライベートメソッドを呼び出すことができます:スクリプトで

puts [].foo 
# => 1 

[].send(:foo) 
# or, in your case, f.send(:puts_content, text_generated, some_condition?) 

また、中にもありません既に定義されている場合、子孫クラスのメソッドを上書きします:

2番目の方法(コアクラスを直接パッチする)は動作し、puts_contentが既にFileに定義されていればそれを上書きします(存在しない場合)。あなたはコアクラスをパッチ適用避けたい場合は、私はお勧めの2つのアプローチがあります

  1. は静的(クラス)メソッドを使用して、引数

    class FileUtils 
        def self.puts_content(file, text, cond) 
        file.puts "Text to be inserted" 
        file.puts text 
        file.puts "Some other text" if cond 
        file.puts "" 
        end 
    end 
    File.open("file.txt", "w").each do |f| 
        FileUtils.puts_content(f, text_generated, some_condition?) 
    end 
    
  2. 洗練を使用すると、ファイルオブジェクトを渡します:

    module FileUtils 
        refine File do 
        def puts_content(text, cond) 
         puts "Text to be inserted" 
         puts text 
         puts "Some other text" if cond 
         puts "" 
        end 
        end 
    end 
    
    # elsewhere ... 
    using FileUtils 
    
    File.open("file.txt", "w").each do |f| 
        f.puts_content(f, text_generated, some_condition?) 
    end 
    

    あなたは改良hereについて読むことができますが、基本的に、彼らは、特定のファイルまたはクラスのコアクラスにパッチを適用する方法です。これにより、他の場所で定義された動作を変更する危険性が少なく、素敵で簡潔な猿のパッチ構文の利点が得られます。

+0

こんにちは、私は精緻化について聞いたことがありません、最後には*(Btw、[こちらはドキュメントです](https://ruby-doc.org/core-2.4.2/doc/syntax/refinements_rdoc.html)バージョン(2.4.2)および詳細は実験的な機能ではありません)*。しかし、私はあなたの最初の提案が本当に好きです。全体の構造はまったく同じであり、意図は変更されません。私はそのすべてのもの(静的クラス)がどのように一緒に収まるかについてもう少し学ぶべきです。また、モジュールの使用はどうですか?私はそれが他のクラスやメソッドと "混合"する可能性のあるモジュールについて聞いたことがあります。 – SanjiBukai

+1

メソッドをモジュールに書くことができます( 'refine File do'行なしの洗練定義と同じように見えます)。 'File.include FileUtils'でそれをインクルードしますが、基本的にFileクラスを再オープンしてメソッドを追加するのと同じことです –

+0

ありがとうございます。それは明らかですが、静的なクラスを使用することはまだ私の好きな方法です。ありがとうございました。 – SanjiBukai

0

あなたの最初の質問についてメソッドがFileクラスで定義されていないため、エラーが発生しました。だからあなたはこれをf.puts_contentのように呼び出すことができませんでした。

puts_content(file, ...)としてFileを受け取るメソッドを定義できます。

質問の第2部分については、これは良い解決策です(オブジェクト指向を考えている)。

+0

こんにちは、私はオープン*一度私が*実際に ...そのようにファイルを書くことについて、私は文章の多くのチャンクを書き込むファイルは思っていないし、さらにいくつかの方法で、「パッケージ」、それらをすることにより、私はまだ多くのカスタムメソッドを持っています。 それ以外の方法で行う必要がある場合は、毎回ファイルを開いて閉じてください。しかし、それは低レベルのものを分離しておくので、より簡単になる可能性があります。 これは別の質問につながります:私は100行のテキストを書かなければならないとしましょう。一度ファイルを開いてすべての行を書き込むか、ファイルを100回、 *(100行を書く必要があると仮定)* – SanjiBukai

+0

ファイルを一度開く方が良いです。コード構造について考えると、コードを多くの関数とクラスに分割することはできません。 –

関連する問題