hutch-java 1.3.0-java

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.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +57 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +899 -0
  7. data/Gemfile +35 -0
  8. data/Guardfile +14 -0
  9. data/LICENSE +23 -0
  10. data/README.md +679 -0
  11. data/Rakefile +21 -0
  12. data/bin/ci/before_build.sh +20 -0
  13. data/bin/ci/before_build_docker.sh +20 -0
  14. data/bin/ci/install_on_debian.sh +46 -0
  15. data/bin/hutch +8 -0
  16. data/examples/consumer.rb +13 -0
  17. data/examples/producer.rb +10 -0
  18. data/hutch.gemspec +27 -0
  19. data/lib/hutch/acknowledgements/base.rb +16 -0
  20. data/lib/hutch/acknowledgements/nack_on_all_failures.rb +19 -0
  21. data/lib/hutch/adapter.rb +11 -0
  22. data/lib/hutch/adapters/bunny.rb +37 -0
  23. data/lib/hutch/adapters/march_hare.rb +41 -0
  24. data/lib/hutch/broker.rb +383 -0
  25. data/lib/hutch/cli.rb +246 -0
  26. data/lib/hutch/config.rb +304 -0
  27. data/lib/hutch/consumer.rb +125 -0
  28. data/lib/hutch/error_handlers/airbrake.rb +54 -0
  29. data/lib/hutch/error_handlers/base.rb +15 -0
  30. data/lib/hutch/error_handlers/bugsnag.rb +30 -0
  31. data/lib/hutch/error_handlers/honeybadger.rb +43 -0
  32. data/lib/hutch/error_handlers/logger.rb +22 -0
  33. data/lib/hutch/error_handlers/rollbar.rb +28 -0
  34. data/lib/hutch/error_handlers/sentry.rb +26 -0
  35. data/lib/hutch/error_handlers/sentry_raven.rb +31 -0
  36. data/lib/hutch/error_handlers.rb +11 -0
  37. data/lib/hutch/exceptions.rb +14 -0
  38. data/lib/hutch/logging.rb +32 -0
  39. data/lib/hutch/message.rb +31 -0
  40. data/lib/hutch/publisher.rb +75 -0
  41. data/lib/hutch/serializers/identity.rb +19 -0
  42. data/lib/hutch/serializers/json.rb +22 -0
  43. data/lib/hutch/tracers/datadog.rb +18 -0
  44. data/lib/hutch/tracers/newrelic.rb +19 -0
  45. data/lib/hutch/tracers/null_tracer.rb +15 -0
  46. data/lib/hutch/tracers.rb +7 -0
  47. data/lib/hutch/version.rb +3 -0
  48. data/lib/hutch/waiter.rb +104 -0
  49. data/lib/hutch/worker.rb +145 -0
  50. data/lib/hutch.rb +69 -0
  51. data/lib/yard-settings/handler.rb +38 -0
  52. data/lib/yard-settings/yard-settings.rb +2 -0
  53. data/spec/hutch/broker_spec.rb +462 -0
  54. data/spec/hutch/cli_spec.rb +93 -0
  55. data/spec/hutch/config_spec.rb +259 -0
  56. data/spec/hutch/consumer_spec.rb +208 -0
  57. data/spec/hutch/error_handlers/airbrake_spec.rb +49 -0
  58. data/spec/hutch/error_handlers/bugsnag_spec.rb +55 -0
  59. data/spec/hutch/error_handlers/honeybadger_spec.rb +58 -0
  60. data/spec/hutch/error_handlers/logger_spec.rb +28 -0
  61. data/spec/hutch/error_handlers/rollbar_spec.rb +45 -0
  62. data/spec/hutch/error_handlers/sentry_raven_spec.rb +37 -0
  63. data/spec/hutch/error_handlers/sentry_spec.rb +47 -0
  64. data/spec/hutch/logger_spec.rb +34 -0
  65. data/spec/hutch/message_spec.rb +38 -0
  66. data/spec/hutch/serializers/json_spec.rb +17 -0
  67. data/spec/hutch/tracers/datadog_spec.rb +44 -0
  68. data/spec/hutch/waiter_spec.rb +51 -0
  69. data/spec/hutch/worker_spec.rb +184 -0
  70. data/spec/hutch_spec.rb +87 -0
  71. data/spec/spec_helper.rb +42 -0
  72. data/templates/default/class/html/settings.erb +0 -0
  73. data/templates/default/class/setup.rb +4 -0
  74. data/templates/default/fulldoc/html/css/hutch.css +13 -0
  75. data/templates/default/layout/html/setup.rb +7 -0
  76. data/templates/default/method_details/html/settings.erb +5 -0
  77. data/templates/default/method_details/setup.rb +4 -0
  78. data/templates/default/method_details/text/settings.erb +0 -0
  79. data/templates/default/module/html/settings.erb +40 -0
  80. data/templates/default/module/setup.rb +4 -0
  81. metadata +204 -0
