cotton-tail 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b9caf210e7f688e34cc64e7e212d1290692a0559ffaa9f72a948c416dda3b7d
4
- data.tar.gz: a8657ad7cd3486e2ab3fb0fab7cd4a707b647fa9141f1184dda77afc2584601b
3
+ metadata.gz: 162f785a8c6f03379f26cc784a5c4a8e28a831d1f1b1145452addb16532f04c2
4
+ data.tar.gz: b5336ab860aafca2a2a15d9c6bc75aff74155bf43ee47948b2ea46688f805cdf
5
5
  SHA512:
6
- metadata.gz: 6818a57540fa93192d254fe1884907d6c91fa9c745642b9b3d5b66025ed682d485f5201580cecdf715d5017c47aea47f71d457e6604b5cac4bee9c9401699847
7
- data.tar.gz: 38f8f0afda612cd5045006cc5f03c164b70cac9b086f4a948c914e12d6e95c780e6b2ffdaf00cc150eac2956330ad7fd519d9a5f05bb462711163f4a635ddf50
6
+ metadata.gz: 1e95e2b691fbfe5ebdbc39b857fd26912015b7b8b74a0a7820540a2b5ab957ad437c06c235149cf7e8bbdadd4634007ce4ce83fc07c7e193c8996d0f71ea3e0f
7
+ data.tar.gz: 21e79d79373570d5d1371d29de75e0e918060f63c0877439bdf9da23ef030ccc85009d68d0f148c585e253a09a95d6dbd6f1a620b6cb67d94bbde1e0dd16df6e
checksums.yaml.gz.sig CHANGED
Binary file
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cotton-tail (0.2.1)
4
+ cotton-tail (0.3.0)
5
5
  bunny (~> 2.12)
6
+ ibsciss-middleware (~> 0.4.2)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -20,6 +21,7 @@ GEM
20
21
  faraday_middleware (0.12.2)
21
22
  faraday (>= 0.7.4, < 1.0)
22
23
  hashie (3.6.0)
24
+ ibsciss-middleware (0.4.2)
23
25
  jaro_winkler (1.5.1)
24
26
  multi_json (1.13.1)
25
27
  multipart-post (2.0.0)
data/cotton-tail.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ['lib']
30
30
 
31
31
  spec.add_dependency 'bunny', '~> 2.12'
32
+ spec.add_dependency 'ibsciss-middleware', '~> 0.4.2'
32
33
 
33
34
  spec.add_development_dependency 'bundler', '~> 1.16'
34
35
  spec.add_development_dependency 'rabbitmq_http_api_client', '~> 1.9'
data/examples/app.rb CHANGED
@@ -4,7 +4,9 @@
4
4
  require 'bundler/setup'
5
5
  require 'cotton_tail'
6
6
 
7
- app = CottonTail::App.new.define do
7
+ app = CottonTail::App.new
8
+
9
+ app.routes.draw do
8
10
  # Create the queue 'hello_world_queue' if it does not exists
9
11
  queue 'hello_world_queue', exclusive: true do
10
12
  # Create a binding from the default topic exchange ('amq.topic') to
@@ -18,26 +20,30 @@ app = CottonTail::App.new.define do
18
20
  puts 'Goodbye cruel world!'
19
21
  end
20
22
 
21
- handle 'inspect.message' do |delivery_info, properties, message|
23
+ handle 'inspect.message' do |env, routing_key, delivery_info, properties, payload|
24
+ puts env: env
25
+ puts routing_key: routing_key
22
26
  puts delivery_info: delivery_info
23
27
  puts properties: properties
24
- puts message: message
28
+ puts payload: payload
25
29
  end
26
30
  end
27
31
 
28
32
  queue 'require_ack_queue', exclusive: true, manual_ack: true do
29
- handle 'get.acked' do |delivery_info, _props, _msg, opts|
30
- conn = opts[:conn]
33
+ handle 'get.acked' do |_env, _routing_key, delivery_info, _properties, _message|
31
34
  delivery_tag = delivery_info[:delivery_tag]
32
35
  puts "acking with #{delivery_tag}"
33
- conn.ack(delivery_tag)
36
+
37
+ ch = delivery_info[:channel]
38
+ ch.ack(delivery_tag)
34
39
  end
35
40
 
36
- handle 'get.nacked' do |delivery_info, _props, _msg, opts|
37
- conn = opts[:conn]
41
+ handle 'get.nacked' do |_env, _routing_key, delivery_info, _properties, _message|
38
42
  delivery_tag = delivery_info[:delivery_tag]
39
43
  puts "nacking with #{delivery_tag}"
