Subscriber.attach_to with inherit_all option

This commit is contained in:
Adrianna Chang 2020-09-16 13:37:24 -04:00
parent 82217c2170
commit 387aa8c373
4 changed files with 105 additions and 20 deletions

View File

@ -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*

View File

@ -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

View File

@ -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:

View File

@ -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