class DramaQueen::Exchange
An Exchange
determines which objects will receive messages from a Producer
. It is merely a wrapper around the routing_key
that is given on initialization, allowing to more easily determine which routing_keys are related, and thus if any subscribers to the related exchanges should get notified in addition to the subscribers to this exchange.
A Exchange
routing_key
is what Producers and Consumers refer to when they’re looking to publish or subscribe. The Exchange
routing_key
can be any object, but some extra semantics & functionality come along if you use DramaQueen’s route_key globbing.
Ruby Objects¶ ↑
First, the simplest case: a routing_key
that is any Ruby object. Producers and subscribers will use this case when observing that Ruby object. related_exchanges
will be empty; all messages published using this routing_key
will only get delivered to consumers subscribing to this Exchange’s routing_key. If you only need this approach, you might be better off just using Ruby’s build-in Observer
library–it accomplishes this, and is much simpler than DramaQueen
.
RouteKey Globbing¶ ↑
Now, the fun stuff: routing key globbing uses period-delimited strings to infer some hierarchy of Exchanges. Using this approach lets you tie together other Exchanges via #related_keys
, thus letting you build some organization/structure into your whole system of routing messages. These globs (somewhat similar to using Dir.glob
for file systems) let your producers and consumers pub/sub to large numbers of topics, yet organize those topics. The structure that you build is up to you. Here’s a contrived example. You could use set up a routing key scheme like:
-
+“my_library.bob.pants”+
-
+“my_library.bob.shirts”+
-
+“my_library.sally.pants”+
-
+“my_library.sally.shirts”+
When producers publish to +“my_library.bob.pants”+, only consumers of +“my_library.bob.pants”+ get notified. If a producer publishes to +“my_library.*.pants”+, consumers of both +“my_library.bob.pants”+ and +“my_library.sally.pants”+ get notifications. Going the other way, if a consumer subscribes to +“my_library.*.pants”+, then when a producer on +“my_library.bob.pants”+, or +“my_library.sally.pants”+, or +“my_library.*.pants”+, that consumer gets all of those messages.
Further, producers and consumers can use a single asterisk at multiple levels: +“*.*.shirts”+ would resolve to both +“my_library.bob.shirts”+ and +“my_library.sally.shirts”+; if there was a +“someone_else.vendor.shirts”+, for example, that would route as well.
Lastly, you can you a double-asterisk to denote that you want everything from that level and deeper. So, +“**”+ would match all existing routing keys, including single-object (non-glob style) keys. +“my_library.**”+ would match all four of our +“my_library”+ keys above.
As you’re devising your routing key scheme, consider naming like you would name classes/modules in a Ruby library: use namespaces to avoid messing others up! Notice the use of +“my_library…”+ above… that was on purpose.
Attributes
@return [Object]
@return [Array]
Public Class Methods
@param [Object] routing_key
# File lib/drama_queen/exchange.rb, line 71 def initialize(routing_key) @routing_key = routing_key @subscribers = [] end
Public Instance Methods
Calls each subscriber’s callback with the given arguments.
@param args
# File lib/drama_queen/exchange.rb, line 105 def notify_with(*args) @subscribers.each do |subscriber| subscriber.call(*args) end end
@param [Object] routing_key
@return [Boolean]
# File lib/drama_queen/exchange.rb, line 78 def routes_to?(routing_key) related_exchanges.include? routing_key end
Private Instance Methods
@param [String] string @return [String]
# File lib/drama_queen/exchange.rb, line 122 def make_matchable(string) matcher = if string =~ %r[\*\*] string.sub(%r[\.?\*\*], '\..+') elsif string =~ %r[\*] string.sub(%r[\*], '[^\.]+') else string end matcher = matcher.gsub('.*', '\.\w+') matcher = "\\A#{matcher}\\z" matcher end
@return [Boolean]
# File lib/drama_queen/exchange.rb, line 114 def routing_key_match?(first, second) self_matcher = make_matchable(second) !!first.match(Regexp.new(self_matcher)) end