cotton-tail 0.5.0 → 0.6.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.rspec +1 -0
- data/Gemfile.lock +2 -2
- data/examples/app.rb +5 -0
- data/examples/messages/send.kitten.to.tom +12 -0
- data/lib/cotton_tail.rb +3 -14
- data/lib/cotton_tail/dsl/queue.rb +5 -5
- data/lib/cotton_tail/message_properties.rb +18 -0
- data/lib/cotton_tail/middleware/router.rb +28 -8
- data/lib/cotton_tail/queue/bunny.rb +3 -2
- data/lib/cotton_tail/request.rb +35 -0
- data/lib/cotton_tail/route.rb +29 -17
- data/lib/cotton_tail/route_segment.rb +70 -0
- data/lib/cotton_tail/version.rb +1 -1
- metadata +6 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a096975de51e023b80e464904906b993f74192d556b868810c752571697d8de4
|
4
|
+
data.tar.gz: 70cec1e974a049e58ca92370e646cf3adddf138bd03b4cc101bfa2956c0c5fcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5334e9b79d5d5762c5f98066866c5e08cd54a4ed0b051dfc6a3b68d3b1bee80a4800b734edc063e5043a2a2a56527997c65e6e1fb5d7ed9b7091e51ca137062b
|
7
|
+
data.tar.gz: d936c74e21b1570b283b513e8b5921215df7c1ebc31b95db9a57bb64cab742208cccf65c294c13a078d8d80b2a6cecc027ebf5a895315a7abb0a36904ff7d573
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.rspec
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cotton-tail (0.
|
4
|
+
cotton-tail (0.6.0)
|
5
5
|
bunny (~> 2.12)
|
6
6
|
ibsciss-middleware (~> 0.4.2)
|
7
7
|
|
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
ast (2.4.0)
|
13
13
|
benchmark-perf (0.4.0)
|
14
14
|
benchmark-trend (0.2.0)
|
15
|
-
bunny (2.
|
15
|
+
bunny (2.13.0)
|
16
16
|
amq-protocol (~> 2.3, >= 2.3.0)
|
17
17
|
diff-lcs (1.3)
|
18
18
|
effin_utf8 (1.0)
|
data/examples/app.rb
CHANGED
@@ -25,6 +25,11 @@ app.routes.draw do
|
|
25
25
|
puts request: request
|
26
26
|
puts response: response
|
27
27
|
end
|
28
|
+
|
29
|
+
handle 'send.*:gift.to.*:name' do |_env, request, _response|
|
30
|
+
gift, name = request.route_params.values_at('gift', 'name')
|
31
|
+
puts "#{gift} sent to #{name}!"
|
32
|
+
end
|
28
33
|
end
|
29
34
|
|
30
35
|
queue 'require_ack_queue', exclusive: true, manual_ack: true do
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
read -r -d '' MESSAGE << EOM
|
4
|
+
{
|
5
|
+
"properties":{},
|
6
|
+
"routing_key":"send.kitten.to.tom",
|
7
|
+
"payload": "ignored",
|
8
|
+
"payload_encoding": "string"
|
9
|
+
}
|
10
|
+
EOM
|
11
|
+
|
12
|
+
curl -i -XPOST -d"${MESSAGE}" http://guest:guest@localhost:15672/api/exchanges/%2f/amq.topic/publish
|
data/lib/cotton_tail.rb
CHANGED
@@ -7,26 +7,15 @@ module CottonTail
|
|
7
7
|
autoload :App, 'cotton_tail/app'
|
8
8
|
autoload :Configuration, 'cotton_tail/configuration'
|
9
9
|
autoload :DSL, 'cotton_tail/dsl'
|
10
|
+
autoload :MessageProperties, 'cotton_tail/message_properties'
|
10
11
|
autoload :Middleware, 'cotton_tail/middleware'
|
11
12
|
autoload :Queue, 'cotton_tail/queue'
|
13
|
+
autoload :Request, 'cotton_tail/request'
|
12
14
|
autoload :Route, 'cotton_tail/route'
|
15
|
+
autoload :RouteSegment, 'cotton_tail/route_segment'
|
13
16
|
autoload :Router, 'cotton_tail/router'
|
14
17
|
autoload :Version, 'cotton_tail/version'
|
15
18
|
|
16
|
-
Request = Struct.new(:delivery_info, :properties, :payload) do
|
17
|
-
def routing_key
|
18
|
-
delivery_info[:routing_key]
|
19
|
-
end
|
20
|
-
|
21
|
-
def delivery_tag
|
22
|
-
delivery_info[:delivery_tag]
|
23
|
-
end
|
24
|
-
|
25
|
-
def channel
|
26
|
-
delivery_info[:channel]
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
19
|
Response = Struct.new(:body)
|
31
20
|
|
32
21
|
class RouteConflictError < StandardError
|
@@ -10,9 +10,9 @@ module CottonTail
|
|
10
10
|
@context = context
|
11
11
|
end
|
12
12
|
|
13
|
-
def handle(
|
14
|
-
bind
|
15
|
-
@context.handle(
|
13
|
+
def handle(pattern, handler = nil, &block)
|
14
|
+
bind pattern
|
15
|
+
@context.handle(pattern, handler, &block)
|
16
16
|
end
|
17
17
|
|
18
18
|
def topic(routing_prefix, &block)
|
@@ -20,10 +20,10 @@ module CottonTail
|
|
20
20
|
topic.instance_eval(&block)
|
21
21
|
end
|
22
22
|
|
23
|
-
def bind(
|
23
|
+
def bind(pattern)
|
24
24
|
return unless @queue.respond_to?(:bind)
|
25
25
|
|
26
|
-
@queue.bind
|
26
|
+
@queue.bind pattern
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CottonTail
|
4
|
+
# Wrapper around Bunny MessageProperties, used to supply route params
|
5
|
+
class MessageProperties < Bunny::MessageProperties
|
6
|
+
def merge(properties)
|
7
|
+
self.class.new(@properties.merge(properties))
|
8
|
+
end
|
9
|
+
|
10
|
+
def route_params
|
11
|
+
@properties[:route_params]
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
to_h == other.to_h
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -15,30 +15,50 @@ module CottonTail
|
|
15
15
|
|
16
16
|
def call(message)
|
17
17
|
env, req, = message
|
18
|
-
@app.call [env, req, response(
|
18
|
+
@app.call [env, req, response(*message)]
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
23
|
+
def response(env, req, res)
|
24
|
+
routing_key = req.routing_key
|
25
|
+
handler = lookup_handler(routing_key)
|
26
|
+
route = lookup_route(routing_key)
|
27
|
+
req = add_route_params(req, route) if route_params?(route, routing_key)
|
26
28
|
|
27
|
-
|
28
|
-
CottonTail::Response.new handler(routing_key).call(message)
|
29
|
+
CottonTail::Response.new handler.call([env, req, res])
|
29
30
|
end
|
30
31
|
|
31
32
|
def routes(routing_key)
|
32
33
|
handlers.keys.select { |route| route.match? routing_key }
|
33
34
|
end
|
34
35
|
|
35
|
-
def
|
36
|
+
def lookup_handler(routing_key)
|
37
|
+
handlers[lookup_route(routing_key)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def lookup_route(routing_key)
|
36
41
|
route, *conflicts = routes(routing_key)
|
37
42
|
raise UndefinedRouteError if route.nil?
|
38
43
|
|
39
44
|
raise RouteConflictError unless conflicts.empty?
|
40
45
|
|
41
|
-
|
46
|
+
route
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_route_params(req, route)
|
50
|
+
delivery_info, properties, payload = req.to_a
|
51
|
+
Request.new(
|
52
|
+
delivery_info,
|
53
|
+
properties.merge(
|
54
|
+
route_params: route.extract_params(req.routing_key)
|
55
|
+
),
|
56
|
+
payload
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def route_params?(route, routing_key)
|
61
|
+
route.extract_params(routing_key) == {} || true
|
42
62
|
end
|
43
63
|
end
|
44
64
|
end
|
@@ -30,11 +30,12 @@ module CottonTail
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def pop
|
33
|
-
|
33
|
+
delivery_info, properties, payload = super
|
34
|
+
Request.new(delivery_info, MessageProperties.new(properties.to_h), payload)
|
34
35
|
end
|
35
36
|
|
36
37
|
def bind(routing_key)
|
37
|
-
source.bind('amq.topic', routing_key: routing_key)
|
38
|
+
source.bind('amq.topic', routing_key: Route.new(routing_key).binding)
|
38
39
|
end
|
39
40
|
|
40
41
|
private
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CottonTail
|
4
|
+
# Value object wrapper for Bunny Message
|
5
|
+
class Request
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :delivery_info, :properties, :payload
|
9
|
+
|
10
|
+
def initialize(delivery_info, properties, payload)
|
11
|
+
@delivery_info = delivery_info
|
12
|
+
@properties = properties
|
13
|
+
@payload = payload
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_a
|
17
|
+
[delivery_info, properties, payload]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
{
|
22
|
+
delivery_info: delivery_info,
|
23
|
+
properties: properties,
|
24
|
+
payload: payload
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
to_h == other.to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def_delegators :delivery_info, :routing_key, :delivery_tag, :channel
|
33
|
+
def_delegators :properties, :route_params
|
34
|
+
end
|
35
|
+
end
|
data/lib/cotton_tail/route.rb
CHANGED
@@ -1,36 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CottonTail
|
4
|
-
# Route
|
5
|
-
class Route
|
6
|
-
attr_reader :pattern
|
7
|
-
|
4
|
+
# Route pattern matcher
|
5
|
+
class Route < SimpleDelegator
|
8
6
|
def initialize(pattern)
|
9
7
|
@pattern = pattern
|
8
|
+
super build_regex
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
13
|
-
return
|
11
|
+
def extract_params(routing_key)
|
12
|
+
return {} unless match? routing_key
|
14
13
|
|
15
|
-
|
14
|
+
match(routing_key).named_captures
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
17
|
+
def binding
|
18
|
+
segments.map(&:binding).join('.')
|
20
19
|
end
|
21
20
|
|
22
21
|
private
|
23
22
|
|
24
|
-
def
|
25
|
-
@
|
23
|
+
def explode
|
24
|
+
@pattern.split('.').map(&RouteSegment.method(:new))
|
25
|
+
end
|
26
|
+
|
27
|
+
def collapse
|
28
|
+
segments.zip(separators).join
|
29
|
+
end
|
30
|
+
|
31
|
+
def segments
|
32
|
+
@segments ||= explode
|
33
|
+
end
|
34
|
+
|
35
|
+
def separators
|
36
|
+
separators = segments.each_with_index.map do |segment, idx|
|
37
|
+
[Regexp.escape('.')].tap do |sep|
|
38
|
+
sep << '?' if segment.hash? && idx.zero?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
separators.map(&:join)[0..-2]
|
26
42
|
end
|
27
43
|
|
28
|
-
def build_regex
|
29
|
-
|
30
|
-
'^',
|
31
|
-
pattern.gsub('*', '([^.]+)').gsub(/\.?#\.?/, '([^.]{0,}\.?)+'),
|
32
|
-
'$'
|
33
|
-
].join
|
44
|
+
def build_regex
|
45
|
+
Regexp.new "^#{collapse}$"
|
34
46
|
end
|
35
47
|
end
|
36
48
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CottonTail
|
4
|
+
# RouteSegment implements the pattern matching for route segments
|
5
|
+
class RouteSegment < SimpleDelegator
|
6
|
+
def initialize(value)
|
7
|
+
@value = value
|
8
|
+
super Regexp.new definition(value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def star?
|
12
|
+
/^#{STAR}|#{NAMED_STAR}$/.match? @value
|
13
|
+
end
|
14
|
+
|
15
|
+
def hash?
|
16
|
+
/^#{HASH}|#{NAMED_HASH}$/.match? @value
|
17
|
+
end
|
18
|
+
|
19
|
+
def binding
|
20
|
+
return '*' if star?
|
21
|
+
|
22
|
+
return '#' if hash?
|
23
|
+
|
24
|
+
@value
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
TRANSFORM = ->(val, func) { func.call(val) }
|
30
|
+
|
31
|
+
def definition(value)
|
32
|
+
transformers.reduce(value, &TRANSFORM)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Converts named route segment to Regexp named capture group
|
36
|
+
# "#:foo" -> "(?<foo>.+)"
|
37
|
+
def sub_named_group_wildcard(pattern)
|
38
|
+
pattern.gsub(NAMED_HASH, '(?<\1>.+)')
|
39
|
+
end
|
40
|
+
|
41
|
+
# Converts named route segment to Regexp named capture group
|
42
|
+
# "*:foo" -> "(?<foo>[^.]+)"
|
43
|
+
def sub_named_single_wildcard(pattern)
|
44
|
+
pattern.gsub(NAMED_STAR, '(?<\1>[^.]+)')
|
45
|
+
end
|
46
|
+
|
47
|
+
def sub_single_wildcard(pattern)
|
48
|
+
pattern.gsub(STAR, '([^.]+)')
|
49
|
+
end
|
50
|
+
|
51
|
+
def sub_multi_wildcard(pattern)
|
52
|
+
pattern.gsub(HASH, '([^.]{0,}\.?)+')
|
53
|
+
end
|
54
|
+
|
55
|
+
def transformers
|
56
|
+
[
|
57
|
+
method(:sub_named_group_wildcard),
|
58
|
+
method(:sub_named_single_wildcard),
|
59
|
+
method(:sub_single_wildcard),
|
60
|
+
method(:sub_multi_wildcard)
|
61
|
+
]
|
62
|
+
end
|
63
|
+
|
64
|
+
STAR = /\*/.freeze
|
65
|
+
HASH = /#/.freeze
|
66
|
+
NAMED = /:(\w+)/.freeze
|
67
|
+
NAMED_STAR = /#{STAR}#{NAMED}/.freeze
|
68
|
+
NAMED_HASH = /#{HASH}#{NAMED}/.freeze
|
69
|
+
end
|
70
|
+
end
|
data/lib/cotton_tail/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotton-tail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Brennan
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
fXe/xr/Sc+2wCjHPVE2J+auN5hk3KCp1I4s2fKqyLIwyhTEF3shuYfCpC8rt/YdN
|
31
31
|
cy9/lg5LCI3OvakzxL4Xt1Sq4h/xJZ06ydTVJ1wxfk6BXHrg
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date:
|
33
|
+
date: 2019-01-16 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: bunny
|
@@ -205,6 +205,7 @@ files:
|
|
205
205
|
- examples/messages/intercept.with.middleware
|
206
206
|
- examples/messages/say.goodbye
|
207
207
|
- examples/messages/say.hello
|
208
|
+
- examples/messages/send.kitten.to.tom
|
208
209
|
- lib/cotton_tail.rb
|
209
210
|
- lib/cotton_tail/app.rb
|
210
211
|
- lib/cotton_tail/configuration.rb
|
@@ -212,6 +213,7 @@ files:
|
|
212
213
|
- lib/cotton_tail/dsl/queue.rb
|
213
214
|
- lib/cotton_tail/dsl/routes.rb
|
214
215
|
- lib/cotton_tail/dsl/topic.rb
|
216
|
+
- lib/cotton_tail/message_properties.rb
|
215
217
|
- lib/cotton_tail/middleware.rb
|
216
218
|
- lib/cotton_tail/middleware/router.rb
|
217
219
|
- lib/cotton_tail/queue.rb
|
@@ -219,7 +221,9 @@ files:
|
|
219
221
|
- lib/cotton_tail/queue/memory.rb
|
220
222
|
- lib/cotton_tail/queue/reader.rb
|
221
223
|
- lib/cotton_tail/queue/supervisor.rb
|
224
|
+
- lib/cotton_tail/request.rb
|
222
225
|
- lib/cotton_tail/route.rb
|
226
|
+
- lib/cotton_tail/route_segment.rb
|
223
227
|
- lib/cotton_tail/version.rb
|
224
228
|
homepage: https://github.com/jamesBrennan/cotton-tail
|
225
229
|
licenses:
|
metadata.gz.sig
CHANGED
Binary file
|