mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #32861 from zvkemp/asn-unsubscribe-proxy
use ProxyPattern to match for ActiveSupport::Notifications fanout/unsubscribe
This commit is contained in:
commit
87a5b5d023
5 changed files with 100 additions and 3 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
* Revise `ActiveSupport::Notifications.unsubscribe` to correctly handle Regex or other multiple-pattern subscribers.
|
||||||
|
|
||||||
|
*Zach Kemp*
|
||||||
|
|
||||||
* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
|
* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
|
||||||
|
|
||||||
*Rosa Gutierrez*
|
*Rosa Gutierrez*
|
||||||
|
|
|
@ -153,6 +153,15 @@ module ActiveSupport
|
||||||
#
|
#
|
||||||
# ActiveSupport::Notifications.unsubscribe("render")
|
# ActiveSupport::Notifications.unsubscribe("render")
|
||||||
#
|
#
|
||||||
|
# Subscribers using a regexp or other pattern-matching object will remain subscribed
|
||||||
|
# to all events that match their original pattern, unless those events match a string
|
||||||
|
# passed to `unsubscribe`:
|
||||||
|
#
|
||||||
|
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
||||||
|
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
||||||
|
# subscriber.matches?('render_template.action_view') # => false
|
||||||
|
# subscriber.matches?('render_partial.action_view') # => true
|
||||||
|
#
|
||||||
# == Default Queue
|
# == Default Queue
|
||||||
#
|
#
|
||||||
# Notifications ships with a queue implementation that consumes and publishes events
|
# Notifications ships with a queue implementation that consumes and publishes events
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
require "mutex_m"
|
require "mutex_m"
|
||||||
require "concurrent/map"
|
require "concurrent/map"
|
||||||
|
require "set"
|
||||||
|
|
||||||
module ActiveSupport
|
module ActiveSupport
|
||||||
module Notifications
|
module Notifications
|
||||||
|
@ -39,6 +40,7 @@ module ActiveSupport
|
||||||
when String
|
when String
|
||||||
@string_subscribers[subscriber_or_name].clear
|
@string_subscribers[subscriber_or_name].clear
|
||||||
@listeners_for.delete(subscriber_or_name)
|
@listeners_for.delete(subscriber_or_name)
|
||||||
|
@other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
|
||||||
else
|
else
|
||||||
pattern = subscriber_or_name.try(:pattern)
|
pattern = subscriber_or_name.try(:pattern)
|
||||||
if String === pattern
|
if String === pattern
|
||||||
|
@ -113,11 +115,33 @@ module ActiveSupport
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Matcher #:nodoc:
|
||||||
|
attr_reader :pattern, :exclusions
|
||||||
|
|
||||||
|
def self.wrap(pattern)
|
||||||
|
return pattern if String === pattern
|
||||||
|
new(pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(pattern)
|
||||||
|
@pattern = pattern
|
||||||
|
@exclusions = Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe!(name)
|
||||||
|
exclusions << -name if pattern === name
|
||||||
|
end
|
||||||
|
|
||||||
|
def ===(name)
|
||||||
|
pattern === name && !exclusions.include?(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class Evented #:nodoc:
|
class Evented #:nodoc:
|
||||||
attr_reader :pattern
|
attr_reader :pattern
|
||||||
|
|
||||||
def initialize(pattern, delegate)
|
def initialize(pattern, delegate)
|
||||||
@pattern = pattern
|
@pattern = Matcher.wrap(pattern)
|
||||||
@delegate = delegate
|
@delegate = delegate
|
||||||
@can_publish = delegate.respond_to?(:publish)
|
@can_publish = delegate.respond_to?(:publish)
|
||||||
end
|
end
|
||||||
|
@ -137,11 +161,15 @@ module ActiveSupport
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribed_to?(name)
|
def subscribed_to?(name)
|
||||||
@pattern === name
|
pattern === name
|
||||||
end
|
end
|
||||||
|
|
||||||
def matches?(name)
|
def matches?(name)
|
||||||
@pattern && @pattern === name
|
pattern && pattern === name
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribe!(name)
|
||||||
|
pattern.unsubscribe!(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -204,6 +232,10 @@ module ActiveSupport
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unsubscribe!(*)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
alias :matches? :===
|
alias :matches? :===
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,6 +84,39 @@ module ActiveSupport
|
||||||
[:finish, "hi", 1, {}]
|
[:finish, "hi", 1, {}]
|
||||||
], listener.events
|
], listener.events
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_listen_to_regexp
|
||||||
|
notifier = Fanout.new
|
||||||
|
listener = Listener.new
|
||||||
|
notifier.subscribe(/[a-z]*.world/, listener)
|
||||||
|
notifier.start("hi.world", 1, {})
|
||||||
|
notifier.finish("hi.world", 2, {})
|
||||||
|
notifier.start("hello.world", 1, {})
|
||||||
|
notifier.finish("hello.world", 2, {})
|
||||||
|
|
||||||
|
assert_equal [
|
||||||
|
[:start, "hi.world", 1, {}],
|
||||||
|
[:finish, "hi.world", 2, {}],
|
||||||
|
[:start, "hello.world", 1, {}],
|
||||||
|
[:finish, "hello.world", 2, {}]
|
||||||
|
], listener.events
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_listen_to_regexp_with_exclusions
|
||||||
|
notifier = Fanout.new
|
||||||
|
listener = Listener.new
|
||||||
|
notifier.subscribe(/[a-z]*.world/, listener)
|
||||||
|
notifier.unsubscribe("hi.world")
|
||||||
|
notifier.start("hi.world", 1, {})
|
||||||
|
notifier.finish("hi.world", 2, {})
|
||||||
|
notifier.start("hello.world", 1, {})
|
||||||
|
notifier.finish("hello.world", 2, {})
|
||||||
|
|
||||||
|
assert_equal [
|
||||||
|
[:start, "hello.world", 1, {}],
|
||||||
|
[:finish, "hello.world", 2, {}]
|
||||||
|
], listener.events
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -128,6 +128,25 @@ module Notifications
|
||||||
assert_equal [["named.subscription", :foo], ["named.subscription", :foo]], @events
|
assert_equal [["named.subscription", :foo], ["named.subscription", :foo]], @events
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_unsubscribing_by_name_leaves_regexp_matched_subscriptions
|
||||||
|
@matched_events = []
|
||||||
|
@notifier.subscribe(/subscription/) { |*args| @matched_events << event(*args) }
|
||||||
|
@notifier.publish("named.subscription", :before)
|
||||||
|
@notifier.wait
|
||||||
|
[@events, @named_events, @matched_events].each do |collector|
|
||||||
|
assert_includes(collector, ["named.subscription", :before])
|
||||||
|
end
|
||||||
|
@notifier.unsubscribe("named.subscription")
|
||||||
|
@notifier.publish("named.subscription", :after)
|
||||||
|
@notifier.publish("other.subscription", :after)
|
||||||
|
@notifier.wait
|
||||||
|
assert_includes(@events, ["named.subscription", :after])
|
||||||
|
assert_includes(@events, ["other.subscription", :after])
|
||||||
|
assert_includes(@matched_events, ["other.subscription", :after])
|
||||||
|
assert_not_includes(@matched_events, ["named.subscription", :after])
|
||||||
|
assert_not_includes(@named_events, ["named.subscription", :after])
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def event(*args)
|
def event(*args)
|
||||||
args
|
args
|
||||||
|
|
Loading…
Reference in a new issue