156

Observersが Rails 4.0 から正式に削除されたため、他の開発者が代わりに何を使用しているのか気になります。(抽出された gem を使用する場合を除きます。) オブザーバーは確かに悪用され、扱いにくくなる可能性がありましたが、有益なキャッシュのクリア以外にも多くのユースケースがありました。

たとえば、モデルへの変更を追跡する必要があるアプリケーションを考えてみましょう。オブザーバーは、モデル A の変更を簡単に監視し、それらの変更をモデル B でデータベースに記録できます。複数のモデルにわたる変化を監視したい場合は、1 つのオブザーバーで処理できます。

Rails 4 では、他の開発者が Observer の代わりにどのような戦略を使用してその機能を再作成しているのか興味があります。

個人的には、これらの変更が各モデル コントローラーの create/update/delete メソッドで追跡される、一種の "fat controller" 実装に傾倒しています。各コントローラーの動作が少し肥大化しますが、すべてのコードが 1 か所にあるため、読みやすさと理解に役立ちます。欠点は、非常によく似たコードが複数のコントローラーに散らばっていることです。そのコードをヘルパー メソッドに抽出することはオプションですが、それでも、あちこちに散らばっているこれらのメソッドへの呼び出しが残っています。世界の終わりではありませんが、「スキニーコントローラー」の精神でもありません.

ActiveRecord コールバックも考えられるオプションですが、個人的には好きではありません。これは、2 つの異なるモデルを密接に結びつける傾向があるためです。

では、Rails 4 のノー オブザーバーの世界で、別のレコードが作成/更新/破棄された後に新しいレコードを作成する必要がある場合、どのデザイン パターンを使用しますか? ファット コントローラ、ActiveRecord コールバック、またはまったく別のものですか?

ありがとうございました。

4

12 に答える 12

33

それらは現在プラグインにあります。

次のようなコントローラーを提供する代替案もお勧めできますか。

class PostsController < ApplicationController
  def create
    @post = Post.new(params[:post])

    @post.subscribe(PusherListener.new)
    @post.subscribe(ActivityListener.new)
    @post.subscribe(StatisticsListener.new)

    @post.on(:create_post_successful) { |post| redirect_to post }
    @post.on(:create_post_failed)     { |post| render :action => :new }

    @post.create
  end
end
于 2013-05-17T14:38:40.717 に答える
13

アクティブ レコード コールバックを使用すると、カップリングの依存関係が単純に反転します。たとえば、 Rails 3 スタイルを観察している場合はmodelA、問題なく削除できます。さて、代わりに、後で保存を手動で呼び出す必要があるとします。これは Rails 4 になります。依存関係を移動しただけなので、安全に削除できますが、削除することはできません。CacheObservermodelACacheObserverACacheObserverACacheObserver

さて、私の象牙の塔から、オブザーバーが観察しているモデルに依存することを好みます。コントローラーを散らかすほど気にしますか? 私にとって、答えはノーです。

おそらく、オブザーバーが必要な/必要な理由について考えたことがあるので、オブザーバーに依存するモデルを作成することはひどい悲劇ではありません。

また、コントローラーのアクションに依存しているあらゆる種類のオブザーバーに対して(合理的に根拠があると思いますが)嫌悪感を持っています。突然、監視対象のモデルを更新する可能性のあるコントローラー アクション (または別のモデル) にオブザーバーを挿入する必要があります。アプリが作成/更新コントローラー アクションを介してのみインスタンスを変更することを保証できる場合、より強力になりますが、それは Rails アプリケーションについて行う仮定ではありません (ネストされたフォーム、モデル ビジネス ロジック更新関連付けなどを検討してください)。

于 2013-03-01T19:53:51.970 に答える
13

Wisperは優れたソリューションです。コールバックに対する私の個人的な好みは、それらがモデルによって起動されることですが、イベントはリクエストが入ったときにのみリッスンされます。つまり、テストなどでモデルをセットアップしている間はコールバックを起動したくありませんが、必要です。コントローラーが関与するたびに発生します。これは、ブロック内のイベントのみをリッスンするように指示できるため、Wisper でのセットアップは非常に簡単です。

class ApplicationController < ActionController::Base
  around_filter :register_event_listeners

  def register_event_listeners(&around_listener_block)
    Wisper.with_listeners(UserListener.new) do
      around_listener_block.call
    end
  end        
