rails/activesupport/lib/active_support/subscriber.rb
Hartley McGuire 2384eb401e
More Subscriber and LogSubscriber doc uniformity
attach_to was previously [improved][1] to allow defining methods after
attaching instead of having to attach after all methods have been
defined. The docs for Subscriber were [updated][2], but LogSubscriber
docs were not.

This commit copies the attach_to doc changes from Subscriber to
LogSubscriber. In addition, there are other improvements to linking and
applying the monospace formatting that were present in one of Subscriber
or LogSubscriber and are now applied to both. The final change is
updating the description of how flush_all! is used, because its usage
has [changed][3] since this doc was written.

[1]: 34088572270a1dd5a2164b6aa5fc3642cb0479cb
[2]: 25b3f738e4f5b09e4d6a66e1454e697defcdda2c
[3]: 378464a2e47bb849f3351cb8c87366554b7ce74d
2023-04-27 22:16:51 -04:00

146 lines
4.2 KiB
Ruby

# frozen_string_literal: true
require "active_support/notifications"
module ActiveSupport
# = Active Support \Subscriber
#
# <tt>ActiveSupport::Subscriber</tt> is an object set to consume
# ActiveSupport::Notifications. The subscriber dispatches notifications to
# a registered object based on its given namespace.
#
# An example would be an Active Record subscriber responsible for collecting
# statistics about queries:
#
# module ActiveRecord
# class StatsSubscriber < ActiveSupport::Subscriber
# attach_to :active_record
#
# def sql(event)
# Statsd.timing("sql.#{event.payload[:name]}", event.duration)
# end
# end
# end
#
# After configured, whenever a <tt>"sql.active_record"</tt> notification is
# published, it will properly dispatch the event
# (ActiveSupport::Notifications::Event) to the +sql+ method.
#
# We can detach a subscriber as well:
#
# ActiveRecord::StatsSubscriber.detach_from(:active_record)
class Subscriber
class << self
# Attach the subscriber to a namespace.
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.
fetch_public_methods(subscriber, inherit_all).each do |event|
add_event_subscriber(event)
end
end
# Detach the subscriber from a namespace.
def detach_from(namespace, notifier = ActiveSupport::Notifications)
@namespace = namespace
@subscriber = find_attached_subscriber
@notifier = notifier
return unless subscriber
subscribers.delete(subscriber)
# Remove event subscribers of all existing methods on the class.
fetch_public_methods(subscriber, true).each do |event|
remove_event_subscriber(event)
end
# Reset notifier so that event subscribers will not add for new methods added to the class.
@notifier = nil
end
# Adds event subscribers for all new methods added to the class.
def method_added(event)
# Only public methods are added as subscribers, and only if a notifier
# has been set up. This means that subscribers will only be set up for
# classes that call #attach_to.
if public_method_defined?(event) && notifier
add_event_subscriber(event)
end
end
def subscribers
@@subscribers ||= []
end
private
attr_reader :subscriber, :notifier, :namespace
def add_event_subscriber(event) # :doc:
return if invalid_event?(event)
pattern = prepare_pattern(event)
# Don't add multiple subscribers (e.g. if methods are redefined).
return if pattern_subscribed?(pattern)
subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
end
def remove_event_subscriber(event) # :doc:
return if invalid_event?(event)
pattern = prepare_pattern(event)
return unless pattern_subscribed?(pattern)
notifier.unsubscribe(subscriber.patterns[pattern])
subscriber.patterns.delete(pattern)
end
def find_attached_subscriber
subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
end
def invalid_event?(event)
%i{ start finish }.include?(event.to_sym)
end
def prepare_pattern(event)
"#{event}.#{namespace}"
end
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:
def initialize
@patterns = {}
super
end
def call(event)
method = event.name[0, event.name.index(".")]
send(method, event)
end
def publish_event(event) # :nodoc:
method = event.name[0, event.name.index(".")]
send(method, event)
end
end
end