40
- conn.nack(delivery_tag)
44
+
45
+ ch = delivery_info[:channel]
46
+ ch.nack(delivery_tag)
41
47
  end
42
48
  end
43
49
  end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'cotton_tail'
6
+
7
+ app = CottonTail::App.new
8
+
9
+ app.config.middleware do |_b|
10
+ # This is added to the end of the middleware stack
11
+ # 'message' is the return value of the handlers defined below
12
+ d.use ->(message) { puts message.upcase }
13
+ end
14
+
15
+ app.routes.draw do
16
+ queue 'hello_world_queue', exclusive: true do
17
+ handle 'say.hello' do
18
+ 'Hello world!'
19
+ end
20
+
21
+ handle 'say.goodbye' do
22
+ 'Goodbye cruel world!'
23
+ end
24
+ end
25
+ end
26
+
27
+ app.start
@@ -3,22 +3,17 @@
3
3
  module CottonTail
4
4
  # App is the main class for a CottonTail server
5
5
  class App
6
- def initialize(queue_strategy: Queue::Bunny, routing_strategy: Router)
7
- @dependencies = {
8
- queue_strategy: queue_strategy,
9
- routing_strategy: routing_strategy
10
- }
11
- end
6
+ attr_reader :env, :config
12
7
 
13
- # Define message routing
14
- def define(&block)
15
- @definition = DSL::App.new(**@dependencies)
16
- @definition.instance_eval(&block)
17
- self
8
+ def initialize(queue_strategy: Queue::Bunny, env: {}, connection: Bunny.new, config: Configuration.new)
9
+ @dependencies = { queue_strategy: queue_strategy, connection: connection }
10
+ @env = env
11
+ @config = config
12
+ @connection = connection.start
18
13
  end
19
14
 
20
15
  def queues
21
- @definition.queues
16
+ routes.queues
22
17
  end
23
18
 
24
19
  # Get a single message queue
@@ -38,11 +33,15 @@ module CottonTail
38
33
  sleep 0.01 while running?
39
34
  end
40
35
 
36
+ def routes
37
+ @routes ||= DSL::Routes.new(**@dependencies)
38
+ end
39
+
41
40
  private
42
41
 
43
42
  def supervisors
44
43
  @supervisors ||= queues.map do |_name, queue|
45
- Queue::Supervisor.new(queue, on_message: @definition.router)
44
+ Queue::Supervisor.new(queue, app: self)
46
45
  end
47
46
  end
48
47
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'middleware'
4
+
3
5
  module CottonTail
4
6
  # Configuration options
5
7
  class Configuration
@@ -7,11 +9,57 @@ module CottonTail
7
9
 
8
10
  def initialize
9
11
  @connection_args = nil
12
+ @middleware = Middleware::DEFAULT_STACK
13
+ @user_configs = {}
10
14
  end
11
15
 
16
+ # Sets the RabbitMQ connection params. Arguments are eventually passed
17
+ # to Bunny.new. Any valid params for Bunny.new are accepted.
18
+ #
19
+ # @see http://rubybunny.info/articles/connecting.html
12
20
  def connection_args=(*args, **kwargs)
13
21
  url, = args
14
22
  @connection_args = url ? Bunny::Session.parse_uri(url) : kwargs
15
23
  end
24
+
25
+ # Modify or retrieve the application middleware stack.
26
+ #
27
+ # @see https://github.com/Ibsciss/ruby-middleware
28
+ def middleware
29
+ return @middleware unless block_given?
30
+
31
+ @middleware = ::Middleware::Builder.new do |b|
32
+ b.use @middleware
33
+ yield b
34
+ end
35
+ end
36
+
37
+ def method_missing(method_id, *arguments, &block)
38
+ if user_config? method_id
39
+ @user_configs[method_id]
40
+ elsif setter?(method_id) && arguments.length == 1
41
+ @user_configs[getter_name(method_id)] = arguments.first
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(method_id, include_private = false)
48
+ user_config?(method_id) || super
49
+ end
50
+
51
+ private
52
+
53
+ def setter?(method_id)
54
+ method_id.to_s.end_with? '='
55
+ end
56
+
57
+ def user_config?(method_id)
58
+ @user_configs.key?(method_id)
59
+ end
60
+
61
+ def getter_name(setter)
62
+ setter.to_s.sub('=', '').to_sym
63
+ end
16
64
  end
17
65
  end
@@ -4,18 +4,22 @@ module CottonTail
4
4
  module DSL
5
5
  # This is the top level DSL for defining the bindings and message routing of
6
6
  # a cotton App
7
- class App
7
+ class Routes
8
8
  attr_reader :queues
9
9
 
