1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

モンキーパッチのプログラミングスタイルとRubyでの利用について

2022-01-04 21:14:29

モンキーパッチとは?動的言語において、ソースコードに手を加えることなく、機能の追加や変更を行うこと。

モンキーパッチを使用する目的
1. 追加機能
2、機能変更
3、プログラムエラーの修正
4. ログの出力、AOPの実装など、メソッド実行中に何らかの処理を行うフックを追加する。
5.キャッシュ、大量の計算の場合、決済後の結果を繰り返し利用できるため、計算終了後のメソッドの入れ替えにより、処理速度を向上させることができる。

Rubyのクラスはオープンクラス、つまりクラス定義の後に任意の内容を追加できるため、Rubyでは特にモンキーパッチの利用が容易です。また、Rubyにはメソッド、クラス、モジュールを操作する機能があり、モンキーパッチをさらに使いやすくしています。
      のエイリアスです。メソッドのエイリアス
      include: 他のモジュールからメソッドを導入する
      remove_method: このクラスからメソッドを削除します。
      undef:メソッドをキャンセルする 
Rubyでモンキーパッチを使う
私が遭遇していたシナリオは、こんな感じでした。

弊社ではEC2の運用にサードパーティのライブラリであるfogを使用しています。インスタンスの作成など、多くのコマンドでインスタンスタイプのパラメータを設定する必要があります。fogでは、すべてのEC2タイプがfog/aws/models/compute/flavors.rbのFLAVORS配列で定義されています。FLAVORS配列にないタイプを設定すると、fogはそれを無効なパラメータとして扱い、エラーを報告します。

その後、Amazonが新しいインスタンスタイプD2をリリースしました。Rubyのサードパーティコミュニティは非常に活発ですが、fogの開発コミュニティはflavors.rbにD2を追加するのに間に合わず、我々の仕事ではD2インスタンスを緊急に使用する必要があります。

背景がわかったところで、どうすればいいのか見てみましょう。

方法1:新しいタイプを追加するために、fogにPull Requestを提出することができます。

しかし、この方法はうまくいきません。knife-ec2 の fog へのバージョン依存は 1.25.* でなければなりませんが、fog は 1.31.0 に更新されており、fog は 1.27.0 から構造が大きく変わっています。明らかに、新しいバージョンの fog をサポートするために knife-ec2 が更新されるのを待つことはできないので、fog を更新するための Pull Request を提出することができません。

方法2:古いfogを手動で更新する 最新バージョンのfogを使うことができないので、1.25バージョンのfogを手動で編集してGemとしてパッケージングします。この方法は、前の方法よりも簡単ですが、メンテナンスが容易ではないという問題があります。非常に小さな変更のためにサードパーティのライブラリに自分のコードを追加することは、必ずしも "clean"十分に感じられるものではありません。

最後に、同僚からの指導もあり、3番目のアプローチであるモンキーパッチを行いました。Rubyプロジェクトにlib/PROJECT_NAME/monkey_patches/flavors.rbを追加し、次のコードを fog/aws/models/ compute/flavors に加えました。

require 'fog/aws/models/compute/flavors'

class Object

 def redef_without_warning(const, value)
  mod = self.is_a?(Module) ? self : self.class
  mod.send(:remove_const, const) if mod.const_defined?(const)
  mod.const_set(const, value)
 end
end

module Fog
 module Compute
  class AWS
   NEW_FLAVORS = FLAVORS + [
    {
     :id => "d2.xlarge",
     ...
    },
    {
     :id => "d2.2xlarge",
     ...
    },
    {
     :id => "d2.4xlarge",
     ...
    },
    {
     :id => "d2.8xlarge",
     ...
    }
   ]

   redef_without_warning :FLAVORS, NEW_FLAVORS

  end
 end
end



概要
自作のコードにMonkeyのパッチを追加することで、fogに新しいインスタンスタイプを動的に追加することができました。そして、この方法は、最小限のコード変更で、より簡単に維持することができるようになりました。

モンキーパッチは完璧な解決策ではなく、いくつかの落とし穴があります。そのため、この手法はソフトウェア工学の世界ではいまだに賛否両論があります。しかし、私はMonkey Patchが良いゼロデイソリューションであると信じています。