Skip to content
Menu
CDhistory
CDhistory

Ruby Method Auditing Using Module#prepend

Posted on 12月 12, 2021 by admin

昨日、ここデンバーの会社で Ruby 開発者のポジションの面接を受けていたとき、面接官がメタプログラミングの良い課題を提示してくれました:自動メソッド監査人を書くようにとのことでした。 このようなものです:

それは、次のような出力を生成するでしょう:

私は、機能するソリューションを考え出しましたが、Object#send に対する危険なハックを含む可能性がありました。 解決策を発表する前に、私は「これはおそらく悪い考えだが…」と言いましたが、そのとおりでした。

彼が提案したソリューションは、しばしば見落とされる Module#prepend を使用するもので、私はそれが十分にクールであり、記事化する価値があると考えました。

そのラッパーでは、メソッドの本体が実行される前に 1 つの「実行中」メッセージが発射され、メソッドの実行自体、そしてメソッドの実行が正常に完了すると最後の「終了」メッセージが発射されるようにします。

Module#prependをどのように使用するかを理解するために、まずRubyのメソッド継承がどのように機能するかをよく理解する必要があります。

  • 祖先チェーン
  • モジュール自身
  • Bringing it all Together
  • まとめ

祖先チェーン

各Rubyオブジェクトは、祖先チェーンと呼ばれるものの末端に位置することになります。 これは、そのオブジェクトが継承する祖先オブジェクトのリストです。 Ruby では、オブジェクトがメッセージを受け取ったときに、どのバージョンのメソッドが実行されるかを決定するために、この Ancestor Chain を使用します。

実際に Module#ancestors を呼び出すと、任意のオブジェクトの祖先ツリーを表示することができます。 たとえば、以下は Messenger クラスの祖先チェーンです。

モジュールをクラスに #include または #prepend したとき、Ruby はそのクラスの祖先チェーンに変更を加え、どのメソッドをどの順序で発見するかを調整します。 #include と #prepend の大きな違いは、その変更が行われる場所です。

から (現在は存在しない) メソッドをインポートするために Module#include を使用すると、Ruby はそのモジュールを Messenger クラスの祖先として押し込んできます。

Messenger でメソッドを呼び出すと、Ruby は Messenger クラスで定義されているものを調べ、呼び出しに一致するメソッドが見つからない場合は Auditable まで祖先の鎖を上がって再び探します。 Auditable で見つからなければ Object といった具合です。

これが #include ですね。 もし代わりに Module#prepend を使ってモジュールの内容をインポートすると、まったく異なる効果が得られます:

#prepend は Messenger を Auditable の祖先にしています。 待てよ。 何?

これは、Messenger が Auditable のスーパークラスになったように見えるかもしれませんが、ここで起こっていることは正確ではありません。 Messenger クラスのインスタンスは Messenger クラスのインスタンスのままですが、Ruby は Messenger で使用するメソッドを Auditable モジュールで探す前に Messenger クラス自体で探します。

そして、この監査役を作るために利用するのは Auditable#share というメソッドを作成すると Ruby は Messenger#share を見つける前にそれを見つけるということです。 そして、Auditable#share の super 呼び出しを使用して、Messenger で定義された元のメソッドにアクセス (そして実行!) することができます。

モジュール自身

実際には Auditable#share というメソッドを作成するつもりはありません。 なぜそうしないのでしょうか。 なぜなら、これを柔軟なユーティリティにしたいからです。 もし Auditable#share というメソッドをハードコードすると、#share メソッドを実装したメソッドでのみ使用することができるようになります。 さらに悪いことに、私たちが監査したいすべてのメソッドに対して、 この同じ監査パターンを再実装しなければならないでしょう。

そこで、代わりに、メソッドを動的に定義し、それを起動する audit_method クラス メソッドを定義することにしました。 この例では、Auditable#shareというメソッドが作成されます。 前述のように、Ruby は Messenger の元のメソッドを見つける前にこのメソッドを見つけます。

つまり、スーパーコールを使って祖先の連鎖をたどり、Messenger#send を実行することができるのです。

一度オリジナルのメソッドを呼び出したら、終了メッセージを表示して終了とします。 よくやった、ギャング!

Bringing it all Together

あとは、このモジュールを Messenger クラスに prepend 追加するだけで、うまくいくはずです:

そしてなんと、それは動作します。 オブジェクト自体を変更することなく、オブジェクトの動作を変更するためにプリペンドを使用することができます。 これはRubyで高次のコンポーネントを作成するための適応的で明確な方法である。 パフォーマンステスト、エラー処理。

まとめ

Rubyのモジュールは多くの人が思っている以上に複雑です。 そして、その違いを理解することで、ユーティリティベルトのための本当にすてきなツールのロックを解除します。

今日、私が Module#include と Module#prepend についてだけ話し、 Module#extend には触れなかったことに気づいたかもしれません。 それは、Module#extendの動作が同系統のものと大きく異なるからです。

今のところ、もっと学びたいなら、Ruby モジュールを読むことをお勧めします。 Include vs Prepend vs Extend (Léonard Hetsch 著) を読むことをお勧めします。 このすべてをまとめるのに本当に役に立ちました。

コメントを残す コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

最近の投稿

  • アセラ復活。 NYCまたはボストンで99ドル
  • OMIM Entry – # 608363 – CHROMOSOME 22q11.2 DUPLICATION SYNDROME
  • Kate Albrecht’s Parents – Learn More About Her Father Chris Albrecht And Mother Annie Albrecht
  • テンプル・フォーク・アウトフィッターズ
  • Burr(小説)

アーカイブ

  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • DeutschDeutsch
  • NederlandsNederlands
  • SvenskaSvenska
  • DanskDansk
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • RomânăRomână
  • PolskiPolski
  • ČeštinaČeština
  • MagyarMagyar
  • SuomiSuomi
  • 日本語日本語
©2022 CDhistory | Powered by WordPress & Superb Themes