emque-consuming 1.0.0.beta4
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 +40 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +205 -0
- data/Rakefile +14 -0
- data/bin/emque +5 -0
- data/emque-consuming.gemspec +36 -0
- data/lib/emque/consuming/actor.rb +21 -0
- data/lib/emque/consuming/adapter.rb +47 -0
- data/lib/emque/consuming/adapters/rabbit_mq/manager.rb +111 -0
- data/lib/emque/consuming/adapters/rabbit_mq/retry_worker.rb +59 -0
- data/lib/emque/consuming/adapters/rabbit_mq/worker.rb +87 -0
- data/lib/emque/consuming/adapters/rabbit_mq.rb +26 -0
- data/lib/emque/consuming/application.rb +112 -0
- data/lib/emque/consuming/cli.rb +140 -0
- data/lib/emque/consuming/command_receivers/base.rb +37 -0
- data/lib/emque/consuming/command_receivers/http_server.rb +103 -0
- data/lib/emque/consuming/command_receivers/unix_socket.rb +169 -0
- data/lib/emque/consuming/configuration.rb +63 -0
- data/lib/emque/consuming/consumer/common.rb +61 -0
- data/lib/emque/consuming/consumer.rb +33 -0
- data/lib/emque/consuming/consuming.rb +27 -0
- data/lib/emque/consuming/control/errors.rb +41 -0
- data/lib/emque/consuming/control/workers.rb +23 -0
- data/lib/emque/consuming/control.rb +33 -0
- data/lib/emque/consuming/core.rb +89 -0
- data/lib/emque/consuming/error_tracker.rb +39 -0
- data/lib/emque/consuming/generators/application.rb +95 -0
- data/lib/emque/consuming/helpers.rb +29 -0
- data/lib/emque/consuming/logging.rb +32 -0
- data/lib/emque/consuming/message.rb +22 -0
- data/lib/emque/consuming/pidfile.rb +54 -0
- data/lib/emque/consuming/router.rb +73 -0
- data/lib/emque/consuming/runner.rb +168 -0
- data/lib/emque/consuming/status.rb +26 -0
- data/lib/emque/consuming/tasks.rb +121 -0
- data/lib/emque/consuming/transmitter.rb +31 -0
- data/lib/emque/consuming/version.rb +5 -0
- data/lib/emque/consuming.rb +9 -0
- data/lib/emque-consuming.rb +3 -0
- data/lib/templates/.gitignore.tt +25 -0
- data/lib/templates/Gemfile.tt +6 -0
- data/lib/templates/Rakefile.tt +7 -0
- data/lib/templates/config/application.rb.tt +42 -0
- data/lib/templates/config/environments/development.rb.tt +2 -0
- data/lib/templates/config/environments/production.rb.tt +2 -0
- data/lib/templates/config/environments/staging.rb.tt +2 -0
- data/lib/templates/config/environments/test.rb.tt +2 -0
- data/lib/templates/config/routes.rb.tt +8 -0
- data/spec/application_spec.rb +28 -0
- data/spec/cli_spec.rb +136 -0
- data/spec/configuration_spec.rb +47 -0
- data/spec/consumer_spec.rb +56 -0
- data/spec/control/errors_spec.rb +170 -0
- data/spec/control_spec.rb +15 -0
- data/spec/core_spec.rb +121 -0
- data/spec/dummy/config/application.rb +38 -0
- data/spec/dummy/config/environments/test.rb +0 -0
- data/spec/dummy/config/routes.rb +0 -0
- data/spec/error_tracker_spec.rb +64 -0
- data/spec/pidfile_spec.rb +74 -0
- data/spec/router_spec.rb +14 -0
- data/spec/runner_spec.rb +138 -0
- data/spec/spec_helper.rb +43 -0
- metadata +309 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
require "oj"
|
2
|
+
require_relative "consumer/common"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Consuming
|
6
|
+
class BlockingFailure < StandardError; end
|
7
|
+
|
8
|
+
class Consumer
|
9
|
+
include Emque::Consuming.consumer
|
10
|
+
|
11
|
+
def process(message)
|
12
|
+
pipe(message, :through => [:parse, :route])
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parse(message)
|
18
|
+
message.with(
|
19
|
+
:values =>
|
20
|
+
Oj.load(message.original, :symbol_keys => true)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def route(message)
|
25
|
+
Emque::Consuming.application.router.route(
|
26
|
+
message.values.fetch(:metadata).fetch(:topic),
|
27
|
+
message.values.fetch(:metadata).fetch(:type),
|
28
|
+
message
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "emque/consuming/runner"
|
2
|
+
require "emque/consuming/logging"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Consuming
|
6
|
+
class << self
|
7
|
+
attr_accessor :application
|
8
|
+
|
9
|
+
# The Configuration instance used to configure the Emque::Consuming environment
|
10
|
+
def config
|
11
|
+
Emque::Consuming.application.config
|
12
|
+
end
|
13
|
+
|
14
|
+
def logger
|
15
|
+
Emque::Consuming::Logging.logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger=(log)
|
19
|
+
Emque::Consuming::Logging.logger = log
|
20
|
+
end
|
21
|
+
|
22
|
+
def runner
|
23
|
+
Emque::Consuming::Runner.instance
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
class Control
|
4
|
+
class Errors
|
5
|
+
include Emque::Consuming::Helpers
|
6
|
+
|
7
|
+
COMMANDS = [:clear, :down, :expire_after, :up, :retry]
|
8
|
+
|
9
|
+
def clear
|
10
|
+
app.error_tracker.occurrences.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
if app.error_tracker.limit > 1
|
15
|
+
config.error_limit = app.error_tracker.limit -= 1
|
16
|
+
app.verify_error_status
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def expire_after(seconds)
|
21
|
+
unless seconds.is_a?(Integer)
|
22
|
+
raise ArgumentError, "first argument must be an integer"
|
23
|
+
end
|
24
|
+
config.error_expiration = app.error_tracker.expiration = seconds
|
25
|
+
end
|
26
|
+
|
27
|
+
def up
|
28
|
+
config.error_limit = app.error_tracker.limit += 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def retry
|
32
|
+
app.manager.retry_errors
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to?(method)
|
36
|
+
COMMANDS.include?(method.to_sym)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
class Control
|
4
|
+
class Workers
|
5
|
+
include Emque::Consuming::Helpers
|
6
|
+
|
7
|
+
COMMANDS = [:down, :up]
|
8
|
+
|
9
|
+
def down(topic)
|
10
|
+
app.manager.worker(topic: topic.to_sym, command: :down)
|
11
|
+
end
|
12
|
+
|
13
|
+
def up(topic)
|
14
|
+
app.manager.worker(topic: topic.to_sym, command: :up)
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_to?(method)
|
18
|
+
COMMANDS.include?(method.to_sym)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "emque/consuming/control/errors"
|
2
|
+
require "emque/consuming/control/workers"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Consuming
|
6
|
+
class Control
|
7
|
+
include Emque::Consuming::Helpers
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@errors = Emque::Consuming::Control::Errors.new
|
11
|
+
@workers = Emque::Consuming::Control::Workers.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def errors(*args)
|
15
|
+
if args[0] && @errors.respond_to?(args[0])
|
16
|
+
@errors.send(args.shift, *args)
|
17
|
+
true
|
18
|
+
else
|
19
|
+
@errors
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def workers(topic = nil, command = nil, *args)
|
24
|
+
if command && topic && @workers.respond_to?(command)
|
25
|
+
@workers.send(command, topic, *args)
|
26
|
+
true
|
27
|
+
else
|
28
|
+
@workers
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "celluloid"
|
2
|
+
require "inflecto"
|
3
|
+
require "emque/consuming/configuration"
|
4
|
+
require "emque/consuming/adapter"
|
5
|
+
require "emque/consuming/logging"
|
6
|
+
require "emque/consuming/router"
|
7
|
+
require "emque/consuming/helpers"
|
8
|
+
|
9
|
+
module Emque
|
10
|
+
module Consuming
|
11
|
+
module Core
|
12
|
+
def self.extended(descendant)
|
13
|
+
descendant.class_eval do
|
14
|
+
class << self
|
15
|
+
attr_accessor :root, :topic_mapping, :router, :instance
|
16
|
+
|
17
|
+
alias :configure :instance_exec
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_core!
|
23
|
+
unless root
|
24
|
+
call_stack = caller_locations.map(&:path)
|
25
|
+
self.root = File.expand_path(
|
26
|
+
"../..",
|
27
|
+
call_stack.find { |call| call !~ %r{lib/emque} }
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
self.topic_mapping = {}
|
32
|
+
|
33
|
+
config.app_name = Inflecto.underscore(to_s).gsub("/application","")
|
34
|
+
|
35
|
+
load_app!
|
36
|
+
initialize_environment!
|
37
|
+
initialize_router!
|
38
|
+
end
|
39
|
+
|
40
|
+
def config
|
41
|
+
@config ||= Emque::Consuming::Configuration.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize_environment!
|
45
|
+
require_relative File.join(
|
46
|
+
root,
|
47
|
+
"config",
|
48
|
+
"environments",
|
49
|
+
"#{emque_env}.rb"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize_logger
|
54
|
+
Emque::Consuming::Logging.initialize_logger(logfile)
|
55
|
+
Emque::Consuming.logger.level = config.log_level
|
56
|
+
Celluloid.logger = Emque::Consuming.logger
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize_router!
|
60
|
+
self.router ||= Emque::Consuming::Router.new
|
61
|
+
require_relative File.join(root, "config", "routes.rb")
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_app!
|
65
|
+
app_files = Dir[File.join(root, "app", "**", "*.rb")]
|
66
|
+
|
67
|
+
app_files.each do |app_file|
|
68
|
+
klass = Inflecto.classify(File.basename(app_file, ".rb"))
|
69
|
+
emque_autoload(klass.to_sym, app_file)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def logfile
|
74
|
+
@logfile ||= File.join(root, "log/#{emque_env}.log").tap do |path|
|
75
|
+
directory = File.dirname(path)
|
76
|
+
Dir.mkdir(directory) unless File.exist?(directory)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def logger
|
81
|
+
Emque::Consuming.logger
|
82
|
+
end
|
83
|
+
|
84
|
+
def emque_env
|
85
|
+
config.env_var || ENV["EMQUE_ENV"] || ENV["RACK_ENV"] || "development"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
class ErrorTracker
|
6
|
+
attr_accessor :occurrences, :limit, :expiration
|
7
|
+
|
8
|
+
def initialize(limit: 5, expiration: 3600)
|
9
|
+
self.limit = limit
|
10
|
+
self.expiration = expiration
|
11
|
+
self.occurrences = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def notice_error_for(context)
|
15
|
+
occurrences[key_for(context)] = Time.now + expiration
|
16
|
+
end
|
17
|
+
|
18
|
+
def limit_reached?
|
19
|
+
count >= limit
|
20
|
+
end
|
21
|
+
|
22
|
+
def count
|
23
|
+
recent_errors.keys.count
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def recent_errors
|
29
|
+
occurrences.delete_if do |key, expiration_time|
|
30
|
+
expiration_time < Time.now
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def key_for(context)
|
35
|
+
Digest::SHA256.hexdigest(context.to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "fileutils"
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module Emque
|
6
|
+
module Consuming
|
7
|
+
module Generators
|
8
|
+
class Application
|
9
|
+
IGNORE = [".", ".."]
|
10
|
+
|
11
|
+
def initialize(options, name)
|
12
|
+
self.name = Inflecto.underscore(name)
|
13
|
+
self.options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate
|
17
|
+
context = Class.new(Object) { |obj|
|
18
|
+
def initialize(options, name)
|
19
|
+
@name = Inflecto.camelize(name)
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_binding; binding; end
|
24
|
+
}.new(options, name).get_binding
|
25
|
+
|
26
|
+
@current_dir = File.realdirpath(Dir.pwd)
|
27
|
+
|
28
|
+
recursively_copy_templates(
|
29
|
+
File.realdirpath(
|
30
|
+
File.join(
|
31
|
+
File.dirname(__FILE__),
|
32
|
+
"..",
|
33
|
+
"..",
|
34
|
+
"..",
|
35
|
+
"templates"
|
36
|
+
)
|
37
|
+
),
|
38
|
+
[current_dir, name],
|
39
|
+
context
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_accessor :name, :options
|
46
|
+
attr_reader :current_dir
|
47
|
+
|
48
|
+
def relative_path(path)
|
49
|
+
path.gsub(current_dir, ".")
|
50
|
+
end
|
51
|
+
|
52
|
+
def recursively_copy_templates(path, nesting, context)
|
53
|
+
Dir.entries(path).each do |e|
|
54
|
+
unless IGNORE.include?(e)
|
55
|
+
loc = File.join(path, e)
|
56
|
+
|
57
|
+
if Dir.exists?(loc)
|
58
|
+
new_nest = nesting + [e]
|
59
|
+
create_path = File.join(*new_nest)
|
60
|
+
|
61
|
+
unless Dir.exists?(create_path)
|
62
|
+
FileUtils.mkdir_p(create_path)
|
63
|
+
puts "created directory #{relative_path(create_path)}"
|
64
|
+
end
|
65
|
+
|
66
|
+
recursively_copy_templates(loc, new_nest, context)
|
67
|
+
elsif e =~ /\.tt$/
|
68
|
+
filename = File.join(
|
69
|
+
FileUtils.mkdir_p(File.join(*nesting)).first,
|
70
|
+
e.gsub(".tt", "")
|
71
|
+
)
|
72
|
+
display = relative_path(filename)
|
73
|
+
overwrite = "Y"
|
74
|
+
|
75
|
+
if File.exists?(filename)
|
76
|
+
print "#{display} exists, overwrite? (yN) "
|
77
|
+
overwrite = $stdin.gets
|
78
|
+
end
|
79
|
+
|
80
|
+
if overwrite.upcase.chomp == "Y"
|
81
|
+
File.open(filename, "w") do |f|
|
82
|
+
f.write(ERB.new(File.read(loc)).result(context))
|
83
|
+
end
|
84
|
+
puts "created file #{display}"
|
85
|
+
else
|
86
|
+
puts "skipping file #{display}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
module Helpers
|
4
|
+
private
|
5
|
+
|
6
|
+
def app
|
7
|
+
@app ||=
|
8
|
+
Emque::Consuming.application.instance ||
|
9
|
+
Emque::Consuming.application.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def config
|
13
|
+
Emque::Consuming.application.config
|
14
|
+
end
|
15
|
+
|
16
|
+
def logger
|
17
|
+
Emque::Consuming.application.logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def router
|
21
|
+
Emque::Consuming.application.router
|
22
|
+
end
|
23
|
+
|
24
|
+
def runner
|
25
|
+
Emque::Consuming.runner
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
module Logging
|
6
|
+
class LogFormatter < Logger::Formatter
|
7
|
+
def call(severity, time, progname, msg)
|
8
|
+
"#{time.utc} [#{severity}] #{msg}\n"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.initialize_logger(log_target = STDOUT)
|
13
|
+
@logger = Logger.new(log_target)
|
14
|
+
@logger.level = Logger::INFO
|
15
|
+
@logger.formatter = LogFormatter.new
|
16
|
+
@logger
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.logger
|
20
|
+
defined?(@logger) ? @logger : initialize_logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.logger=(log)
|
24
|
+
@logger = log || Logger.new("/dev/null")
|
25
|
+
end
|
26
|
+
|
27
|
+
def logger
|
28
|
+
Emque::Consuming::Logging.logger
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
class Message
|
6
|
+
include Virtus.value_object
|
7
|
+
|
8
|
+
values do
|
9
|
+
attribute :offset, Integer
|
10
|
+
attribute :original, Object
|
11
|
+
attribute :partition, Integer
|
12
|
+
attribute :status, Symbol, :default => :processing
|
13
|
+
attribute :topic, Symbol, :default => :unknown
|
14
|
+
attribute :values, Hash, :default => {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def continue?
|
18
|
+
status == :processing
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
class Pidfile
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def_delegators :pid, :to_i, :to_s
|
11
|
+
|
12
|
+
def initialize(path)
|
13
|
+
self.path = path
|
14
|
+
ensure_dir_exists
|
15
|
+
self.pid = File.read(path).to_i if File.exists?(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def running?
|
19
|
+
if pid
|
20
|
+
if pid == 0
|
21
|
+
rm_file
|
22
|
+
else
|
23
|
+
begin
|
24
|
+
Process.getpgid(pid)
|
25
|
+
return true
|
26
|
+
rescue Errno::ESRCH
|
27
|
+
rm_file
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def write
|
35
|
+
File.open(path, "w") do |f|
|
36
|
+
f.puts Process.pid
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_writer :path
|
43
|
+
attr_accessor :pid
|
44
|
+
|
45
|
+
def ensure_dir_exists
|
46
|
+
FileUtils.mkdir_p(File.dirname(path))
|
47
|
+
end
|
48
|
+
|
49
|
+
def rm_file
|
50
|
+
FileUtils.rm_f(path) if File.exists?(path)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
class Router
|
4
|
+
def initialize
|
5
|
+
self.mappings = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def map(&block)
|
9
|
+
self.instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def topic(mapping, &block)
|
13
|
+
mapping = Mapping.new(mapping, &block)
|
14
|
+
mappings[mapping.topic.to_sym] ||= []
|
15
|
+
mappings[mapping.topic.to_sym] << mapping
|
16
|
+
end
|
17
|
+
|
18
|
+
def route(topic, type, message)
|
19
|
+
mappings[topic.to_sym].each do |mapping|
|
20
|
+
method = mapping.route(type.to_s)
|
21
|
+
|
22
|
+
if method
|
23
|
+
consumer = mapping.consumer
|
24
|
+
|
25
|
+
consumer.new.consume(method, message)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def topic_mapping
|
31
|
+
mappings.inject({}) do |hash, (topic, maps)|
|
32
|
+
hash.tap do |h|
|
33
|
+
h[topic] = maps.map(&:consumer)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def workers(topic)
|
39
|
+
mappings[topic.to_sym].map(&:workers).max
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_accessor :mappings
|
45
|
+
|
46
|
+
class Mapping
|
47
|
+
attr_reader :consumer, :topic, :workers
|
48
|
+
|
49
|
+
def initialize(mapping, &block)
|
50
|
+
self.topic = mapping.keys.first
|
51
|
+
self.workers = mapping.fetch(:workers, 1)
|
52
|
+
self.consumer = mapping.values.first
|
53
|
+
self.mapping = {}
|
54
|
+
|
55
|
+
self.instance_eval(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def map(map)
|
59
|
+
mapping.merge!(map)
|
60
|
+
end
|
61
|
+
|
62
|
+
def route(type)
|
63
|
+
mapping[type]
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
attr_accessor :mapping
|
69
|
+
attr_writer :consumer, :topic, :workers
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|