hutch 0.9.0 → 0.10.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 +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +13 -1
- data/hutch.gemspec +2 -2
- data/lib/hutch/broker.rb +24 -3
- data/lib/hutch/cli.rb +20 -1
- data/lib/hutch/config.rb +7 -2
- data/lib/hutch/consumer.rb +10 -0
- data/lib/hutch/error_handlers/logger.rb +1 -1
- data/lib/hutch/error_handlers/sentry.rb +1 -1
- data/lib/hutch/message.rb +2 -0
- data/lib/hutch/version.rb +1 -1
- data/lib/hutch/worker.rb +4 -4
- data/spec/hutch/broker_spec.rb +68 -35
- data/spec/hutch/cli_spec.rb +6 -6
- data/spec/hutch/config_spec.rb +12 -12
- data/spec/hutch/consumer_spec.rb +13 -9
- data/spec/hutch/error_handlers/logger_spec.rb +3 -3
- data/spec/hutch/error_handlers/sentry_spec.rb +2 -2
- data/spec/hutch/logger_spec.rb +3 -3
- data/spec/hutch/message_spec.rb +7 -4
- data/spec/hutch/worker_spec.rb +19 -18
- data/spec/hutch_spec.rb +9 -11
- data/spec/spec_helper.rb +1 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 803369cb7619eae888379612cf278d49133a3f68
|
4
|
+
data.tar.gz: 4eb4a16d6b7a12441b9e56d972ebcc413b5fec82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f557376e9db220197b6f6f99cd28621593ed3a5f0bc96e26c471fb40b7b54e3f79564cad661873b223c7688713112bbf5536b705efdb0d3aa4b86d88725cd6b
|
7
|
+
data.tar.gz: d497420735d0567a167290a9de4130d66635f4ef36d5b1f674ca77e0320956518014cbc91054d72b10dc16ef3c2edc5498e47dd7a1b6ddce4edb013a757158ff
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.10.0 — unreleased
|
2
|
+
|
3
|
+
### Bunny Update
|
4
|
+
|
5
|
+
Bunny is updated to `1.4.x`.
|
6
|
+
|
7
|
+
### Exceptions in Error Handlers Don't Prevent Nacks
|
8
|
+
|
9
|
+
Exceptions in error handlers no longer prevent messages from being
|
10
|
+
`basic.nack`-ed.
|
11
|
+
|
12
|
+
|
13
|
+
## 0.9.0 — May 13, 2014
|
2
14
|
|
3
15
|
### Platform-aware Signal Registration
|
4
16
|
|
data/hutch.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.expand_path('../lib/hutch/version', __FILE__)
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
|
-
gem.add_runtime_dependency 'bunny', '
|
4
|
+
gem.add_runtime_dependency 'bunny', '>= 1.5.1'
|
5
5
|
gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
|
6
6
|
gem.add_runtime_dependency 'multi_json', '~> 1.5'
|
7
|
-
gem.add_development_dependency 'rspec', '~>
|
7
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
8
8
|
gem.add_development_dependency 'simplecov', '~> 0.7.1'
|
9
9
|
|
10
10
|
gem.name = 'hutch'
|
data/lib/hutch/broker.rb
CHANGED
@@ -19,8 +19,11 @@ module Hutch
|
|
19
19
|
set_up_api_connection if options.fetch(:enable_http_api_use, true)
|
20
20
|
|
21
21
|
if block_given?
|
22
|
-
|
23
|
-
|
22
|
+
begin
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
disconnect
|
26
|
+
end
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
@@ -46,6 +49,16 @@ module Hutch
|
|
46
49
|
end
|
47
50
|
|
48
51
|
def open_connection!
|
52
|
+
if @config[:uri] && !@config[:uri].empty?
|
53
|
+
u = URI.parse(@config[:uri])
|
54
|
+
|
55
|
+
@config[:mq_host] = u.host
|
56
|
+
@config[:mq_port] = u.port
|
57
|
+
@config[:mq_vhost] = u.path.sub(/^\//, "")
|
58
|
+
@config[:mq_username] = u.user
|
59
|
+
@config[:mq_password] = u.password
|
60
|
+
end
|
61
|
+
|
49
62
|
host = @config[:mq_host]
|
50
63
|
port = @config[:mq_port]
|
51
64
|
vhost = @config[:mq_vhost]
|
@@ -57,7 +70,6 @@ module Hutch
|
|
57
70
|
protocol = tls ? "amqps://" : "amqp://"
|
58
71
|
sanitized_uri = "#{protocol}#{username}@#{host}:#{port}/#{vhost.sub(/^\//, '')}"
|
59
72
|
logger.info "connecting to rabbitmq (#{sanitized_uri})"
|
60
|
-
|
61
73
|
@connection = Bunny.new(host: host, port: port, vhost: vhost,
|
62
74
|
tls: tls, tls_key: tls_key, tls_cert: tls_cert,
|
63
75
|
username: username, password: password,
|
@@ -68,6 +80,7 @@ module Hutch
|
|
68
80
|
@connection.start
|
69
81
|
end
|
70
82
|
|
83
|
+
logger.info "connected to RabbitMQ at #{host} as #{username}"
|
71
84
|
@connection
|
72
85
|
end
|
73
86
|
|
@@ -148,6 +161,14 @@ module Hutch
|
|
148
161
|
@channel.work_pool.kill
|
149
162
|
end
|
150
163
|
|
164
|
+
def requeue(delivery_tag)
|
165
|
+
@channel.reject(delivery_tag, true)
|
166
|
+
end
|
167
|
+
|
168
|
+
def reject(delivery_tag, requeue=false)
|
169
|
+
@channel.reject(delivery_tag, requeue)
|
170
|
+
end
|
171
|
+
|
151
172
|
def ack(delivery_tag)
|
152
173
|
@channel.ack(delivery_tag, false)
|
153
174
|
end
|
data/lib/hutch/cli.rb
CHANGED
@@ -12,7 +12,11 @@ module Hutch
|
|
12
12
|
def run(argv = ARGV)
|
13
13
|
parse_options(argv)
|
14
14
|
|
15
|
-
|
15
|
+
::Process.daemon(true) if Hutch::Config.daemonise
|
16
|
+
|
17
|
+
write_pid if Hutch::Config.pidfile
|
18
|
+
|
19
|
+
Hutch.logger.info "hutch booted with pid #{::Process.pid}"
|
16
20
|
|
17
21
|
if load_app && start_work_loop == :success
|
18
22
|
# If we got here, the worker was shut down nicely
|
@@ -170,6 +174,14 @@ module Hutch
|
|
170
174
|
Hutch::Config.namespace = namespace
|
171
175
|
end
|
172
176
|
|
177
|
+
opts.on('-d', '--daemonise', 'Daemonise') do |daemonise|
|
178
|
+
Hutch::Config.daemonise = daemonise
|
179
|
+
end
|
180
|
+
|
181
|
+
opts.on('--pidfile PIDFILE', 'Pidfile') do |pidfile|
|
182
|
+
Hutch::Config.pidfile = pidfile
|
183
|
+
end
|
184
|
+
|
173
185
|
opts.on('--version', 'Print the version and exit') do
|
174
186
|
puts "hutch v#{VERSION}"
|
175
187
|
exit 0
|
@@ -181,5 +193,12 @@ module Hutch
|
|
181
193
|
end
|
182
194
|
end.parse!(args)
|
183
195
|
end
|
196
|
+
|
197
|
+
def write_pid
|
198
|
+
pidfile = File.expand_path(Hutch::Config.pidfile)
|
199
|
+
Hutch.logger.info "writing pid in #{pidfile}"
|
200
|
+
File.open(pidfile, 'w') { |f| f.puts ::Process.pid }
|
201
|
+
end
|
202
|
+
|
184
203
|
end
|
185
204
|
end
|
data/lib/hutch/config.rb
CHANGED
@@ -7,7 +7,7 @@ module Hutch
|
|
7
7
|
module Config
|
8
8
|
require 'yaml'
|
9
9
|
|
10
|
-
def self.initialize
|
10
|
+
def self.initialize(params={})
|
11
11
|
@config = {
|
12
12
|
mq_host: 'localhost',
|
13
13
|
mq_port: 5672,
|
@@ -21,13 +21,18 @@ module Hutch
|
|
21
21
|
mq_api_host: 'localhost',
|
22
22
|
mq_api_port: 15672,
|
23
23
|
mq_api_ssl: false,
|
24
|
+
# placeholder, allows specifying connection parameters
|
25
|
+
# as a URI.
|
26
|
+
uri: nil,
|
24
27
|
log_level: Logger::INFO,
|
25
28
|
require_paths: [],
|
26
29
|
autoload_rails: true,
|
27
30
|
error_handlers: [Hutch::ErrorHandlers::Logger.new],
|
28
31
|
namespace: nil,
|
32
|
+
daemonise: false,
|
33
|
+
pidfile: nil,
|
29
34
|
channel_prefetch: 0
|
30
|
-
}
|
35
|
+
}.merge(params)
|
31
36
|
end
|
32
37
|
|
33
38
|
def self.get(attr)
|
data/lib/hutch/consumer.rb
CHANGED
@@ -5,11 +5,21 @@ module Hutch
|
|
5
5
|
# gain a class method called `consume`, which should be used to register
|
6
6
|
# the routing keys a consumer is interested in.
|
7
7
|
module Consumer
|
8
|
+
attr_accessor :broker, :delivery_info
|
9
|
+
|
8
10
|
def self.included(base)
|
9
11
|
base.extend(ClassMethods)
|
10
12
|
Hutch.register_consumer(base)
|
11
13
|
end
|
12
14
|
|
15
|
+
def reject!
|
16
|
+
broker.reject(delivery_info.delivery_tag)
|
17
|
+
end
|
18
|
+
|
19
|
+
def requeue!
|
20
|
+
broker.requeue(delivery_info.delivery_tag)
|
21
|
+
end
|
22
|
+
|
13
23
|
module ClassMethods
|
14
24
|
# Add one or more routing keys to the set of routing keys the consumer
|
15
25
|
# wants to subscribe to.
|
@@ -5,7 +5,7 @@ module Hutch
|
|
5
5
|
class Logger
|
6
6
|
include Logging
|
7
7
|
|
8
|
-
def handle(message_id, consumer, ex)
|
8
|
+
def handle(message_id, payload, consumer, ex)
|
9
9
|
prefix = "message(#{message_id || '-'}): "
|
10
10
|
logger.error prefix + "error in consumer '#{consumer}'"
|
11
11
|
logger.error prefix + "#{ex.class} - #{ex.message}"
|
@@ -12,7 +12,7 @@ module Hutch
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def handle(message_id, consumer, ex)
|
15
|
+
def handle(message_id, payload, consumer, ex)
|
16
16
|
prefix = "message(#{message_id || '-'}): "
|
17
17
|
logger.error prefix + "Logging event to Sentry"
|
18
18
|
logger.error prefix + "#{ex.class} - #{ex.message}"
|
data/lib/hutch/message.rb
CHANGED
data/lib/hutch/version.rb
CHANGED
data/lib/hutch/worker.rb
CHANGED
@@ -81,17 +81,17 @@ module Hutch
|
|
81
81
|
broker = @broker
|
82
82
|
begin
|
83
83
|
message = Message.new(delivery_info, properties, payload)
|
84
|
-
consumer.new.process(message)
|
84
|
+
consumer.new.tap { |c| c.broker, c.delivery_info = @broker, delivery_info }.process(message)
|
85
85
|
broker.ack(delivery_info.delivery_tag)
|
86
86
|
rescue StandardError => ex
|
87
|
-
handle_error(properties.message_id, consumer, ex)
|
88
87
|
broker.nack(delivery_info.delivery_tag)
|
88
|
+
handle_error(properties.message_id, payload, consumer, ex)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
def handle_error(message_id, consumer, ex)
|
92
|
+
def handle_error(message_id, payload, consumer, ex)
|
93
93
|
Hutch::Config[:error_handlers].each do |backend|
|
94
|
-
backend.handle(message_id, consumer, ex)
|
94
|
+
backend.handle(message_id, payload, consumer, ex)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
data/spec/hutch/broker_spec.rb
CHANGED
@@ -6,37 +6,48 @@ describe Hutch::Broker do
|
|
6
6
|
subject(:broker) { Hutch::Broker.new(config) }
|
7
7
|
|
8
8
|
describe '#connect' do
|
9
|
-
before { broker.
|
10
|
-
before { broker.
|
11
|
-
before { broker.
|
9
|
+
before { allow(broker).to receive(:set_up_amqp_connection) }
|
10
|
+
before { allow(broker).to receive(:set_up_api_connection) }
|
11
|
+
before { allow(broker).to receive(:disconnect) }
|
12
12
|
|
13
13
|
it 'sets up the amqp connection' do
|
14
|
-
broker.
|
14
|
+
expect(broker).to receive(:set_up_amqp_connection)
|
15
15
|
broker.connect
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'sets up the api connection' do
|
19
|
-
broker.
|
19
|
+
expect(broker).to receive(:set_up_api_connection)
|
20
20
|
broker.connect
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'does not disconnect' do
|
24
|
-
broker.
|
24
|
+
expect(broker).not_to receive(:disconnect)
|
25
25
|
broker.connect
|
26
26
|
end
|
27
27
|
|
28
28
|
context 'when given a block' do
|
29
29
|
it 'disconnects' do
|
30
|
-
broker.
|
30
|
+
expect(broker).to receive(:disconnect).once
|
31
31
|
broker.connect { }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
context 'when given a block that fails' do
|
36
|
+
let(:exception) { Class.new(StandardError) }
|
37
|
+
|
38
|
+
it 'disconnects' do
|
39
|
+
expect(broker).to receive(:disconnect).once
|
40
|
+
expect do
|
41
|
+
broker.connect { fail exception }
|
42
|
+
end.to raise_error(exception)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
35
46
|
context "with options" do
|
36
47
|
let(:options) { { enable_http_api_use: false } }
|
37
48
|
|
38
49
|
it "doesnt set up api" do
|
39
|
-
broker.
|
50
|
+
expect(broker).not_to receive(:set_up_api_connection)
|
40
51
|
broker.connect options
|
41
52
|
end
|
42
53
|
end
|
@@ -47,16 +58,27 @@ describe Hutch::Broker do
|
|
47
58
|
before { broker.set_up_amqp_connection }
|
48
59
|
after { broker.disconnect }
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
|
61
|
+
describe '#connection' do
|
62
|
+
subject { super().connection }
|
63
|
+
it { is_expected.to be_a Bunny::Session }
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#channel' do
|
67
|
+
subject { super().channel }
|
68
|
+
it { is_expected.to be_a Bunny::Channel }
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#exchange' do
|
72
|
+
subject { super().exchange }
|
73
|
+
it { is_expected.to be_a Bunny::Exchange }
|
74
|
+
end
|
53
75
|
end
|
54
76
|
|
55
77
|
context 'when given invalid details' do
|
56
78
|
before { config[:mq_host] = 'notarealhost' }
|
57
79
|
let(:set_up_amqp_connection) { ->{ broker.set_up_amqp_connection } }
|
58
80
|
|
59
|
-
specify { set_up_amqp_connection.
|
81
|
+
specify { expect(set_up_amqp_connection).to raise_error }
|
60
82
|
end
|
61
83
|
|
62
84
|
context 'with channel_prefetch set' do
|
@@ -65,7 +87,8 @@ describe Hutch::Broker do
|
|
65
87
|
after { broker.disconnect }
|
66
88
|
|
67
89
|
it "set's channel's prefetch" do
|
68
|
-
Bunny::Channel
|
90
|
+
expect_any_instance_of(Bunny::Channel).
|
91
|
+
to receive(:prefetch).with(prefetch_value)
|
69
92
|
broker.set_up_amqp_connection
|
70
93
|
end
|
71
94
|
end
|
@@ -76,7 +99,10 @@ describe Hutch::Broker do
|
|
76
99
|
before { broker.set_up_api_connection }
|
77
100
|
after { broker.disconnect }
|
78
101
|
|
79
|
-
|
102
|
+
describe '#api_client' do
|
103
|
+
subject { super().api_client }
|
104
|
+
it { is_expected.to be_a CarrotTop }
|
105
|
+
end
|
80
106
|
end
|
81
107
|
|
82
108
|
context 'when given invalid details' do
|
@@ -84,17 +110,19 @@ describe Hutch::Broker do
|
|
84
110
|
after { broker.disconnect }
|
85
111
|
let(:set_up_api_connection) { ->{ broker.set_up_api_connection } }
|
86
112
|
|
87
|
-
specify { set_up_api_connection.
|
113
|
+
specify { expect(set_up_api_connection).to raise_error }
|
88
114
|
end
|
89
115
|
end
|
90
116
|
|
91
117
|
describe '#queue' do
|
92
118
|
let(:channel) { double('Channel') }
|
93
|
-
before { broker.
|
119
|
+
before { allow(broker).to receive(:channel) { channel } }
|
94
120
|
|
95
121
|
it 'applies a global namespace' do
|
96
122
|
config[:namespace] = 'mirror-all.service'
|
97
|
-
broker.channel.
|
123
|
+
expect(broker.channel).to receive(:queue) do |*args|
|
124
|
+
args.first == 'mirror-all.service:test'
|
125
|
+
end
|
98
126
|
broker.queue('test')
|
99
127
|
end
|
100
128
|
end
|
@@ -104,7 +132,10 @@ describe Hutch::Broker do
|
|
104
132
|
subject { broker.bindings }
|
105
133
|
|
106
134
|
context 'with no bindings' do
|
107
|
-
|
135
|
+
describe '#keys' do
|
136
|
+
subject { super().keys }
|
137
|
+
it { is_expected.not_to include 'test' }
|
138
|
+
end
|
108
139
|
end
|
109
140
|
|
110
141
|
context 'with a binding' do
|
@@ -114,7 +145,7 @@ describe Hutch::Broker do
|
|
114
145
|
queue.unbind(broker.exchange, routing_key: 'key').delete
|
115
146
|
end
|
116
147
|
|
117
|
-
it {
|
148
|
+
it { is_expected.to include({ 'test' => ['key'] }) }
|
118
149
|
end
|
119
150
|
end
|
120
151
|
|
@@ -124,17 +155,17 @@ describe Hutch::Broker do
|
|
124
155
|
|
125
156
|
let(:routing_keys) { %w( a b c ) }
|
126
157
|
let(:queue) { double('Queue', bind: nil, unbind: nil, name: 'consumer') }
|
127
|
-
before { broker.
|
158
|
+
before { allow(broker).to receive(:bindings).and_return('consumer' => ['d']) }
|
128
159
|
|
129
160
|
it 'calls bind for each routing key' do
|
130
161
|
routing_keys.each do |key|
|
131
|
-
queue.
|
162
|
+
expect(queue).to receive(:bind).with(broker.exchange, routing_key: key)
|
132
163
|
end
|
133
164
|
broker.bind_queue(queue, routing_keys)
|
134
165
|
end
|
135
166
|
|
136
167
|
it 'calls unbind for each redundant existing binding' do
|
137
|
-
queue.
|
168
|
+
expect(queue).to receive(:unbind).with(broker.exchange, routing_key: 'd')
|
138
169
|
broker.bind_queue(queue, routing_keys)
|
139
170
|
end
|
140
171
|
|
@@ -142,29 +173,29 @@ describe Hutch::Broker do
|
|
142
173
|
let(:queue) { broker.queue('consumer') }
|
143
174
|
let(:routing_key) { 'key' }
|
144
175
|
|
145
|
-
before { broker.
|
176
|
+
before { allow(broker).to receive(:bindings).and_call_original }
|
146
177
|
before { queue.bind(broker.exchange, routing_key: 'redundant-key') }
|
147
178
|
after { queue.unbind(broker.exchange, routing_key: routing_key).delete }
|
148
179
|
|
149
180
|
it 'results in the correct bindings' do
|
150
181
|
broker.bind_queue(queue, [routing_key])
|
151
|
-
broker.bindings.
|
182
|
+
expect(broker.bindings).to include({ queue.name => [routing_key] })
|
152
183
|
end
|
153
184
|
end
|
154
185
|
end
|
155
186
|
|
156
187
|
describe '#wait_on_threads' do
|
157
188
|
let(:thread) { double('Thread') }
|
158
|
-
before { broker.
|
189
|
+
before { allow(broker).to receive(:work_pool_threads).and_return(threads) }
|
159
190
|
|
160
191
|
context 'when all threads finish within the timeout' do
|
161
192
|
let(:threads) { [double(join: thread), double(join: thread)] }
|
162
|
-
specify { expect(broker.wait_on_threads(1)).to
|
193
|
+
specify { expect(broker.wait_on_threads(1)).to be_truthy }
|
163
194
|
end
|
164
195
|
|
165
196
|
context 'when timeout expires for one thread' do
|
166
197
|
let(:threads) { [double(join: thread), double(join: nil)] }
|
167
|
-
specify { expect(broker.wait_on_threads(1)).to
|
198
|
+
specify { expect(broker.wait_on_threads(1)).to be_falsey }
|
168
199
|
end
|
169
200
|
end
|
170
201
|
|
@@ -174,12 +205,12 @@ describe Hutch::Broker do
|
|
174
205
|
after { broker.disconnect }
|
175
206
|
|
176
207
|
it 'publishes to the exchange' do
|
177
|
-
broker.exchange.
|
208
|
+
expect(broker.exchange).to receive(:publish).once
|
178
209
|
broker.publish('test.key', 'message')
|
179
210
|
end
|
180
211
|
|
181
212
|
it 'sets default properties' do
|
182
|
-
broker.exchange.
|
213
|
+
expect(broker.exchange).to receive(:publish).with(
|
183
214
|
JSON.dump("message"),
|
184
215
|
hash_including(
|
185
216
|
persistent: true,
|
@@ -192,29 +223,31 @@ describe Hutch::Broker do
|
|
192
223
|
end
|
193
224
|
|
194
225
|
it 'allows passing message properties' do
|
195
|
-
broker.exchange.
|
226
|
+
expect(broker.exchange).to receive(:publish).once
|
196
227
|
broker.publish('test.key', 'message', {expiration: "2000", persistent: false})
|
197
228
|
end
|
198
229
|
|
199
230
|
context 'when there are global properties' do
|
200
231
|
context 'as a hash' do
|
201
232
|
before do
|
202
|
-
Hutch.
|
233
|
+
allow(Hutch).to receive(:global_properties).and_return(app_id: 'app')
|
203
234
|
end
|
204
235
|
|
205
236
|
it 'merges the properties' do
|
206
|
-
broker.exchange
|
237
|
+
expect(broker.exchange).
|
238
|
+
to receive(:publish).with('"message"', hash_including(app_id: 'app'))
|
207
239
|
broker.publish('test.key', 'message')
|
208
240
|
end
|
209
241
|
end
|
210
242
|
|
211
243
|
context 'as a callable object' do
|
212
244
|
before do
|
213
|
-
Hutch.
|
245
|
+
allow(Hutch).to receive(:global_properties).and_return(proc { { app_id: 'app' } })
|
214
246
|
end
|
215
247
|
|
216
248
|
it 'calls the proc and merges the properties' do
|
217
|
-
broker.exchange
|
249
|
+
expect(broker.exchange).
|
250
|
+
to receive(:publish).with('"message"', hash_including(app_id: 'app'))
|
218
251
|
broker.publish('test.key', 'message')
|
219
252
|
end
|
220
253
|
end
|
@@ -228,7 +261,7 @@ describe Hutch::Broker do
|
|
228
261
|
end
|
229
262
|
|
230
263
|
it 'logs an error' do
|
231
|
-
broker.logger.
|
264
|
+
expect(broker.logger).to receive(:error)
|
232
265
|
broker.publish('test.key', 'message') rescue nil
|
233
266
|
end
|
234
267
|
end
|
data/spec/hutch/cli_spec.rb
CHANGED
@@ -8,7 +8,7 @@ describe Hutch::CLI do
|
|
8
8
|
context "--config" do
|
9
9
|
context "when the config file does not exist" do
|
10
10
|
let(:file) { "/path/to/nonexistant/file" }
|
11
|
-
before { STDERR.
|
11
|
+
before { allow(STDERR).to receive(:write) }
|
12
12
|
|
13
13
|
it "bails" do
|
14
14
|
expect {
|
@@ -23,7 +23,7 @@ describe Hutch::CLI do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "parses the config" do
|
26
|
-
Hutch::Config.
|
26
|
+
expect(Hutch::Config).to receive(:load_from_file)
|
27
27
|
cli.parse_options(["--config=#{file}"])
|
28
28
|
end
|
29
29
|
end
|
@@ -32,7 +32,7 @@ describe Hutch::CLI do
|
|
32
32
|
context "--mq-tls-key" do
|
33
33
|
context "when the keyfile file does not exist" do
|
34
34
|
let(:file) { "/path/to/nonexistant/file" }
|
35
|
-
before { STDERR.
|
35
|
+
before { allow(STDERR).to receive(:write) }
|
36
36
|
|
37
37
|
it "bails" do
|
38
38
|
expect {
|
@@ -47,7 +47,7 @@ describe Hutch::CLI do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
it "sets mq_tls_key to the file" do
|
50
|
-
Hutch::Config.
|
50
|
+
expect(Hutch::Config).to receive(:mq_tls_key=)
|
51
51
|
cli.parse_options(["--mq-tls-key=#{file}"])
|
52
52
|
end
|
53
53
|
end
|
@@ -56,7 +56,7 @@ describe Hutch::CLI do
|
|
56
56
|
context "--mq-tls-cert" do
|
57
57
|
context "when the certfile file does not exist" do
|
58
58
|
let(:file) { "/path/to/nonexistant/file" }
|
59
|
-
before { STDERR.
|
59
|
+
before { allow(STDERR).to receive(:write) }
|
60
60
|
|
61
61
|
it "bails" do
|
62
62
|
expect {
|
@@ -71,7 +71,7 @@ describe Hutch::CLI do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it "sets mq_tls_cert to the file" do
|
74
|
-
Hutch::Config.
|
74
|
+
expect(Hutch::Config).to receive(:mq_tls_cert=)
|
75
75
|
cli.parse_options(["--mq-tls-cert=#{file}"])
|
76
76
|
end
|
77
77
|
end
|
data/spec/hutch/config_spec.rb
CHANGED
@@ -9,18 +9,18 @@ describe Hutch::Config do
|
|
9
9
|
subject { Hutch::Config.get(:mq_host) }
|
10
10
|
|
11
11
|
context 'with no overridden value' do
|
12
|
-
it {
|
12
|
+
it { is_expected.to eq('localhost') }
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'with an overridden value' do
|
16
|
-
before { Hutch::Config.
|
17
|
-
it {
|
16
|
+
before { allow(Hutch::Config).to receive_messages(user_config: { mq_host: new_value }) }
|
17
|
+
it { is_expected.to eq(new_value) }
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
context 'for invalid attributes' do
|
22
22
|
let(:invalid_get) { ->{ Hutch::Config.get(:invalid_attr) } }
|
23
|
-
specify { invalid_get.
|
23
|
+
specify { expect(invalid_get).to raise_error Hutch::UnknownAttributeError }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -30,41 +30,41 @@ describe Hutch::Config do
|
|
30
30
|
subject { Hutch::Config.user_config[:mq_host] }
|
31
31
|
|
32
32
|
context 'sets value in user config hash' do
|
33
|
-
it {
|
33
|
+
it { is_expected.to eq(new_value) }
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
context 'for invalid attributes' do
|
38
38
|
let(:invalid_set) { ->{ Hutch::Config.set(:invalid_attr, new_value) } }
|
39
|
-
specify { invalid_set.
|
39
|
+
specify { expect(invalid_set).to raise_error Hutch::UnknownAttributeError }
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
describe 'a magic getter' do
|
44
44
|
context 'for a valid attribute' do
|
45
45
|
it 'calls get' do
|
46
|
-
Hutch::Config.
|
46
|
+
expect(Hutch::Config).to receive(:get).with(:mq_host)
|
47
47
|
Hutch::Config.mq_host
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
context 'for an invalid attribute' do
|
52
52
|
let(:invalid_getter) { ->{ Hutch::Config.invalid_attr } }
|
53
|
-
specify { invalid_getter.
|
53
|
+
specify { expect(invalid_getter).to raise_error NoMethodError }
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
describe 'a magic setter' do
|
58
58
|
context 'for a valid attribute' do
|
59
59
|
it 'calls set' do
|
60
|
-
Hutch::Config.
|
60
|
+
expect(Hutch::Config).to receive(:set).with(:mq_host, new_value)
|
61
61
|
Hutch::Config.mq_host = new_value
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'for an invalid attribute' do
|
66
66
|
let(:invalid_setter) { ->{ Hutch::Config.invalid_attr = new_value } }
|
67
|
-
specify { invalid_setter.
|
67
|
+
specify { expect(invalid_setter).to raise_error NoMethodError }
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -92,8 +92,8 @@ describe Hutch::Config do
|
|
92
92
|
|
93
93
|
it 'loads in the config data' do
|
94
94
|
Hutch::Config.load_from_file(file)
|
95
|
-
Hutch::Config.mq_host.
|
96
|
-
Hutch::Config.mq_username.
|
95
|
+
expect(Hutch::Config.mq_host).to eq host
|
96
|
+
expect(Hutch::Config.mq_username).to eq username
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
data/spec/hutch/consumer_spec.rb
CHANGED
@@ -29,8 +29,8 @@ describe Hutch::Consumer do
|
|
29
29
|
|
30
30
|
describe 'module inclusion' do
|
31
31
|
it 'registers the class as a consumer' do
|
32
|
-
Hutch.
|
33
|
-
klass.
|
32
|
+
expect(Hutch).to receive(:register_consumer) do |klass|
|
33
|
+
expect(klass).to eq(simple_consumer)
|
34
34
|
end
|
35
35
|
|
36
36
|
simple_consumer
|
@@ -40,20 +40,24 @@ describe Hutch::Consumer do
|
|
40
40
|
|
41
41
|
describe '.consume' do
|
42
42
|
it 'saves the routing key to the consumer' do
|
43
|
-
simple_consumer.routing_keys.
|
43
|
+
expect(simple_consumer.routing_keys).to include 'hutch.test1'
|
44
44
|
end
|
45
45
|
|
46
46
|
context 'with multiple routing keys' do
|
47
47
|
it 'registers the class once for each routing key' do
|
48
|
-
complex_consumer.routing_keys.
|
49
|
-
complex_consumer.routing_keys.
|
48
|
+
expect(complex_consumer.routing_keys).to include 'hutch.test1'
|
49
|
+
expect(complex_consumer.routing_keys).to include 'hutch.test2'
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
context 'when given the same routing key multiple times' do
|
54
54
|
subject { simple_consumer.routing_keys }
|
55
55
|
before { simple_consumer.consume 'hutch.test1' }
|
56
|
-
|
56
|
+
|
57
|
+
describe '#length' do
|
58
|
+
subject { super().length }
|
59
|
+
it { is_expected.to eq(1)}
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
@@ -72,7 +76,7 @@ describe Hutch::Consumer do
|
|
72
76
|
queue_name "bar"
|
73
77
|
end
|
74
78
|
|
75
|
-
Foo.get_queue_name.
|
79
|
+
expect(Foo.get_queue_name).to eq("bar")
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -84,7 +88,7 @@ describe Hutch::Consumer do
|
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
87
|
-
Foo::Bar.get_queue_name.
|
91
|
+
expect(Foo::Bar.get_queue_name).to eq('foo:bar')
|
88
92
|
end
|
89
93
|
|
90
94
|
it 'converts camelcase class names to snake case' do
|
@@ -92,7 +96,7 @@ describe Hutch::Consumer do
|
|
92
96
|
include Hutch::Consumer
|
93
97
|
end
|
94
98
|
|
95
|
-
FooBarBAZ.get_queue_name.
|
99
|
+
expect(FooBarBAZ.get_queue_name).to eq('foo_bar_baz')
|
96
100
|
end
|
97
101
|
end
|
98
102
|
end
|
@@ -4,12 +4,12 @@ describe Hutch::ErrorHandlers::Logger do
|
|
4
4
|
let(:error_handler) { Hutch::ErrorHandlers::Logger.new }
|
5
5
|
|
6
6
|
describe '#handle' do
|
7
|
-
let(:error) {
|
7
|
+
let(:error) { double(message: "Stuff went wrong", class: "RuntimeError",
|
8
8
|
backtrace: ["line 1", "line 2"]) }
|
9
9
|
|
10
10
|
it "logs three separate lines" do
|
11
|
-
Hutch::Logging.logger.
|
12
|
-
error_handler.handle("1",
|
11
|
+
expect(Hutch::Logging.logger).to receive(:error).exactly(3).times
|
12
|
+
error_handler.handle("1", "{}", double, error)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -13,8 +13,8 @@ describe Hutch::ErrorHandlers::Sentry do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "logs the error to Sentry" do
|
16
|
-
Raven.
|
17
|
-
error_handler.handle("1",
|
16
|
+
expect(Raven).to receive(:capture_exception).with(error)
|
17
|
+
error_handler.handle("1", "{}", double, error)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/spec/hutch/logger_spec.rb
CHANGED
@@ -11,16 +11,16 @@ describe Hutch::Logging do
|
|
11
11
|
context 'with the default logger' do
|
12
12
|
subject { Hutch::Logging.logger }
|
13
13
|
|
14
|
-
it {
|
14
|
+
it { is_expected.to be_instance_of(Logger) }
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'with a custom logger' do
|
18
|
-
let(:dummy_logger) {
|
18
|
+
let(:dummy_logger) { double("Dummy logger", warn: true, info: true) }
|
19
19
|
after { Hutch::Logging.setup_logger }
|
20
20
|
|
21
21
|
it "users the custom logger" do
|
22
22
|
Hutch::Logging.logger = dummy_logger
|
23
|
-
Hutch::Logging.logger.
|
23
|
+
expect(Hutch::Logging.logger).to eq(dummy_logger)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/spec/hutch/message_spec.rb
CHANGED
@@ -7,17 +7,20 @@ describe Hutch::Message do
|
|
7
7
|
let(:json_body) { MultiJson.dump(body) }
|
8
8
|
subject(:message) { Hutch::Message.new(delivery_info, props, json_body) }
|
9
9
|
|
10
|
-
|
10
|
+
describe '#body' do
|
11
|
+
subject { super().body }
|
12
|
+
it { is_expected.to eq(body) }
|
13
|
+
end
|
11
14
|
|
12
15
|
describe '[]' do
|
13
16
|
subject { message[:foo] }
|
14
|
-
it {
|
17
|
+
it { is_expected.to eq('bar') }
|
15
18
|
end
|
16
19
|
|
17
20
|
[:message_id, :timestamp].each do |method|
|
18
21
|
describe method.to_s do
|
19
22
|
it 'delegates to @properties' do
|
20
|
-
props.
|
23
|
+
expect(props).to receive(method)
|
21
24
|
message.send(method)
|
22
25
|
end
|
23
26
|
end
|
@@ -26,7 +29,7 @@ describe Hutch::Message do
|
|
26
29
|
[:routing_key, :exchange].each do |method|
|
27
30
|
describe method.to_s do
|
28
31
|
it 'delegates to @delivery_info' do
|
29
|
-
delivery_info.
|
32
|
+
expect(delivery_info).to receive(method)
|
30
33
|
message.send(method)
|
31
34
|
end
|
32
35
|
end
|
data/spec/hutch/worker_spec.rb
CHANGED
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
require 'hutch/worker'
|
3
3
|
|
4
4
|
describe Hutch::Worker do
|
5
|
-
before { Raven.as_null_object }
|
6
5
|
let(:consumer) { double('Consumer', routing_keys: %w( a b c ),
|
7
6
|
get_queue_name: 'consumer') }
|
8
7
|
let(:consumers) { [consumer, double('Consumer')] }
|
@@ -12,7 +11,7 @@ describe Hutch::Worker do
|
|
12
11
|
describe '#setup_queues' do
|
13
12
|
it 'sets up queues for each of the consumers' do
|
14
13
|
consumers.each do |consumer|
|
15
|
-
worker.
|
14
|
+
expect(worker).to receive(:setup_queue).with(consumer)
|
16
15
|
end
|
17
16
|
worker.setup_queues
|
18
17
|
end
|
@@ -20,21 +19,21 @@ describe Hutch::Worker do
|
|
20
19
|
|
21
20
|
describe '#setup_queue' do
|
22
21
|
let(:queue) { double('Queue', bind: nil, subscribe: nil) }
|
23
|
-
before { worker.
|
24
|
-
before { broker.
|
22
|
+
before { allow(worker).to receive_messages(consumer_queue: queue) }
|
23
|
+
before { allow(broker).to receive_messages(queue: queue, bind_queue: nil) }
|
25
24
|
|
26
25
|
it 'creates a queue' do
|
27
|
-
broker.
|
26
|
+
expect(broker).to receive(:queue).with(consumer.get_queue_name).and_return(queue)
|
28
27
|
worker.setup_queue(consumer)
|
29
28
|
end
|
30
29
|
|
31
30
|
it 'binds the queue to each of the routing keys' do
|
32
|
-
broker.
|
31
|
+
expect(broker).to receive(:bind_queue).with(queue, %w( a b c ))
|
33
32
|
worker.setup_queue(consumer)
|
34
33
|
end
|
35
34
|
|
36
35
|
it 'sets up a subscription' do
|
37
|
-
queue.
|
36
|
+
expect(queue).to receive(:subscribe).with(ack: true)
|
38
37
|
worker.setup_queue(consumer)
|
39
38
|
end
|
40
39
|
end
|
@@ -45,34 +44,36 @@ describe Hutch::Worker do
|
|
45
44
|
let(:delivery_info) { double('Delivery Info', routing_key: '',
|
46
45
|
delivery_tag: 'dt') }
|
47
46
|
let(:properties) { double('Properties', message_id: nil) }
|
48
|
-
before { consumer.
|
49
|
-
before { broker.
|
50
|
-
before { broker.
|
47
|
+
before { allow(consumer).to receive_messages(new: consumer_instance) }
|
48
|
+
before { allow(broker).to receive(:ack) }
|
49
|
+
before { allow(broker).to receive(:nack) }
|
50
|
+
before { allow(consumer_instance).to receive(:broker=) }
|
51
|
+
before { allow(consumer_instance).to receive(:delivery_info=) }
|
51
52
|
|
52
53
|
it 'passes the message to the consumer' do
|
53
|
-
consumer_instance.
|
54
|
+
expect(consumer_instance).to receive(:process).
|
54
55
|
with(an_instance_of(Hutch::Message))
|
55
56
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
56
57
|
end
|
57
58
|
|
58
59
|
it 'acknowledges the message' do
|
59
|
-
consumer_instance.
|
60
|
-
broker.
|
60
|
+
allow(consumer_instance).to receive(:process)
|
61
|
+
expect(broker).to receive(:ack).with(delivery_info.delivery_tag)
|
61
62
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
62
63
|
end
|
63
64
|
|
64
65
|
context 'when the consumer raises an exception' do
|
65
|
-
before { consumer_instance.
|
66
|
+
before { allow(consumer_instance).to receive(:process).and_raise('a consumer error') }
|
66
67
|
|
67
68
|
it 'logs the error' do
|
68
69
|
Hutch::Config[:error_handlers].each do |backend|
|
69
|
-
backend.
|
70
|
+
expect(backend).to receive(:handle)
|
70
71
|
end
|
71
72
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
72
73
|
end
|
73
74
|
|
74
75
|
it 'rejects the message' do
|
75
|
-
broker.
|
76
|
+
expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
|
76
77
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
77
78
|
end
|
78
79
|
end
|
@@ -82,13 +83,13 @@ describe Hutch::Worker do
|
|
82
83
|
|
83
84
|
it 'logs the error' do
|
84
85
|
Hutch::Config[:error_handlers].each do |backend|
|
85
|
-
backend.
|
86
|
+
expect(backend).to receive(:handle)
|
86
87
|
end
|
87
88
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
88
89
|
end
|
89
90
|
|
90
91
|
it 'rejects the message' do
|
91
|
-
broker.
|
92
|
+
expect(broker).to receive(:nack).with(delivery_info.delivery_tag)
|
92
93
|
worker.handle_message(consumer, delivery_info, properties, payload)
|
93
94
|
end
|
94
95
|
end
|
data/spec/hutch_spec.rb
CHANGED
@@ -8,8 +8,8 @@ describe Hutch do
|
|
8
8
|
it 'saves the consumers in the global consumer list' do
|
9
9
|
Hutch.register_consumer(consumer_a)
|
10
10
|
Hutch.register_consumer(consumer_b)
|
11
|
-
Hutch.consumers.
|
12
|
-
Hutch.consumers.
|
11
|
+
expect(Hutch.consumers).to include consumer_a
|
12
|
+
expect(Hutch.consumers).to include consumer_b
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -21,8 +21,8 @@ describe Hutch do
|
|
21
21
|
let(:action) { Hutch.connect(options, config) }
|
22
22
|
|
23
23
|
it 'passes options and config' do
|
24
|
-
Hutch::Broker.
|
25
|
-
broker.
|
24
|
+
expect(Hutch::Broker).to receive(:new).with(config).and_return broker
|
25
|
+
expect(broker).to receive(:connect).with options
|
26
26
|
|
27
27
|
action
|
28
28
|
end
|
@@ -30,15 +30,15 @@ describe Hutch do
|
|
30
30
|
it 'sets @connect' do
|
31
31
|
action
|
32
32
|
|
33
|
-
expect(Hutch.connected?).to
|
33
|
+
expect(Hutch.connected?).to be_truthy
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
context 'connected' do
|
38
|
-
before { Hutch.
|
38
|
+
before { allow(Hutch).to receive(:connected?).and_return true }
|
39
39
|
|
40
40
|
it 'does not reconnect' do
|
41
|
-
Hutch::Broker.
|
41
|
+
expect(Hutch::Broker).not_to receive :new
|
42
42
|
Hutch.connect
|
43
43
|
end
|
44
44
|
end
|
@@ -48,12 +48,10 @@ describe Hutch do
|
|
48
48
|
let(:broker) { double(Hutch::Broker) }
|
49
49
|
let(:args) { ['test.key', 'message', { headers: { foo: 'bar' } }] }
|
50
50
|
|
51
|
-
before
|
52
|
-
Hutch.stub broker: broker
|
53
|
-
end
|
51
|
+
before { allow(Hutch).to receive(:broker).and_return(broker) }
|
54
52
|
|
55
53
|
it 'delegates to Hutch::Broker#publish' do
|
56
|
-
broker.
|
54
|
+
expect(broker).to receive(:publish).with(*args)
|
57
55
|
Hutch.publish(*args)
|
58
56
|
end
|
59
57
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hutch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harry Marr
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.5.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.5.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: carrot-top
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '3.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '3.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|