shivam 0.0.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 +384 -0
  25. data/lib/hutch/cli.rb +246 -0
  26. data/lib/hutch/config.rb +305 -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 +205 -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,305 @@
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
+ # RabbitMQ port
77
+ number_setting :mq_port, 5672
78
+
79
+ # RabbitMQ HTTP API port
80
+ number_setting :mq_api_port, 15672
81
+
82
+ # [RabbitMQ heartbeat timeout](http://rabbitmq.com/heartbeats.html)
83
+ number_setting :heartbeat, 30
84
+
85
+ # The <tt>basic.qos</tt> prefetch value to use.
86
+ #
87
+ # Default: `0`, no limit. See Bunny and RabbitMQ documentation.
88
+ number_setting :channel_prefetch, 0
89
+
90
+ # Bunny's socket open timeout
91
+ number_setting :connection_timeout, 11
92
+
93
+ # Bunny's socket read timeout
94
+ number_setting :read_timeout, 11
95
+
96
+ # Bunny's socket write timeout
97
+ number_setting :write_timeout, 11
98
+
99
+ # Bunny's enable/disable network recovery
100
+ boolean_setting :automatically_recover, true
101
+
102
+ # Bunny's reconnect interval
103
+ number_setting :network_recovery_interval, 1
104
+
105
+ # FIXME: DOCUMENT THIS
106
+ number_setting :graceful_exit_timeout, 11
107
+
108
+ # Bunny consumer work pool size
109
+ number_setting :consumer_pool_size, 1
110
+
111
+ # Should TLS be used?
112
+ boolean_setting :mq_tls, false
113
+
114
+ # Should SSL certificate be verified?
115
+ boolean_setting :mq_verify_peer, true
116
+
117
+ # Should SSL be used for the RabbitMQ API?
118
+ boolean_setting :mq_api_ssl, false
119
+
120
+ # Should the current Rails app directory be required?
121
+ boolean_setting :autoload_rails, true
122
+
123
+ # Should the Hutch runner process daemonise?
124
+ #
125
+ # The option is ignored on JRuby.
126
+ boolean_setting :daemonise, false
127
+
128
+ # Should RabbitMQ publisher confirms be enabled?
129
+ #
130
+ # Leaves it up to the app how they are tracked
131
+ # (e.g. using Hutch::Broker#confirm_select callback or Hutch::Broker#wait_for_confirms)
132
+ boolean_setting :publisher_confirms, false
133
+
134
+ # Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for
135
+ # every publish.
136
+ #
137
+ # **This is the safest option which also offers the
138
+ # lowest throughput**.
139
+ boolean_setting :force_publisher_confirms, false
140
+
141
+ # Should the RabbitMQ HTTP API be used?
142
+ boolean_setting :enable_http_api_use, true
143
+
144
+ # Should Bunny's consumer work pool threads abort on exception.
145
+ #
146
+ # The option is ignored on JRuby.
147
+ boolean_setting :consumer_pool_abort_on_exception, false
148
+
149
+ # Prefix displayed on the consumers tags.
150
+ string_setting :consumer_tag_prefix, 'hutch'
151
+
152
+ # A namespace to help group your queues
153
+ string_setting :namespace, nil
154
+
155
+ string_setting :group, ''
156
+
157
+ # Set of all setting keys
158
+ ALL_KEYS = @boolean_keys + @number_keys + @string_keys
159
+
160
+ def self.initialize(params = {})
161
+ unless @config
162
+ @config = default_config
163
+ define_methods
164
+ @config.merge!(env_based_config)
165
+ end
166
+ @config.merge!(params)
167
+ @config
168
+ end
169
+
170
+ # Default settings
171
+ #
172
+ # @return [Hash]
173
+ def self.default_config
174
+ @settings_defaults.merge({
175
+ mq_exchange_options: {},
176
+ mq_tls_cert: nil,
177
+ mq_tls_key: nil,
178
+ mq_tls_ca_certificates: nil,
179
+ uri: nil,
180
+ log_level: Logger::INFO,
181
+ client_logger: nil,
182
+ require_paths: [],
183
+ error_handlers: [Hutch::ErrorHandlers::Logger.new],
184
+ # note that this is not a list, it is a chain of responsibility
185
+ # that will fall back to "nack unconditionally"
186
+ error_acknowledgements: [],
187
+ setup_procs: [],
188
+ consumer_groups: {},
189
+ tracer: Hutch::Tracers::NullTracer,
190
+ namespace: nil,
191
+ pidfile: nil,
192
+ serializer: Hutch::Serializers::JSON
193
+ })
194
+ end
195
+
196
+ # Override defaults with ENV variables which begin with <tt>HUTCH_</tt>
197
+ #
198
+ # @return [Hash]
199
+ def self.env_based_config
200
+ env_keys_configured.each_with_object({}) {|attr, result|
201
+ value = ENV[key_for(attr)]
202
+
203
+ result[attr] = type_cast(attr, value)
204
+ }
205
+ end
206
+
207
+ # @return [Array<Symbol>]
208
+ def self.env_keys_configured
209
+ ALL_KEYS.each {|attr| check_attr(attr) }
210
+
211
+ ALL_KEYS.select { |attr| ENV.key?(key_for(attr)) }
212
+ end
213
+
214
+ def self.get(attr)
215
+ check_attr(attr.to_sym)
216
+ user_config[attr.to_sym]
217
+ end
218
+
219
+ def self.key_for(attr)
220
+ key = attr.to_s.gsub('.', '__').upcase
221
+ "HUTCH_#{key}"
222
+ end
223
+
224
+ def self.is_bool(attr)
225
+ @boolean_keys.include?(attr)
226
+ end
227
+
228
+ def self.to_bool(value)
229
+ case value
230
+ when nil, false, '', /^(false|f|no|n|0)$/i
231
+ false
232
+ else
233
+ true
234
+ end
235
+ end
236
+
237
+ def self.is_num(attr)
238
+ @number_keys.include?(attr)
239
+ end
240
+
241
+ def self.set(attr, value)
242
+ check_attr(attr.to_sym)
243
+ user_config[attr.to_sym] = type_cast(attr, value)
244
+ end
245
+
246
+ class << self
247
+ alias_method :[], :get
248
+ alias_method :[]=, :set
249
+ end
250
+
251
+ def self.check_attr(attr)
252
+ unless user_config.key?(attr)
253
+ raise UnknownAttributeError, "#{attr.inspect} is not a valid config attribute"
254
+ end
255
+ end
256
+
257
+ def self.user_config
258
+ @config ||= initialize
259
+ end
260
+
261
+ def self.to_hash
262
+ user_config
263
+ end
264
+
265
+ def self.load_from_file(file)
266
+ YAML.load(ERB.new(File.read(file)).result).each do |attr, value|
267
+ Hutch::Config.send("#{attr}=", convert_value(attr, value))
268
+ end
269
+ end
270
+
271
+ def self.convert_value(attr, value)
272
+ case attr
273
+ when 'tracer'
274
+ Kernel.const_get(value)
275
+ else
276
+ value
277
+ end
278
+ end
279
+
280
+ def self.type_cast(attr, value)
281
+ case
282
+ when is_bool(attr) || value == 'false'
283
+ to_bool(value)
284
+ when is_num(attr)
285
+ value.to_i
286
+ else
287
+ value
288
+ end
289
+ end
290
+ private_class_method :type_cast
291
+
292
+ def self.define_methods
293
+ @config.keys.each do |key|
294
+ define_singleton_method(key) do
295
+ get(key)
296
+ end
297
+
298
+ define_singleton_method("#{key}=") do |val|
299
+ set(key, val)
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ 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
+