From d287e90870a9832c9d7e9d222881d3a6102bd04d Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sat, 5 Nov 2011 12:02:54 -0700
Subject: implements AS::Notifications.subscribed, which provides subscriptions
 to events while a block runs

---
 activesupport/CHANGELOG.md                        |  2 ++
 activesupport/lib/active_support/notifications.rb | 41 +++++++++++++++++++++++
 activesupport/test/notifications_test.rb          | 20 +++++++++++
 3 files changed, 63 insertions(+)

(limited to 'activesupport')

diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index ad3828d5e8..e03bfdee92 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
 ## Rails 3.2.0 (unreleased) ##
 
+*   ActiveSupport::Notifications.subscribed provides subscriptions to events while a block runs. *fxn*
+
 *   Module#qualified_const_(defined?|get|set) are analogous to the corresponding methods
     in the standard API, but accept qualified constant names. *fxn*
 
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 89afe7b982..719f074948 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -63,6 +63,40 @@ module ActiveSupport
   # and even pass no argument to +subscribe+, in which case you are subscribing
   # to all events.
   #
+  # == Temporary Subscriptions
+  #
+  # Sometimes you do not want to subscribe to an event for the entire life of
+  # the application. There are two ways two unsubscribe.
+  #
+  # === Subscribe While a Block Runs
+  #
+  # You can subscribe to some event temporarily while some block runs. For
+  # example, in
+  #
+  #   callback = lambda {|*args| ... }
+  #   ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+  #     ...
+  #   end
+  #
+  # the callback will be called for all "sql.active_record" events instrumented
+  # during the execution of the block. The callback is unsubscribed automatically
+  # after that.
+  #
+  # === Manual Unsubscription
+  #
+  # The +subscribe+ method returns a subscriber object:
+  #
+  #   subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
+  #     ...
+  #   end
+  #
+  # To prevent that block from being called anymore, just unsubscribe passing
+  # that reference:
+  #
+  #   ActiveSupport::Notifications.unsubscribe(subscriber)
+  #
+  # == Default Queue
+  #
   # Notifications ships with a queue implementation that consumes and publish events
   # to log subscribers in a thread. You can use any queue implementation you want.
   #
@@ -94,6 +128,13 @@ module ActiveSupport
         end
       end
 
+      def subscribed(callback, *args, &block)
+        subscriber = subscribe(*args, &callback)
+        yield
+      ensure
+        unsubscribe(subscriber)
+      end
+
       def unsubscribe(args)
         notifier.unsubscribe(args)
         @instrumenters.clear
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 884ee61547..fc9fa90d07 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -24,6 +24,26 @@ module Notifications
     end
   end
 
+  class SubscribedTest < TestCase
+    def test_subscribed
+      name     = "foo"
+      name2    = name * 2
+      expected = [name, name]
+
+      events   = []
+      callback = lambda {|*_| events << _.first}
+      ActiveSupport::Notifications.subscribed(callback, name) do
+        ActiveSupport::Notifications.instrument(name)
+        ActiveSupport::Notifications.instrument(name2)
+        ActiveSupport::Notifications.instrument(name)
+      end
+      assert_equal expected, events
+
+      ActiveSupport::Notifications.instrument(name)
+      assert_equal expected, events
+    end
+  end
+
   class UnsubscribeTest < TestCase
     def test_unsubscribing_removes_a_subscription
       @notifier.publish :foo
-- 
cgit v1.2.3