end

class User
  include Wisper::Publisher
  after_create{ |user| publish(:user_registered, user) }
end

class UserListener
  def user_registered(user)
    Analytics.track("user:registered", user.analytics)
  end
end
于 2014-03-28T19:21:35.517 に答える
4

Rails 3 Observers に代わる私の方法は、モデル内で定義されたコールバックを利用する手動実装ですが、(agmin が上記の回答で述べているように)「依存関係を反転させます...カップリング」を管理します。

私のオブジェクトは、オブザーバーの登録を提供する基本クラスから継承します。

class Party411BaseModel

  self.abstract_class = true
  class_attribute :observers

  def self.add_observer(observer)
    observers << observer
    logger.debug("Observer #{observer.name} added to #{self.name}")
  end

  def notify_observers(obj, event_name, *args)
    observers && observers.each do |observer|
    if observer.respond_to?(event_name)
        begin
          observer.public_send(event_name, obj, *args)
        rescue Exception => e
          logger.error("Error notifying observer #{observer.name}")
          logger.error e.message
          logger.error e.backtrace.join("\n")
        end
    end
  end

end

(確かに、継承より合成の精神で、上記のコードをモジュールに配置し、各モデルに混在させることができます。)

初期化子はオブザーバーを登録します。

User.add_observer(NotificationSender)
User.add_observer(ProfilePictureCreator)

その後、各モデルは、基本的な ActiveRecord コールバックを超えて、独自の監視可能なイベントを定義できます。たとえば、私の User モデルは 2 つのイベントを公開します。

class User < Party411BaseModel

  self.observers ||= []

  after_commit :notify_observers, :on => :create

  def signed_up_via_lunchwalla
    self.account_source == ACCOUNT_SOURCES['LunchWalla']
  end

  def notify_observers
    notify_observers(self, :new_user_created)
    notify_observers(self, :new_lunchwalla_user_created) if self.signed_up_via_lunchwalla
  end
end

これらのイベントの通知を受け取りたいオブザーバーは、(1) イベントを公開するモデルに登録し、(2) イベントに一致する名前のメソッドを持つ必要があるだけです。予想どおり、複数のオブザーバーが同じイベントに登録でき、(元の質問の 2 番目の段落を参照して) オブザーバーは複数のモデルにわたってイベントを監視できます。

以下の NotificationSender および ProfilePictureCreator オブザーバー クラスは、さまざまなモデルによって公開されるイベントのメソッドを定義します。

NotificationSender
  def new_user_created(user_id)
    ...
  end

  def new_invitation_created(invitation_id)
    ...
  end

  def new_event_created(event_id)
    ...
  end
end

class ProfilePictureCreator
  def new_lunchwalla_user_created(user_id)
    ...
  end

  def new_twitter_user_created(user_id)
    ...
  end
end

1 つの注意点は、すべてのモデルで公開されるすべてのイベントの名前が一意でなければならないことです。

于 2013-07-18T03:57:58.960 に答える
3

オブザーバーが廃止されることの問題は、オブザーバー自体が悪かったということではなく、悪用されていたということだと思います。

オブザーバー パターンというこの問題に対する適切な解決策が既に存在する場合、コールバックにあまりにも多くのロジックを追加したり、単にコードを移動してオブザーバーの動作をシミュレートしたりしないように注意してください。

オブザーバーを使用することが理にかなっている場合は、必ずオブザーバーを使用してください。オブザーバー ロジックが SOLID などの適切なコーディング プラクティスに従っていることを確認する必要があることを理解しておいてください。

プロジェクトに追加したい場合は、オブザーバージェムを ruby​​gems で利用できます https://github.com/rails/rails-observers

この短いスレッドを参照してください。完全な包括的な議論ではありませんが、基本的な議論は有効だと思います。 https://github.com/rails/rails-observers/issues/2

于 2014-10-29T14:51:16.687 に答える
2

https://github.com/TiagoCardoso1983/association_observersを試すことができます。Rails 4 (まだリリースされていません) ではまだテストされておらず、さらにコラボレーションが必要ですが、うまくいくかどうかを確認できます。

于 2013-05-17T14:45:45.283 に答える