1

既存のメソッドを変更する既存のライブラリのモジュラー拡張を ruby​​ で記述する最良の方法は何だろうか。コードの繰り返しを導入するべきではなく、オンデマンドでのみ使用する必要があります。

私が達成しようとしている特定のタスクは、標準に準拠していないサーバーNet::FTPをサポートするために ruby​​ のモジュールを拡張することです。このような拡張機能は、標準準拠のライブラリ IMHO から完全に分離する必要があります。

追加のファイルを必要とすることは、元のコードで何らかのスイッチを必要とすることさえないので、非常に良いと思いました。したがって、追加require 'net/ftp/forgiving'により、元のライブラリは、あまり才能のないFTPサーバーの仲間に対してもう少し寛容になります.

関連ファイルは、Ruby のオープン クラスとモジュール アーキテクチャを利用して、FTP クラスにパッチを適用できます。上記にリンクされている風変わりな動作の例を修正するには、パッチを適用する必要がありNet::FTP#mkdirます。これは次のようになります。

#content of net/ftp/forgiving
require 'net/ftp'

module Net
  class FTP

    # mkdir that will accept a '250 Directory created' as a valid response
    def mkdir(dirname)
      begin
        original_mkdir(dirname)
      rescue FTPReplyError => e
        raise unless e.message.start_with? '250 Directory created'
        return ""
      end
    end

  end
end

ただし、これには、コードを DRY に保つために、何らかの形でオリジナルNet::FTP#mkdirをキャッシュする必要があります。Net::FTP#original_mkdirこれは可能ですか?このパッチ適用/拡張方法を改善する方法について、さらに提案はありますか? それとも、まったく異なるアプローチでさえありますか?

4

1 に答える 1

4

これは「モンキーパッチング」と呼ばれ、まさに以下のためalias_methodに作成されたユースケースです。

alias_method :original_mkdir, :mkdir
def mkdir(dirname)
  begin
    original_mkdir(dirname)
  rescue FTPReplyError => e
    raise unless e.message.start_with? '250 Directory created'
    return ""
  end
end

これは Ruby でよく見られる「イディオム」ですが、この場合、例外の発生に依存している既存のコード(場合によっては 内のコード) が壊れます。これらの変更をファイルのみに限定することはできません。したがって、元のクラスを開くよりもサブクラスを作成する方がはるかにクリーンです。Netmkdirrequire 'net/ftp/forgiving'

module Net
  class ForgivingFTP < FTP
    # mkdir that will accept a '250 Directory created' as a valid response
    def mkdir(dirname)
      begin
        super(dirname)
      rescue FTPReplyError => e
        raise unless e.message.start_with? '250 Directory created'
        return ""
      end
    end
  end
end

または、さらに良いことに、カスタム名前空間に配置してください! 良い経験則は次のとおりです。

サブクラス可能な場合、monkeypatch必要な場合

(これについては@tadmanに感謝します)。この場合は必要ないようです。

更新:コメントをフォローアップすると、クラスの特定のインスタンスのみを拡張する場合は、Net::FTPそれらのシングルトン クラスを拡張できます。

obj = Net::FTP.new
class << obj
  alias_method :original_mkdir, :mkdir
  def mkdir(dirname)
    #...
    original_mkdir(dirname)
    #...
  end
end
于 2012-02-13T17:26:57.853 に答える