diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 8502795b57..b15f426a03 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,38 @@ +* ActiveSupport::Subscriber#attach_to now accepts an inherit_from: argument. When set to true, + it allows a subscriber to receive events for methods defined in the subscriber's ancestor class(es) + + ```ruby + class ActionControllerSubscriber < ActiveSupport::Subscriber + attach_to :action_controller + + def start_processing(event) + info "Processing by #{event.payload[:controller]}##{event.payload[:action]} as #{format}" + end + + def redirect_to(event) + info { "Redirected to #{event.payload[:location]}" } + end + end + + # We detach ActionControllerSubscriber from the :action_controller namespace so that our CustomActionControllerSubscriber + # can provide its own instrumentation for certain events in the namespace + ActionControllerSubscriber.detach_from(:action_controller) + + class CustomActionControllerSubscriber < ActionControllerSubscriber + attach_to :action_controller, inherit_all: true + + def start_processing(event) + info "A custom response to start_processing events" + end + + # => CustomActionControllerSubscriber will process events for "start_processing.action_controller" notifications + # using its own #start_processing implementation, while retaining ActionControllerSubscriber's instrumentation + # for "redirect_to.action_controller" notifications + end + ``` + + *Adrianna Chang* + * Fix bug to make memcached write_entry expire correctly with unless_exist *Jye Lee* diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 8b9dd1fffe..af90bccebf 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -96,6 +96,11 @@ module ActiveSupport def flush_all! logger.flush if logger.respond_to?(:flush) end + + private + def fetch_public_methods(subscriber, inherit_all) + subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true) + end end def logger diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 1dc5a57313..24f8681af8 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -31,15 +31,16 @@ module ActiveSupport class Subscriber class << self # Attach the subscriber to a namespace. - def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications) + def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false) @namespace = namespace @subscriber = subscriber @notifier = notifier + @inherit_all = inherit_all subscribers << subscriber # Add event subscribers for all existing methods on the class. - subscriber.public_methods(false).each do |event| + fetch_public_methods(subscriber, inherit_all).each do |event| add_event_subscriber(event) end end @@ -55,7 +56,7 @@ module ActiveSupport subscribers.delete(subscriber) # Remove event subscribers of all existing methods on the class. - subscriber.public_methods(false).each do |event| + fetch_public_methods(subscriber, true).each do |event| remove_event_subscriber(event) end @@ -117,6 +118,10 @@ module ActiveSupport def pattern_subscribed?(pattern) subscriber.patterns.key?(pattern) end + + def fetch_public_methods(subscriber, inherit_all) + subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true) + end end attr_reader :patterns # :nodoc: diff --git a/activesupport/test/subscriber_test.rb b/activesupport/test/subscriber_test.rb index ab6f069e98..e01b6976d9 100644 --- a/activesupport/test/subscriber_test.rb +++ b/activesupport/test/subscriber_test.rb @@ -4,8 +4,6 @@ require_relative "abstract_unit" require "active_support/subscriber" class TestSubscriber < ActiveSupport::Subscriber - attach_to :doodle - cattr_reader :events def self.clear @@ -16,23 +14,19 @@ class TestSubscriber < ActiveSupport::Subscriber events << event end + def another_open_party(event) + events << event + end + private def private_party(event) events << event end end -class TestSubscriber2 < ActiveSupport::Subscriber - attach_to :doodle - detach_from :doodle - - cattr_reader :events - - def self.clear - @@events = [] - end - - def open_party(event) +class PartySubscriber < TestSubscriber + def another_open_party(event) + event.payload["processing_class"] = self.class events << event end end @@ -48,31 +42,77 @@ end class SubscriberTest < ActiveSupport::TestCase def setup TestSubscriber.clear - TestSubscriber2.clear end def test_attaches_subscribers + TestSubscriber.attach_to :doodle + ActiveSupport::Notifications.instrument("open_party.doodle") assert_equal "open_party.doodle", TestSubscriber.events.first.name + ensure + TestSubscriber.detach_from :doodle + end + + def test_attaches_subscribers_with_inherit_all_option + PartySubscriber.attach_to :doodle, inherit_all: true + + ActiveSupport::Notifications.instrument("open_party.doodle") + + assert_equal "open_party.doodle", PartySubscriber.events.first.name + ensure + PartySubscriber.detach_from :doodle + end + + def test_attaches_subscribers_with_inherit_all_option_replaces_original_behaviour + PartySubscriber.attach_to :doodle, inherit_all: true + + ActiveSupport::Notifications.instrument("another_open_party.doodle") + + assert_equal 1, PartySubscriber.events.size + + event = PartySubscriber.events.first + assert_equal "another_open_party.doodle", event.name + assert_equal PartySubscriber, event.payload.fetch("processing_class") + ensure + PartySubscriber.detach_from :doodle end def test_attaches_only_one_subscriber + TestSubscriber.attach_to :doodle + ActiveSupport::Notifications.instrument("open_party.doodle") assert_equal 1, TestSubscriber.events.size + ensure + TestSubscriber.detach_from :doodle end def test_does_not_attach_private_methods + TestSubscriber.attach_to :doodle + ActiveSupport::Notifications.instrument("private_party.doodle") + assert_equal [], TestSubscriber.events + ensure + TestSubscriber.detach_from :doodle + end + + def test_detaches_subscribers + TestSubscriber.attach_to :doodle + TestSubscriber.detach_from :doodle + + ActiveSupport::Notifications.instrument("open_party.doodle") + assert_equal [], TestSubscriber.events end - def test_detaches_subscribers + def test_detaches_subscribers_from_inherited_methods + PartySubscriber.attach_to :doodle + PartySubscriber.detach_from :doodle + ActiveSupport::Notifications.instrument("open_party.doodle") - assert_equal [], TestSubscriber2.events - assert_equal 1, TestSubscriber.events.size + assert_equal [], TestSubscriber.events end end