2011-02-01 21 views
10

現在作業中のアプリケーションでは、多くのオブザーバが表示されます。これは確かに、私がコードを変更したり、新しい機能を追加したりしている間、私にとって大きなトラブルを引き起こしています。これらのオブザーバは、数多くの副作用を引き起こします。Rails Observers - Railsでオブザーバを使用しない場合とそうでない場合

私はオブザーバーとその人たちがオブザーバートラップに落ちるように誘惑されたときに経験的または個人的な経験を必要とする機会を知りたいと思います。

あなたの貴重な経験、戦争の物語や考えが求められています。叫んでください!

+0

、(ビジネスロジック)の配列である[ユースケース](http://webuild.envato.com/blog/a-case-for-use-cases/)を使用することを検討すべき手順実行される。独自のユースケースをロールするのは難しくありませんが、いくつかのまともな宝石もあります。 [solid_use_case(https://github.com/mindeavor/solid_use_case)または[USE_CASE(https://github.com/cjohansen/use_case) – Dennis

答えて

28

人はActiveRecordのライフサイクルコールバックを同じものとして扱っているので、オブザーバーが悪いラップをしていると感じます。ライフサイクルコールバックに関する一般的な意見は誤解を招きやすく、自分自身を混乱させることに同意しますが、私は個人的には特定のモデルの中核的責任ではないモデルクラスから物事を守るためのオブザーバーの大ファンです。 Railsのオブザーバーは、部分的にはアスペクト指向のプログラミングに触発されました。彼らはクロスカッティングに関する懸念があります。観察しているモデルと緊密に結合されているオブザーバにビジネスロジックを組み込んでいる場合、間違ったIMOを実行しています。

彼らは、キャッシュの有効期限(掃除)、様々な種類の通知、アクティビティストリームの更新、カスタム分析イベントを追跡するためにバックグラウンドジョブをキックオフ、暖かいキャッシュなど

のように、モデルクラスの外に混乱を維持するための理想的です

ユニットテストを正しく行うことが難しい観察者について、私はBlueFishに断然反対しています。これは正確にライフサイクルコールバックと区別できる最大のポイントです。オブザーバを単独でテストすることができます。そうすることで、BlueFishが指摘している州やオーダー重視のデザインの落とし穴の多くに落ちることを避けることができますライフサイクルコールバックの真実)。ここで

は私の処方です:

  1. はデフォルトで、あなたのテストスイート内のすべてのオブザーバーを無効にします。彼らはあなたのモデルテストを複雑にするべきではありません。なぜなら、とにかく別々の懸念があるからです。オブザーバーが実際に発射する単体テストは必要ありません。なぜなら、ActiveRecordのテストスイートはこれを行い、統合テストではそれをカバーするからです。 ActiveRecord::Base.observers.enableのブロック形式を使用してください。本当にには、単体テストの小さな一部についてオブザーバーを有効にするのがよい理由があると思われますが、誤用や設計上の問題の可能性があります。
  2. 統合テストのオブザーバーのみを有効にする。統合テストは完全なスタックでなければならず、他のすべてのものと同様にオブザーバの動作を検証する必要があります。
  3. オブザーバクラスを孤立してユニットテストするafter_createのようなメソッドを直接呼び出します)。オブザーバが観測されたモデルのビジネスロジックの一部でない場合、おそらくモデルインスタンスの状態の詳細にはあまり依存せず、多くのテスト設定を必要としません。統合テストで最も気にしていることを合理的に確信している場合は、ここで共同作業者を模擬することができます。

ここでRSpecのを使用してアプリケーションのための私の標準的な定型spec/support/observers.rbです:

RSpec.configure do |config| 
    # Assure we're testing models in isolation from Observer behavior. Enable 
    # them explicitly in a block if you need to integrate against an Observer -- 
    # see the documentation for {ActiveModel::ObserverArray}. 
    config.before do 
    ActiveRecord::Base.observers.disable :all 
    end 

    # Integration tests are full-stack, lack of isolation is by design. 
    config.before(type: :feature) do 
    ActiveRecord::Base.observers.enable :all 
    end 
end 

そしてhere is a real-world example私はオブザーバを使用して、痛みなしでそれをテストするための優れたケースを示している願っています。オブザーバーに対するそれらのため

+0

