drama_queen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/History.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +193 -0
- data/Rakefile +6 -0
- data/drama_queen.gemspec +25 -0
- data/lib/drama_queen.rb +37 -0
- data/lib/drama_queen/consumer.rb +34 -0
- data/lib/drama_queen/exchange.rb +137 -0
- data/lib/drama_queen/producer.rb +32 -0
- data/lib/drama_queen/version.rb +3 -0
- data/spec/acceptance/drama_city_spec.rb +100 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/drama_queen/consumer_spec.rb +49 -0
- data/spec/unit/drama_queen/exchange_spec.rb +252 -0
- data/spec/unit/drama_queen/producer_spec.rb +96 -0
- data/spec/unit/drama_queen_spec.rb +57 -0
- data/test.rb +163 -0
- metadata +116 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: a1cb78ce2e3af6d28e68255b0505b9d704c86ed5
|
|
4
|
+
data.tar.gz: 64f553c705c27367ebc8b17cf6229c589e371b3f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 343d156ca2cbf4d87ba4846c316ce5d3c51763a870d688744ce22982940942f02f2052ae00bae6f0d03fa69b20335545ec35388e3582bd287844c64aa45e6c55
|
|
7
|
+
data.tar.gz: 220dd484df1bc26b0c438b752f5883d27a13fbe61ed14fc21d2f70e84ac2ad09748791cd3fcfe528e438418445ee2b6752792b929c3beca6d593e17bb271481d
|
data/.gitignore
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/History.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Steve Loveless
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# DramaQueen
|
|
2
|
+
|
|
3
|
+
A simple, synchronous pub-sub/observer library that allows objects to observe or
|
|
4
|
+
publish and subscribe to topics.
|
|
5
|
+
|
|
6
|
+
[](https://travis-ci.org/turboladen/drama_queen)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
* Observe Ruby objects and receive updates when they change
|
|
11
|
+
([Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern))
|
|
12
|
+
* Subscribe and publish on a topic or routing key
|
|
13
|
+
([Publish-Subscribe Pattern](http://en.wikipedia.org/wiki/Publish–subscribe_pattern))
|
|
14
|
+
* Synchronous, no threading
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
My initial desire for this library was to replace Ruby's built-in Observer
|
|
20
|
+
module with something that allowed objects to observe specific topics, as
|
|
21
|
+
opposed to simply just observing objects. DramaQueen lets you do both.
|
|
22
|
+
|
|
23
|
+
### Subscribe to an object
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
class MyProducer
|
|
27
|
+
include DramaQueen::Producer
|
|
28
|
+
|
|
29
|
+
def do_stuff
|
|
30
|
+
publish(self, "I did some stuff!", [1, 2, 3])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class MyConsumer
|
|
35
|
+
include DramaQueen::Consumer
|
|
36
|
+
|
|
37
|
+
def initialize(topic)
|
|
38
|
+
subscribe(topic, :call_me!)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def call_me!(message, neat_array)
|
|
42
|
+
puts "He did it! -> #{message}"
|
|
43
|
+
puts "A present for me?! #{neat_array}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
producer = MyProducer.new
|
|
48
|
+
consumer = MyConsumer.new(producer)
|
|
49
|
+
producer.do_stuff
|
|
50
|
+
|
|
51
|
+
# "He did it! -> I did some stuff!"
|
|
52
|
+
# "A present for me?! [1, 2, 3]"
|
|
53
|
+
#=> true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`consumer` subscribes to the `producer` object and `producer` publishes on the
|
|
57
|
+
topic of itself, thus publishing to `consumer`. Also notice that the consumer
|
|
58
|
+
passed in `:call_me!` as the second parameter to `#subscribe`: that registers
|
|
59
|
+
the `#call_me!` method to be called when the producer publishes. We can see
|
|
60
|
+
that when we call `producer.do_stuff`, `consumer.call_me!` gets called by
|
|
61
|
+
the strings that get output to the console. Also notice that `consumer.call_me!`
|
|
62
|
+
gets the same parameters passed to it that are passed in to `producer`'s call
|
|
63
|
+
to `#publish`.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### Subscribe to a topic
|
|
67
|
+
|
|
68
|
+
Subscribing to a topic is no different than subscribing to an object.
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
class ThingsProducer
|
|
72
|
+
include DramaQueen::Producer
|
|
73
|
+
|
|
74
|
+
def do_stuff
|
|
75
|
+
publish('things', "I did some stuff!", [1, 2, 3])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class ThingsConsumer
|
|
80
|
+
include DramaQueen::Consumer
|
|
81
|
+
|
|
82
|
+
def initialize(topic)
|
|
83
|
+
subscribe(topic, :call_me!)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def call_me!(message, neat_array)
|
|
87
|
+
puts "He did it! -> #{message}"
|
|
88
|
+
puts "A present for me?! #{neat_array}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
producer = ThingsProducer.new
|
|
93
|
+
consumer = ThingsConsumer.new('things')
|
|
94
|
+
producer.do_stuff
|
|
95
|
+
|
|
96
|
+
# "He did it! -> I did some stuff!"
|
|
97
|
+
# "A present for me?! [1, 2, 3]"
|
|
98
|
+
#=> true
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Moral of the story: The topic that you publish/subscribe on can be any Ruby
|
|
102
|
+
object--how you use this is up to you.
|
|
103
|
+
|
|
104
|
+
### Routing Key Matching
|
|
105
|
+
|
|
106
|
+
Thus far, we've talked about subscribing to a "topic".
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
class A
|
|
110
|
+
include DramaQueen::Consumer
|
|
111
|
+
|
|
112
|
+
def initialize
|
|
113
|
+
subscribe 'root.*.children', :call_me
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def call_me(*args)
|
|
117
|
+
puts "A got called with args: #{args}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class B
|
|
122
|
+
include DramaQueen::Producer
|
|
123
|
+
|
|
124
|
+
def do_stuff
|
|
125
|
+
publish 'root.parent', 1, 2, 3
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class C
|
|
130
|
+
include DramaQueen::Producer
|
|
131
|
+
|
|
132
|
+
def do_stuff
|
|
133
|
+
publish 'root.parent.children', 1, 2, 3
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
a = A.new
|
|
138
|
+
b = B.new
|
|
139
|
+
c = C.new
|
|
140
|
+
|
|
141
|
+
b.do_stuff
|
|
142
|
+
|
|
143
|
+
# (A does not get called)
|
|
144
|
+
c.do_stuff
|
|
145
|
+
# "A got called with args: 1, 2, 3
|
|
146
|
+
#=> true
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
See {DramaQueen::Exchange} for more.
|
|
150
|
+
|
|
151
|
+
### Synchronous Only!
|
|
152
|
+
|
|
153
|
+
DramaQueen does not use any threading or fancy asynchronous stuff. When your
|
|
154
|
+
producer publishes, you can expect your subscribers to receive the notifications:
|
|
155
|
+
|
|
156
|
+
* in the order the publishing was triggered, and
|
|
157
|
+
* in the order that consumers subscribed.
|
|
158
|
+
|
|
159
|
+
This is intentional. If you want publishing to take place in a thread, you can
|
|
160
|
+
thread your call to #publish in your code.
|
|
161
|
+
|
|
162
|
+
### Application-wide
|
|
163
|
+
|
|
164
|
+
DramaQueen stores all of its exchanges in a singleton, which is thus accessible
|
|
165
|
+
throughout your app/library. This makes it possible to subscribe to a topic in
|
|
166
|
+
one object and publish to that topic in another unrelated object from anywhere
|
|
167
|
+
in your code. And while you have access to `DramaQueen`, you shouldn't ever
|
|
168
|
+
need to deal with it directly--just use `#publish` and `#subscribe` and let it
|
|
169
|
+
do its thing.
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
## Installation
|
|
173
|
+
|
|
174
|
+
Add this line to your application's Gemfile:
|
|
175
|
+
|
|
176
|
+
gem 'drama_queen'
|
|
177
|
+
|
|
178
|
+
And then execute:
|
|
179
|
+
|
|
180
|
+
$ bundle
|
|
181
|
+
|
|
182
|
+
Or install it yourself as:
|
|
183
|
+
|
|
184
|
+
$ gem install drama_queen
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Contributing
|
|
188
|
+
|
|
189
|
+
1. Fork it
|
|
190
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
191
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
192
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
193
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/drama_queen.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'drama_queen/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'drama_queen'
|
|
8
|
+
spec.version = DramaQueen::VERSION
|
|
9
|
+
spec.author = 'Steve Loveless'
|
|
10
|
+
spec.email = 'steve.loveless@gmail.com'
|
|
11
|
+
spec.description = %q{A simple, non-threaded, local-object pub-sub/observer
|
|
12
|
+
with the ability to pub-sub on topics. Topics can be any Ruby object.}
|
|
13
|
+
spec.summary = spec.description
|
|
14
|
+
spec.homepage = 'https://githb.com/turboladen/drama_queen'
|
|
15
|
+
spec.license = 'MIT'
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files`.split($/)
|
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
20
|
+
spec.require_paths = %w[lib]
|
|
21
|
+
|
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
|
23
|
+
spec.add_development_dependency 'rake'
|
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0.beta1'
|
|
25
|
+
end
|
data/lib/drama_queen.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative 'drama_queen/version'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# This is the singleton that maintains the list of active exchanges.
|
|
5
|
+
module DramaQueen
|
|
6
|
+
|
|
7
|
+
# The list of all exchanges that DramaQueen knows about. This is updated
|
|
8
|
+
# by DramaQueen::Consumers as they subscribe to topics.
|
|
9
|
+
#
|
|
10
|
+
# @return [Array<DramaQueen::Exchange>]
|
|
11
|
+
def self.exchanges
|
|
12
|
+
@exchanges ||= Array.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Finds the DramaQueen::Exchange for the given +routing_key+.
|
|
16
|
+
#
|
|
17
|
+
# @param [Object] routing_key
|
|
18
|
+
# @return [DramaQueen::Exchange]
|
|
19
|
+
def self.exchange_for(routing_key)
|
|
20
|
+
exchanges.find do |exchange|
|
|
21
|
+
exchange.routing_key == routing_key
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param [Object] routing_key
|
|
26
|
+
# @return [Boolean]
|
|
27
|
+
def self.routes_to?(routing_key)
|
|
28
|
+
!!exchange_for(routing_key)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Removes all exchanges from the exchanges list.
|
|
32
|
+
#
|
|
33
|
+
# @return [Array]
|
|
34
|
+
def self.unsubscribe_all
|
|
35
|
+
@exchanges = Array.new
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require_relative '../drama_queen'
|
|
2
|
+
require_relative 'exchange'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module DramaQueen
|
|
6
|
+
|
|
7
|
+
# A +consumer+ is simply an object that receives messages from a +producer+.
|
|
8
|
+
# In order to sign up to receive messages from a producer, the consumer
|
|
9
|
+
# subscribes to a +topic+ that they're interested in. When the producer
|
|
10
|
+
# +publish+es something on that topic, the consumer's +callback+ will get
|
|
11
|
+
# called.
|
|
12
|
+
module Consumer
|
|
13
|
+
|
|
14
|
+
# @param [Object] routing_key The routing key that represents the Exchange
|
|
15
|
+
# to subscribe to.
|
|
16
|
+
# @param [Symbol,Method,Proc] callback If given a Symbol, this will be
|
|
17
|
+
# converted to a Method that gets called on the includer of Consumer. If
|
|
18
|
+
# +callback+ is not a Symbol, it simply must just respond to +call+.
|
|
19
|
+
def subscribe(routing_key, callback)
|
|
20
|
+
callable_callback = callback.is_a?(Symbol) ? method(callback) : callback
|
|
21
|
+
|
|
22
|
+
unless callable_callback.respond_to?(:call)
|
|
23
|
+
raise "The given callback is not a Symbol, nor responds to #call: #{callback}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless DramaQueen.routes_to? routing_key
|
|
27
|
+
DramaQueen.exchanges << Exchange.new(routing_key)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
exchange = DramaQueen.exchange_for(routing_key)
|
|
31
|
+
exchange.subscribers << callable_callback
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
module DramaQueen
|
|
2
|
+
|
|
3
|
+
# An Exchange determines which objects will receive messages from a Producer.
|
|
4
|
+
# It is merely a wrapper around the +routing_key+ that is given on
|
|
5
|
+
# initialization, allowing to more easily determine which routing_keys are
|
|
6
|
+
# related, and thus if any subscribers to the related exchanges should get
|
|
7
|
+
# notified in addition to the subscribers to this exchange.
|
|
8
|
+
#
|
|
9
|
+
# A Exchange routing_key is what Producers and Consumers refer to when they're
|
|
10
|
+
# looking to publish or subscribe. The Exchange +routing_key+ can be any
|
|
11
|
+
# object, but some extra semantics & functionality come along if you use
|
|
12
|
+
# DramaQueen's route_key globbing.
|
|
13
|
+
#
|
|
14
|
+
# === Ruby Objects
|
|
15
|
+
#
|
|
16
|
+
# First, the simplest case: a +routing_key+ that is any Ruby object.
|
|
17
|
+
# Producers and subscribers will use this case when observing that Ruby
|
|
18
|
+
# object. #related_exchanges will be empty; all messages published using this
|
|
19
|
+
# routing_key will only get delivered to consumers subscribing to this
|
|
20
|
+
# Exchange's routing_key. If you only need this approach, you might be better
|
|
21
|
+
# off just using Ruby's build-in +Observer+ library--it accomplishes this, and
|
|
22
|
+
# is much simpler than DramaQueen.
|
|
23
|
+
#
|
|
24
|
+
# === RouteKey Globbing
|
|
25
|
+
#
|
|
26
|
+
# Now, the fun stuff: routing key globbing uses period-delimited strings to
|
|
27
|
+
# infer some hierarchy of Exchanges. Using this approach lets you tie
|
|
28
|
+
# together other Exchanges via +#related_keys+, thus letting you build
|
|
29
|
+
# some organization/structure into your whole system of routing messages.
|
|
30
|
+
# These globs (somewhat similar to using +Dir.glob+ for file systems) let your
|
|
31
|
+
# producers and consumers pub/sub to large numbers of topics, yet organize
|
|
32
|
+
# those topics. The structure that you build is up to you. Here's a
|
|
33
|
+
# contrived example. You could use set up a routing key scheme like:
|
|
34
|
+
#
|
|
35
|
+
# * +"my_library.bob.pants"+
|
|
36
|
+
# * +"my_library.bob.shirts"+
|
|
37
|
+
# * +"my_library.sally.pants"+
|
|
38
|
+
# * +"my_library.sally.shirts"+
|
|
39
|
+
#
|
|
40
|
+
# When producers publish to +"my_library.bob.pants"+, only consumers of
|
|
41
|
+
# +"my_library.bob.pants"+ get notified. If a producer publishes to
|
|
42
|
+
# +"my_library.*.pants"+, consumers of both +"my_library.bob.pants"+ _and_
|
|
43
|
+
# +"my_library.sally.pants"+ get notifications. Going the other way, if
|
|
44
|
+
# a consumer subscribes to +"my_library.*.pants"+, then when a producer on
|
|
45
|
+
# +"my_library.bob.pants"+, _or_ +"my_library.sally.pants"+, _or_
|
|
46
|
+
# +"my_library.*.pants"+, that consumer gets all of those messages.
|
|
47
|
+
#
|
|
48
|
+
# Further, producers and consumers can use a single asterisk at multiple
|
|
49
|
+
# levels: +"\*.\*.shirts"+ would resolve to both +"my_library.bob.shirts"+ and
|
|
50
|
+
# +"my_library.sally.shirts"+; if there was a +"someone_else.vendor.shirts"+,
|
|
51
|
+
# for example, that would route as well.
|
|
52
|
+
#
|
|
53
|
+
# Lastly, you can you a double-asterisk to denote that you want everything
|
|
54
|
+
# from that level and deeper. So, +"**"+ would match _all_ existing routing
|
|
55
|
+
# keys, including single-object (non-glob style) keys. +"my_library.**"+
|
|
56
|
+
# would match all four of our +"my_library"+ keys above.
|
|
57
|
+
#
|
|
58
|
+
# As you're devising your routing key scheme, consider naming like you would
|
|
59
|
+
# name classes/modules in a Ruby library: use namespaces to avoid messing
|
|
60
|
+
# others up! Notice the use of +"my_library..."+ above... that was on
|
|
61
|
+
# purpose.
|
|
62
|
+
class Exchange
|
|
63
|
+
|
|
64
|
+
# @return [Object]
|
|
65
|
+
attr_reader :routing_key
|
|
66
|
+
|
|
67
|
+
# @return [Array]
|
|
68
|
+
attr_reader :subscribers
|
|
69
|
+
|
|
70
|
+
# @param [Object] routing_key
|
|
71
|
+
def initialize(routing_key)
|
|
72
|
+
@routing_key = routing_key
|
|
73
|
+
@subscribers = []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @param [Object] routing_key
|
|
77
|
+
# @return [Boolean]
|
|
78
|
+
def routes_to?(routing_key)
|
|
79
|
+
related_exchanges.include? routing_key
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# All other DramaQueen::Exchange objects that the current Exchange routes
|
|
83
|
+
# to.
|
|
84
|
+
#
|
|
85
|
+
# @return [Array<DramaQueen::Exchange]
|
|
86
|
+
def related_exchanges
|
|
87
|
+
return DramaQueen.exchanges if self.routing_key == '**'
|
|
88
|
+
|
|
89
|
+
DramaQueen.exchanges.find_all do |exchange|
|
|
90
|
+
next if exchange.routing_key == '**'
|
|
91
|
+
next if exchange.routing_key == self.routing_key
|
|
92
|
+
|
|
93
|
+
if self.routing_key.is_a?(String) && exchange.routing_key.is_a?(String)
|
|
94
|
+
routing_key_match?(exchange.routing_key, self.routing_key) ||
|
|
95
|
+
routing_key_match?(self.routing_key, exchange.routing_key)
|
|
96
|
+
else
|
|
97
|
+
exchange == self
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Calls each subscriber's callback with the given arguments.
|
|
103
|
+
#
|
|
104
|
+
# @param args
|
|
105
|
+
def notify_with(*args)
|
|
106
|
+
@subscribers.each do |subscriber|
|
|
107
|
+
subscriber.call(*args)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# @return [Boolean]
|
|
114
|
+
def routing_key_match?(first, second)
|
|
115
|
+
self_matcher = make_matchable(second)
|
|
116
|
+
|
|
117
|
+
!!first.match(Regexp.new(self_matcher))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @param [String] string
|
|
121
|
+
# @return [String]
|
|
122
|
+
def make_matchable(string)
|
|
123
|
+
matcher = if string =~ %r[\*\*]
|
|
124
|
+
string.sub(%r[\.?\*\*], '\..+')
|
|
125
|
+
elsif string =~ %r[\*]
|
|
126
|
+
string.sub(%r[\*], '[^\.]+')
|
|
127
|
+
else
|
|
128
|
+
string
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
matcher = matcher.gsub('.*', '\.\w+')
|
|
132
|
+
matcher = "\\A#{matcher}\\z"
|
|
133
|
+
|
|
134
|
+
matcher
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|