10
- def initialize(queue_strategy:, routing_strategy:)
10
+ def initialize(queue_strategy:, connection:)
11
11
  @queue_strategy = queue_strategy
12
- @routing_strategy = routing_strategy
12
+ @connection = connection
13
13
  @queues = {}
14
14
  end
15
15
 
16
+ def draw(&block)
17
+ instance_eval(&block)
18
+ end
19
+
16
20
  # Define a new queue
17
21
  def queue(name, **opts, &block)
18
- @queue_strategy.call(name: name, **opts).tap do |queue_instance|
22
+ @queue_strategy.call(name: name, connection: @connection, **opts).tap do |queue_instance|
19
23
  @queues[name] = queue_instance
20
24
  queue_dsl = Queue.new(name, queue_instance, self)
21
25
  queue_dsl.instance_eval(&block) if block_given?
@@ -39,11 +43,11 @@ module CottonTail
39
43
 
40
44
  def handle(key, handler = nil, &block)
41
45
  handler ||= block
42
- router.route key, handler
46
+ handlers[key] = handler
43
47
  end
44
48
 
45
- def router
46
- @router ||= @routing_strategy.call
49
+ def handlers
50
+ @handlers ||= {}
47
51
  end
48
52
  end
49
53
  end
@@ -9,9 +9,9 @@ module CottonTail
9
9
  @context = context
10
10
  end
11
11
 
12
- def handle(routing_suffix, klass)
12
+ def handle(routing_suffix, handler = nil, &block)
13
13
  key = routing_key(routing_suffix)
14
- @context.instance_eval { handle key, klass }
14
+ @context.instance_eval { handle key, handler, &block }
15
15
  end
16
16
 
17
17
  private
@@ -3,7 +3,7 @@
3
3
  module CottonTail
4
4
  # DSL namespace
5
5
  module DSL
6
- autoload :App, 'cotton_tail/dsl/app'
6
+ autoload :Routes, 'cotton_tail/dsl/routes'
7
7
  autoload :Queue, 'cotton_tail/dsl/queue'
8
8
  autoload :Topic, 'cotton_tail/dsl/topic'
9
9
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CottonTail
4
+ module Middleware
5
+ # Router Middleware
6
+ class Router
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(request)
12
+ message = parse(request)
13
+ request.shift
14
+ request.unshift message.app.env
15
+ @app.call handler(message.app, message.routing_key).call(request)
16
+ end
17
+
18
+ private
19
+
20
+ def handler(app, route)
21
+ handlers(app).fetch(route) { raise UndefinedRouteError }
22
+ end
23
+
24
+ def parse(msg)
25
+ Message.new(*msg)
26
+ end
27
+
28
+ def handlers(app)
29
+ app.routes.handlers
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'middleware'
4
+ require 'logger'
5
+
6
+ module CottonTail
7
+ # Top level namespace for Middleware
8
+ module Middleware
9
+ autoload :Router, 'cotton_tail/middleware/router'
10
+
11
+ DEFAULT_STACK = ::Middleware::Builder.new do |b|
12
+ b.use Router
13
+ end
14
+ end
15
+ end
@@ -10,15 +10,16 @@ module CottonTail
10
10
  class Bunny < SimpleDelegator
11
11
  extend Forwardable
12
12
 
13
- def self.call(name:, **opts)
14
- new(name, **opts)
13
+ def self.call(**opts)
14
+ new(**opts)
15
15
  end
16
16
 
17
- def initialize(name, manual_ack: false, **opts)
17
+ def initialize(name:, connection:, manual_ack: false, **opts)
18
18
  super ::Queue.new
19
19
 
20
20
  @name = name
21
21
  @source_opts = opts
22
+ @connection = connection
22
23
 
23
24
  watch_source manual_ack
24
25
  end
@@ -40,16 +41,12 @@ module CottonTail
40
41
 
41
42
  private
42
43
 
43
- def_delegator :'CottonTail.configuration', :connection_args
44
+ attr_reader :connection
44
45
 
45
46
  def watch_source(manual_ack)
46
47
  source.subscribe(manual_ack: manual_ack) { |*args| self << args }
47
48
  end
48
49
 
49
- def connection
50
- @connection ||= ::Bunny.new(*connection_args).start
51
- end
52
-
53
50
  def source
54
51
  @source ||= channel.queue(@name, **@source_opts)
55
52
  end
@@ -10,9 +10,9 @@ module CottonTail
10
10
  Thread.new { new(queue, **kwargs).start }
11
11
  end
12
12
 
13
- def initialize(queue, on_message:)
13
+ def initialize(queue, app:)
14
14
  @queue = queue
15
- @on_message = on_message
15
+ @app = app
16
16
  end