data/lib/hutch/cli.rb ADDED
@@ -0,0 +1,246 @@
1
+ require 'optparse'
2
+
3
+ require 'hutch/logging'
4
+ require 'hutch/config'
5
+ require 'hutch/version'
6
+ require 'hutch/exceptions'
7
+
8
+ module Hutch
9
+ class CLI
10
+ include Logging
11
+
12
+ # Run a Hutch worker with the command line interface.
13
+ def run(argv = ARGV)
14
+ Hutch::Config.initialize
15
+ parse_options(argv)
16
+
17
+ daemonise_process
18
+
19
+ write_pid if Hutch::Config.pidfile
20
+
21
+ Hutch.logger.info "hutch booted with pid #{::Process.pid}"
22
+
23
+ if load_app && start_work_loop == :success
24
+ # If we got here, the worker was shut down nicely
25
+ Hutch.logger.info 'hutch shut down gracefully'
26
+ exit 0
27
+ else
28
+ Hutch.logger.info 'hutch terminated due to an error'
29
+ exit 1
30
+ end
31
+ end
32
+
33
+ def load_app
34
+ # Try to load a Rails app in the current directory
35
+ load_rails_app('.') if Hutch::Config.autoload_rails
36
+ set_up_code_paths!
37
+
38
+ # Because of the order things are required when we run the Hutch binary
39
+ # in hutch/bin, the Sentry Raven gem gets required **after** the error
40
+ # handlers are set up. Due to this, we never got any Sentry notifications
41
+ # when an error occurred in any of the consumers.
42
+ if defined?(Raven)
43
+ Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::SentryRaven.new
44
+ end
45
+ if defined?(Sentry)
46
+ Hutch::Config[:error_handlers] << Hutch::ErrorHandlers::Sentry.new
47
+ end
48
+
49
+ true
50
+ end
51
+
52
+ def set_up_code_paths!
53
+ Hutch::Config.require_paths.each do |path|
54
+ # See if each path is a Rails app. If so, try to load it.
55
+ next if load_rails_app(path)
56
+
57
+ # Given path is not a Rails app, try requiring it as a file
58
+ logger.info "requiring '#{path}'"
59
+ begin
60
+ # Need to add '.' to load path for relative requires
61
+ $LOAD_PATH << '.'
62
+ require path
63
+ rescue LoadError => e
64
+ logger.fatal "could not load file '#{path}': #{e}"
65
+ return false
66
+ ensure
67
+ # Clean up load path
68
+ $LOAD_PATH.delete('.')
69
+ end
70
+ end
71
+ end
72
+
73
+ def load_rails_app(path)
74
+ # path should point to the app's top level directory
75
+ if File.directory?(path)
76
+ # Smells like a Rails app if it's got a script/rails or bin/rails file
77
+ is_rails_app = ['script/rails', 'bin/rails'].any? do |file|
78
+ File.exist?(File.expand_path(File.join(path, file)))
79
+ end
80
+ rails_path = File.expand_path(File.join(path, 'config/environment.rb'))
81
+ if is_rails_app && File.exist?(rails_path)
82
+ ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] || 'development'
83
+ logger.info "found rails project (#{path}), booting app in #{ENV['RACK_ENV']} environment"
84
+ require rails_path
85
+ ::Rails.application.eager_load!
86
+ return true
87
+ end
88
+ end
89
+ false
90
+ end
91
+
92
+ # Kick off the work loop. This method returns when the worker is shut down
93
+ # gracefully (with a SIGQUIT, SIGTERM or SIGINT).
94
+ def start_work_loop
95
+ Hutch.connect
96
+ @worker = Hutch::Worker.new(Hutch.broker, Hutch.consumers, Hutch::Config.setup_procs)
97
+ @worker.run
98
+ :success
99
+ rescue ConnectionError, AuthenticationError, WorkerSetupError => ex
100
+ Hutch::Config[:error_handlers].each do |backend|
101
+ backend.handle_setup_exception(ex)
102
+ end
103
+ logger.fatal ex.message
104
+ :error
105
+ end
106
+
107
+ def parse_options(args = ARGV)
108
+ OptionParser.new do |opts|
109
+ opts.banner = 'usage: hutch [options]'
110
+
111
+ opts.on('--mq-host HOST', 'Set the RabbitMQ host') do |host|
112
+ Hutch::Config.mq_host = host
113
+ end
114
+
115
+ opts.on('--mq-port PORT', 'Set the RabbitMQ port') do |port|
116
+ Hutch::Config.mq_port = port
117
+ end
118
+
119
+ opts.on("-t", "--[no-]mq-tls", 'Use TLS for the AMQP connection') do |tls|
120
+ Hutch::Config.mq_tls = tls
121
+ end
122
+
123
+ opts.on('--mq-tls-cert FILE', 'Certificate for TLS client verification') do |file|
124
+ abort_without_file(file, 'Certificate file') do
125
+ Hutch::Config.mq_tls_cert = file
126
+ end
127
+ end
128
+
129
+ opts.on('--mq-tls-key FILE', 'Private key for TLS client verification') do |file|
130
+ abort_without_file(file, 'Private key file') do
131
+ Hutch::Config.mq_tls_key = file
132
+ end
133
+ end
134
+
135
+ opts.on('--mq-exchange EXCHANGE',
136
+ 'Set the RabbitMQ exchange') do |exchange|
137
+ Hutch::Config.mq_exchange = exchange
138
+ end
139
+
140
+ opts.on('--mq-vhost VHOST', 'Set the RabbitMQ vhost') do |vhost|
141
+ Hutch::Config.mq_vhost = vhost
142
+ end
143
+
144
+ opts.on('--mq-username USERNAME',
145
+ 'Set the RabbitMQ username') do |username|
146
+ Hutch::Config.mq_username = username
147
+ end
148
+
149
+ opts.on('--mq-password PASSWORD',
150
+ 'Set the RabbitMQ password') do |password|
151
+ Hutch::Config.mq_password = password
152
+ end
153
+
154
+ opts.on('--mq-api-host HOST', 'Set the RabbitMQ API host') do |host|
155
+ Hutch::Config.mq_api_host = host
156
+ end
157
+
158
+ opts.on('--mq-api-port PORT', 'Set the RabbitMQ API port') do |port|
159
+ Hutch::Config.mq_api_port = port
160
+ end
161
+
162
+ opts.on("-s", "--[no-]mq-api-ssl", 'Use SSL for the RabbitMQ API') do |api_ssl|
163
+ Hutch::Config.mq_api_ssl = api_ssl
164
+ end
165
+
166
+ opts.on('--config FILE', 'Load Hutch configuration from a file') do |file|
167
+ begin
168
+ File.open(file) { |fp| Hutch::Config.load_from_file(fp) }
169
+ rescue Errno::ENOENT
170
+ abort_with_message("Config file '#{file}' not found")
171
+ end
172
+ end
173
+
174
+ opts.on('--require PATH', 'Require a Rails app or path') do |path|
175
+ Hutch::Config.require_paths << path
176
+ end
177
+
178
+ opts.on('--[no-]autoload-rails', 'Require the current rails app directory') do |autoload_rails|
179
+ Hutch::Config.autoload_rails = autoload_rails
180
+ end
181
+
182
+ opts.on('-q', '--quiet', 'Quiet logging') do
183
+ Hutch::Config.log_level = Logger::WARN
184
+ end
185
+
186
+ opts.on('-v', '--verbose', 'Verbose logging') do
187
+ Hutch::Config.log_level = Logger::DEBUG
188
+ end
189
+
190
+ opts.on('--namespace NAMESPACE', 'Queue namespace') do |namespace|
191
+ Hutch::Config.namespace = namespace
192
+ end
193
+
194
+ opts.on('-d', '--daemonise', 'Daemonise') do |daemonise|
195
+ Hutch::Config.daemonise = daemonise
196
+ end
197
+
198
+ opts.on('--pidfile PIDFILE', 'Pidfile') do |pidfile|
199
+ Hutch::Config.pidfile = pidfile
200
+ end
201
+
202
+ opts.on('--only-group GROUP', 'Load only consumers in this group') do |group|
203
+ Hutch::Config.group = group
204
+ end
205
+
206
+ opts.on('--version', 'Print the version and exit') do
207
+ puts "hutch v#{VERSION}"
208
+ exit 0
209
+ end
210
+
211
+ opts.on('-h', '--help', 'Show this message and exit') do
212
+ puts opts
213
+ exit 0
214
+ end
215
+ end.parse!(args)
216
+ end
217
+
218
+ def write_pid
219
+ pidfile = File.expand_path(Hutch::Config.pidfile)
220
+ Hutch.logger.info "writing pid in #{pidfile}"
221
+ File.open(pidfile, 'w') { |f| f.puts ::Process.pid }
222
+ end
223
+
224
+ private
225
+
226
+ def daemonise_process
227
+ return unless Hutch::Config.daemonise
228
+ if defined?(JRUBY_VERSION)
229
+ Hutch.logger.warn "JRuby ignores the --daemonise option"
230
+ return
231
+ end
232
+
233
+ ::Process.daemon(true)
234
+ end
235
+
236
+ def abort_without_file(file, file_description, &block)
237
+ abort_with_message("#{file_description} '#{file}' not found") unless File.exist?(file)
238
+
239
+ yield
240
+ end
241
+
242
+ def abort_with_message(message)
243
+ abort message
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,304 @@
1
+ require 'hutch/error_handlers/logger'
2
+ require 'hutch/tracers'
3
+ require 'hutch/serializers/json'
4
+ require 'erb'
5
+ require 'logger'
6
+
7
+ module Hutch
8
+ class UnknownAttributeError < StandardError; end
9
+
10
+ # Configuration settings, available everywhere
11
+ #
12
+ # There are defaults, which can be overridden by ENV variables prefixed by
13
+ # <tt>HUTCH_</tt>, and each of these can be overridden using the {.set}
14
+ # method.
15
+ #
16
+ # @example Configuring on the command-line
17
+ # HUTCH_PUBLISHER_CONFIRMS=false hutch
18
+ module Config
19
+ require 'yaml'
20
+ @string_keys = Set.new
21
+ @number_keys = Set.new
22
+ @boolean_keys = Set.new
23
+ @settings_defaults = {}
24
+
25
+ # Define a String user setting
26
+ # @!visibility private
27
+ def self.string_setting(name, default_value)
28
+ @string_keys << name
29
+ @settings_defaults[name] = default_value
30
+ end
31
+
32
+ # Define a Number user setting
33
+ # @!visibility private
34
+ def self.number_setting(name, default_value)
35
+ @number_keys << name
36
+ @settings_defaults[name] = default_value
37
+ end
38
+
39
+ # Define a Boolean user setting
40
+ # @!visibility private
41
+ def self.boolean_setting(name, default_value)
42
+ @boolean_keys << name
43
+ @settings_defaults[name] = default_value
44
+ end
45
+
46
+ # RabbitMQ hostname
47
+ string_setting :mq_host, '127.0.0.1'
48
+
49
+ # RabbitMQ Exchange to use for publishing
50
+ string_setting :mq_exchange, 'hutch'
51
+
52
+ # RabbitMQ Exchange type to use for publishing
53
+ string_setting :mq_exchange_type, 'topic'
54
+
55
+ # RabbitMQ vhost to use
56
+ string_setting :mq_vhost, '/'
57
+
58
+ # RabbitMQ username to use.
59
+ #
60
+ # As of RabbitMQ 3.3.0, <tt>guest</tt> can only can connect from localhost.
61
+ string_setting :mq_username, 'guest'
62
+
63
+ # RabbitMQ password
64
+ string_setting :mq_password, 'guest'
65
+
66
+ # RabbitMQ Auth Mechanism
67
+ string_setting :mq_auth_mechanism, 'PLAIN'
68
+
69
+ # RabbitMQ URI (takes precedence over MQ username, password, host, port and vhost settings)
70
+ string_setting :uri, nil
71
+
72
+ # RabbitMQ HTTP API hostname
73
+ string_setting :mq_api_host, '127.0.0.1'
74
+
75
+ # RabbitMQ port
76
+ number_setting :mq_port, 5672
77
+
78
+ # RabbitMQ HTTP API port
79
+ number_setting :mq_api_port, 15672
80
+
81
+ # [RabbitMQ heartbeat timeout](http://rabbitmq.com/heartbeats.html)
82
+ number_setting :heartbeat, 30
83
+
84
+ # The <tt>basic.qos</tt> prefetch value to use.
85
+ #
86
+ # Default: `0`, no limit. See Bunny and RabbitMQ documentation.
87
+ number_setting :channel_prefetch, 0
88
+
89
+ # Bunny's socket open timeout
90
+ number_setting :connection_timeout, 11
91
+
92
+ # Bunny's socket read timeout
93
+ number_setting :read_timeout, 11
94
+
95
+ # Bunny's socket write timeout
96
+ number_setting :write_timeout, 11
97
+
98
+ # Bunny's enable/disable network recovery
99
+ boolean_setting :automatically_recover, true
100
+
101
+ # Bunny's reconnect interval
102
+ number_setting :network_recovery_interval, 1
103
+
104
+ # FIXME: DOCUMENT THIS
105
+ number_setting :graceful_exit_timeout, 11
106
+
107
+ # Bunny consumer work pool size
108
+ number_setting :consumer_pool_size, 1
109
+
110
+ # Should TLS be used?
111
+ boolean_setting :mq_tls, false
112
+
113
+ # Should SSL certificate be verified?
114
+ boolean_setting :mq_verify_peer, true
115
+
116
+ # Should SSL be used for the RabbitMQ API?
117
+ boolean_setting :mq_api_ssl, false
118
+
119
+ # Should the current Rails app directory be required?
120
+ boolean_setting :autoload_rails, true
121
+
122
+ # Should the Hutch runner process daemonise?
123
+ #
124
+ # The option is ignored on JRuby.
125
+ boolean_setting :daemonise, false
126
+
127
+ # Should RabbitMQ publisher confirms be enabled?
128
+ #
129
+ # Leaves it up to the app how they are tracked
130
+ # (e.g. using Hutch::Broker#confirm_select callback or Hutch::Broker#wait_for_confirms)
131
+ boolean_setting :publisher_confirms, false
132
+
133
+ # Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for
134
+ # every publish.
135
+ #
136
+ # **This is the safest option which also offers the
137
+ # lowest throughput**.
138
+ boolean_setting :force_publisher_confirms, false
139
+
140
+ # Should the RabbitMQ HTTP API be used?
141
+ boolean_setting :enable_http_api_use, true
142
+
143
+ # Should Bunny's consumer work pool threads abort on exception.
144
+ #
145
+ # The option is ignored on JRuby.
146
+ boolean_setting :consumer_pool_abort_on_exception, false
147
+
148
+ # Prefix displayed on the consumers tags.
149
+ string_setting :consumer_tag_prefix, 'hutch'
150
+
151
+ # A namespace to help group your queues
152
+ string_setting :namespace, nil
153
+
154
+ string_setting :group, ''
155
+
156
+ # Set of all setting keys
157
+ ALL_KEYS = @boolean_keys + @number_keys + @string_keys
158
+
159
+ def self.initialize(params = {})
160
+ unless @config
161
+ @config = default_config
162
+ define_methods
163
+ @config.merge!(env_based_config)
164
+ end
165
+ @config.merge!(params)
166
+ @config
167
+ end
168
+
169
+ # Default settings
170
+ #
171
+ # @return [Hash]
172
+ def self.default_config
173
+ @settings_defaults.merge({
174
+ mq_exchange_options: {},
175
+ mq_tls_cert: nil,
176
+ mq_tls_key: nil,
177
+ mq_tls_ca_certificates: nil,
178
+ uri: nil,
179
+ log_level: Logger::INFO,
180
+ client_logger: nil,
181
+ require_paths: [],
182
+ error_handlers: [Hutch::ErrorHandlers::Logger.new],
183
+ # note that this is not a list, it is a chain of responsibility
184
+ # that will fall back to "nack unconditionally"
185
+ error_acknowledgements: [],
186
+ setup_procs: [],
187
+ consumer_groups: {},
188
+ tracer: Hutch::Tracers::NullTracer,
189
+ namespace: nil,
190
+ pidfile: nil,
191
+ serializer: Hutch::Serializers::JSON
192
+ })
193
+ end
194
+
195
+ # Override defaults with ENV variables which begin with <tt>HUTCH_</tt>
196
+ #
197
+ # @return [Hash]
198
+ def self.env_based_config
199
+ env_keys_configured.each_with_object({}) {|attr, result|
200
+ value = ENV[key_for(attr)]
201
+
202
+ result[attr] = type_cast(attr, value)
203
+ }
204
+ end
205
+
206
+ # @return [Array<Symbol>]
207
+ def self.env_keys_configured
208
+ ALL_KEYS.each {|attr| check_attr(attr) }
209
+
210
+ ALL_KEYS.select { |attr| ENV.key?(key_for(attr)) }
211
+ end
212
+
213
+ def self.get(attr)
214
+ check_attr(attr.to_sym)
215
+ user_config[attr.to_sym]
216
+ end
217
+
218
+ def self.key_for(attr)
219
+ key = attr.to_s.gsub('.', '__').upcase
220
+ "HUTCH_#{key}"
221
+ end
222
+
223
+ def self.is_bool(attr)
224
+ @boolean_keys.include?(attr)
225
+ end
226
+
227
+ def self.to_bool(value)
228
+ case value
229
+ when nil, false, '', /^(false|f|no|n|0)$/i
230
+ false
231
+ else
232
+ true
233
+ end
234
+ end
235
+
236
+ def self.is_num(attr)
237
+ @number_keys.include?(attr)
238
+ end
239
+
240
+ def self.set(attr, value)
241
+ check_attr(attr.to_sym)
242
+ user_config[attr.to_sym] = type_cast(attr, value)
243
+ end
244
+
245
+ class << self
246
+ alias_method :[], :get
247
+ alias_method :[]=, :set
248
+ end
249
+
250
+ def self.check_attr(attr)
251
+ unless user_config.key?(attr)
252
+ raise UnknownAttributeError, "#{attr.inspect} is not a valid config attribute"
253
+ end
254
+ end
255
+
256
+ def self.user_config
257
+ @config ||= initialize
258
+ end
259
+
260
+ def self.to_hash
261
+ user_config
262
+ end
263
+
264
+ def self.load_from_file(file)
265
+ YAML.load(ERB.new(File.read(file)).result).each do |attr, value|
266
+ Hutch::Config.send("#{attr}=", convert_value(attr, value))
267
+ end
268
+ end
269
+
270
+ def self.convert_value(attr, value)
271
+ case attr
272
+ when 'tracer'
273
+ Kernel.const_get(value)
274
+ else
275
+ value
276
+ end
277
+ end
278
+
279
+ def self.type_cast(attr, value)
280
+ case
281
+ when is_bool(attr) || value == 'false'
282
+ to_bool(value)
283
+ when is_num(attr)
284
+ value.to_i
285
+ else
286
+ value
287
+ end
288
+ end
289
+ private_class_method :type_cast
290
+
291
+ def self.define_methods
292
+ @config.keys.each do |key|
293
+ define_singleton_method(key) do
294
+ get(key)
295
+ end
296
+
297
+ define_singleton_method("#{key}=") do |val|
298
+ set(key, val)
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+ Hutch::Config.initialize
@@ -0,0 +1,125 @@
1
+ require 'set'
2
+
3
+ module Hutch
4
+ # Include this module in a class to register it as a consumer. Consumers
5
+ # gain a class method called `consume`, which should be used to register
6
+ # the routing keys a consumer is interested in.
7
+ module Consumer
8
+ attr_accessor :broker, :delivery_info
9
+
10
+ def self.included(base)
11
+ base.extend(ClassMethods)
12
+ Hutch.register_consumer(base)
13
+ end
14
+
15
+ def reject!
16
+ @message_rejected = true
17
+ broker.reject(delivery_info.delivery_tag)
18
+ end
19
+
20
+ def requeue!
21
+ @message_rejected = true
22
+ broker.requeue(delivery_info.delivery_tag)
23
+ end
24
+
25
+ def message_rejected?
26
+ !!@message_rejected
27
+ end
28
+
29
+ def logger
30
+ Hutch::Logging.logger
31
+ end
32
+
33
+ module ClassMethods
34
+ # Add one or more routing keys to the set of routing keys the consumer
35
+ # wants to subscribe to.
36
+ def consume(*routing_keys)
37
+ @routing_keys = self.routing_keys.union(routing_keys)
38
+ # these are opt-in
39
+ @queue_mode = nil
40
+ @queue_type = nil
41
+ end
42
+
43
+ attr_reader :queue_mode, :queue_type, :initial_group_size
44
+
45
+ # Explicitly set the queue name
46
+ def queue_name(name)
47
+ @queue_name = name
48
+ end
49
+
50
+ # Explicitly set the queue mode to 'lazy'
51
+ def lazy_queue
52
+ @queue_mode = 'lazy'
53
+ end
54
+
55
+ # Explicitly set the queue type to 'classic'
56
+ def classic_queue
57
+ @queue_type = 'classic'
58
+ end
59
+
60
+ # Explicitly set the queue type to 'quorum'
61
+ # @param [Hash] options the options params related to quorum queue
62
+ # @option options [Integer] :initial_group_size Initial Replication Factor
63
+ def quorum_queue(options = {})
64
+ @queue_type = 'quorum'
65
+ @initial_group_size = options[:initial_group_size]
66
+ end
67
+
68
+ # Configures an optional argument that will be passed when declaring the queue.
69
+ # Prefer using a policy to this DSL: https://www.rabbitmq.com/parameters.html#policies
70
+ def arguments(arguments = {})
71
+ @arguments = arguments
72
+ end
73
+
74
+ # Congfiures queue options that will be passed when declaring the queue.
75
+ def queue_options(options = {})
76
+ @queue_options = options
77
+ end
78
+
79
+ # Set custom serializer class, override global value
80
+ def serializer(name)
81
+ @serializer = name
82
+ end
83
+
84
+ # The RabbitMQ queue name for the consumer. This is derived from the
85
+ # fully-qualified class name. Module separators are replaced with single
86
+ # colons, camelcased class names are converted to snake case.
87
+ def get_queue_name
88
+ return @queue_name unless @queue_name.nil?
89
+ queue_name = self.name.gsub(/::/, ':')
90
+ queue_name.gsub!(/([^A-Z:])([A-Z])/) { "#{$1}_#{$2}" }
91
+ queue_name.downcase
92
+ end
93
+
94
+ # Returns consumer custom arguments.
95
+ def get_arguments
96
+ all_arguments = @arguments || {}
97
+
98
+ all_arguments['x-queue-mode'] = @queue_mode if @queue_mode
99
+ all_arguments['x-queue-type'] = @queue_type if @queue_type
100
+ all_arguments['x-quorum-initial-group-size'] = @initial_group_size if @initial_group_size
101
+
102
+ all_arguments
103
+ end
104
+
105
+ def get_options
106
+ default_options = { durable: true }
107
+
108
+ all_options = default_options.merge(@queue_options || {})
109
+ all_options[:arguments] = get_arguments
110
+
111
+ all_options
112
+ end
113
+
114
+ # Accessor for the consumer's routing key.
115
+ def routing_keys
116
+ @routing_keys ||= Set.new
117
+ end
118
+
119
+ def get_serializer
120
+ @serializer
121
+ end
122
+ end
123
+ end
124
+ end
125
+