shivam 0.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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
+