17
17
 
18
18
  def fiber
@@ -22,10 +22,18 @@ module CottonTail
22
22
  end
23
23
 
24
24
  def start
25
- while fiber.alive?
26
- args = fiber.resume
27
- @on_message.call(*args) if args
28
- end
25
+ call_next while fiber.alive?
26
+ end
27
+
28
+ private
29
+
30
+ def call_next
31
+ args = fiber.resume
32
+ middleware.call([@app, *args]) if args
33
+ end
34
+
35
+ def middleware
36
+ @app.config.middleware
29
37
  end
30
38
  end
31
39
  end
@@ -6,9 +6,9 @@ module CottonTail
6
6
  module Queue
7
7
  # A supervisor for a single queue
8
8
  class Supervisor
9
- def initialize(queue, on_message:)
9
+ def initialize(queue, app:)
10
10
  @queue = queue
11
- @on_message = on_message
11
+ @app = app
12
12
  end
13
13
 
14
14
  def start
@@ -28,7 +28,7 @@ module CottonTail
28
28
  private
29
29
 
30
30
  def process
31
- @process ||= Reader.spawn(@queue, on_message: @on_message)
31
+ @process ||= Reader.spawn(@queue, app: @app)
32
32
  end
33
33
  end
34
34
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CottonTail
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/cotton_tail.rb CHANGED
@@ -7,19 +7,16 @@ 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 :Middleware, 'cotton_tail/middleware'
10
11
  autoload :Queue, 'cotton_tail/queue'
11
12
  autoload :Router, 'cotton_tail/router'
12
13
  autoload :Version, 'cotton_tail/version'
13
14
 
14
- class << self
15
- def configure
16
- return configuration unless block_given?
15
+ # Message is a struct for working with the messages that are passed through
16
+ # the middleware stack.
17
+ Message = Struct.new(:app, :routing_key, :delivery_info, :properties,
18
+ :payload)
17
19
 
18
- yield configuration
19
- end
20
-
21
- def configuration
22
- @configuration ||= Configuration.new
23
- end
20
+ class UndefinedRouteError < StandardError
24
21
  end
25
22
  end
data.tar.gz.sig CHANGED
Binary file
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.2.1
4
+ version: 0.3.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: 2018-11-16 00:00:00.000000000 Z
33
+ date: 2018-11-21 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: bunny
@@ -46,6 +46,20 @@ dependencies:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
48
  version: '2.12'
49
+ - !ruby/object:Gem::Dependency
50
+ name: ibsciss-middleware
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.4.2
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.4.2
49
63
  - !ruby/object:Gem::Dependency
50
64
  name: bundler
51
65
  requirement: !ruby/object:Gem::Requirement
@@ -169,6 +183,7 @@ files:
169
183
  - cotton-tail.gemspec
170
184
  - docker-compose.yml
171
185
  - examples/app.rb
186
+ - examples/app_with_middleware.rb
172
187
  - examples/messages/get.acked
173
188
  - examples/messages/get.nacked
174
189
  - examples/messages/inspect.message
@@ -178,15 +193,16 @@ files:
178
193
  - lib/cotton_tail/app.rb
179
194
  - lib/cotton_tail/configuration.rb
180
195
  - lib/cotton_tail/dsl.rb
181
- - lib/cotton_tail/dsl/app.rb
182
196
  - lib/cotton_tail/dsl/queue.rb
197
+ - lib/cotton_tail/dsl/routes.rb
183
198
  - lib/cotton_tail/dsl/topic.rb
199
+ - lib/cotton_tail/middleware.rb
200
+ - lib/cotton_tail/middleware/router.rb
184
201
  - lib/cotton_tail/queue.rb
185
202
  - lib/cotton_tail/queue/bunny.rb
186
203
  - lib/cotton_tail/queue/memory.rb
187
204
  - lib/cotton_tail/queue/reader.rb
188
205
  - lib/cotton_tail/queue/supervisor.rb
189
- - lib/cotton_tail/router.rb
190
206
  - lib/cotton_tail/version.rb
191
207
  homepage: https://github.com/jamesBrennan/cotton-tail
192
208
  licenses:
metadata.gz.sig CHANGED
Binary file
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CottonTail
4
- # Register message handlers and dispatch messages to them
5
- class Router
6
- def self.call
7
- new
8
- end
9
-
10
- def initialize
11
- @handlers = {}
12
- end
13
-
14
- def route(key, handler)
15
- @handlers[key] = handler
16
- end
17
-
18
- def dispatch(key, *args)
19
- @handlers[key].call(*args)
20
- end
21
-
22
- alias call dispatch
23
- end
24
- end