すばらしい答え!仰るとおりです。そして、 – karthiks

+0

:-)この質問に答えるあなたの時間を離陸のためのおかげで私は答えに記載された問題の多くを解決Wisper宝石(https://github.com/krisleech/wisper)を提案することができます。 – Kris

6

IMHO - オブザーバーは、私は彼らがそう考える理由の数を通過します

を吸います。これは一般的にbefore_xまたはafter_xメソッドの使用にも当てはまります。これは一般的なObserverのより細かい例です。

は難しい、適切なユニットテスト通常

を書くためにあなたはユニットテストを書くとき、あなたは機能の特定の部分をテストしているします。しかし、オブザーバーをテストするには、イベントをテストするためにイベントをトリガーする必要があります。時にはそれはまったく不便です。

など。オブザーバをbefore_saveに接続してコードをトリガするには、モデルを保存する必要があります。これは、あなたが永続性ではなくビジネスロジックをテストしていることを考えると、テストするのは難しいことです。保存したものを模倣すると、トリガーが機能しないことがあります。あなたがそれを保存させると、あなたのテストは遅くなります。

状態

オブザーバーはテストが困難になる傾向があるという事実に続き必要、オブザーバーはまた、国家の多くを必要とする傾向があります。その理由は、オブザーバーのロジックがさまざまな「ビジネスイベント」を区別しようとしているためであり、そうする唯一の方法はオブジェクトの状態を見ることです。これはテストで多くのセットアップを必要とするため、テストが難しく、面倒で問題になります。

意図しない結果

間違いなく、あなたはちょうどあなたが複数の観測をフックすることができますので、あなたはさまざまな動作をトリガすることができるもの見当がつかないことを経験しています。これは意図しない結果につながります。統合/システムテスト(ゆっくりとしたフィードバック)によってのみ取り上げることができます。あなたのオブザーバーを追跡することも、あまり面白くありません。

注文仮定の問題

観察者がで蹴ることがある。あなたはそれがキックオフされることだけが保証されている保証はありません。ビジネスルールの一部として暗黙の注文がある場合、オブザーバーは間違っています。

が悪いの設計につながるオブザーバーをフックして、物事を追加する貧しいデザインに

をリードしています。それは、保存したり、削除したり、イベントを作成したりするのに便利ですが、理解しづらいこともあります。例えば。ユーザーを保存すると、ユーザーの詳細を更新しているか、新しいアカウント名を追加している可能性があります。あなたがオブジェクトに対して特に行うことができることを知ることは、メソッドと意味のあるアクションベースの名前を持つ理由の一部です。すべてがオブザーバーであれば、これは失われ、すべてがイベントに応答しています。そして、観測ロジック内では、どのビジネスイベントに属するイベントを区別しようとする傾向があります。

オブザーバが素晴らしい場所がいくつかありますが、通常は例外です。コールバックを介して暗黙的にロジックをエンコードするのではなく、明示的にできることを移植する方がはるかに優れています。

5

私は、オブザーバーが不必要な複雑さを導入する可能性があるという点で、BlueFishに部分的に同意しますが、オブザーバーはオブジェクトから懸念事項を分離するのに便利です。

たとえば、支払いARモデルでは、作成後に領収書を受け取りたいことがあります。通常のAR after_createコールバックを使用して、deliver_receiptメソッドが失敗した場合、支払いはデータベースに書き込まれません - oops!しかし、オブザーバーでは、支払いはまだ保存されます。 失敗は救助によって処理されるべきだと主張する人もいるかもしれませんが、私はまだそこに属していないと思います。それはオブザーバーに属します。

+0

興味深いPOV。しかし、after_createの一部として、receipt_ジェネレーションをdelayed_jobとしてプッシュする方が良いとは限りません。そうすれば、オブザーバーは避けられ、コードも維持することができます。 – karthiks

+0

遅延ジョブとして追加することは確かにオプションですが、電子メールを送信することは非常に高価な操作ではありません。そのため、IMOはその複雑さの追加を正当化しません。 – Zubin

+0

支払いオブジェクトにステートマシンがあり、不完全 - >失敗または不完全 - >成功への移行にフックする必要がありますか? 'state_machine'のようなものは、DB /永続化イベントではなくビジネスイベントに対応する状態変化のためのオブザーバを書くことを可能にします。 –