これが、私が ActiveRecord コールバックを好まない理由です。なぜなら、コールバックとは何の関係も持ちたくない場合 (たとえば、コールバック内で外部サービスを呼び出しているため) について心配する必要があるからです。それをスタブします。はい、コールバック内でメソッドをスタブ化することもできますが、それは同じ問題であり、実際には少し悪いです。なぜなら、メソッド内で何もしたくない何かが心配になっているからです。
いつものように、ここには複数のオプションがあります。
私が過去によく使ったオプションの 1 つは、デフォルトでオフにする条件をコールバックに追加することです。したがって、Post クラスは次のようになります。
class Post
before_save :sync_with_store, :if => :syncing_with_store?
def syncing_with_store?; @syncing_with_store; end
attr_writer :syncing_with_store
def sync_with_store
# make an HTTP request or something
end
end
これで、コールバックを本当に呼び出したい場所 (おそらくコントローラー内またはどこでも) で、post.syncing_with_store = true
を呼び出す前に設定できますpost.save
。
このアプローチの欠点は、あなた (およびあなたと協力している他の開発者) が心に留めておかなければならないことであり、これを行う必要があることは実際には明らかではありません。一方、これを忘れても何も悪いことはありません。
別のオプションは、偽のクラスを使用することです。保存時にデータを外部データ ストアにプッシュする Post があるとします。でアクセスできる別のクラス (Pusher など) にプッシュするコードを抽出できますPost.pusher_service
。ただし、デフォルトでは、これは同じインターフェースに応答するが何もしない偽の Pusher クラスに設定されます。以下のようなので:
class Post
class << self
attr_accessor :pusher_service
end
self.pusher_service = FakePostPusher
before_save :sync_with_store
def sync_with_store
self.class.pusher_service.run(self)
end
end
class FakePostPusher
def self.run(post)
new(post).run
end
def initialize(post)
@post = post
end
def run
# do nothing
end
end
class PostPusher < FakePostPusher
def run
# actually make the HTTP request or whatever
end
end
本番環境ファイルでは、Post.pusher_service = Pusher
. 個々のテストまたはテスト ケースでは、Post のサブクラスを作成し、 -- let(:klass) { Class.new(Post) }
-- 設定klass.pusher_service = Pusher
します (そうすれば、永続的に設定して将来のテストに影響を与えることはありません)。
私が実験してきた 3 番目のアプローチは次のとおりです。単純に ActiveRecord コールバックを使用しないでください。これは、 Gary Bernhardt のスクリーンキャスト(ちなみに、これはかなり素晴らしいものです)から拾ったものです。代わりに、投稿を作成する行為をラップするサービス クラスを定義します。何かのようなもの:
class PostCreator
def self.run(attrs={})
new(attrs).run
end
def initialize(attrs={})
@post = Post.new(attrs)
end
def run
if @post.save
make_http_request
return true
else
return false
end
end
def make_http_request
# ...
end
end
この方法PostCreator.run(attrs)
は、Post を経由するのではなく、事実上、投稿を作成する方法です。Post 内で保存をテストするために、コールバックをスタブ化する必要はありません。PostCreator プロセスをテストしたい場合、魔法のようなことは何もありません。必要なメソッドを簡単にスタブ化するか、個別にテストすることができます。(ここでメソッドをスタブ化することは、AR コールバックをスタブ化することと同じであると主張することができますが、何が起こっているのかをより明確にしていると思います。)明らかに、これは投稿の作成のみを処理しますが、投稿の更新についても同じことができます。
とにかく、さまざまなアイデア、あなたの毒を選んでください。