cotton-tail 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|