hutch 0.19.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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +438 -0
- data/Gemfile +22 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +317 -0
- data/Rakefile +14 -0
- data/bin/hutch +8 -0
- data/circle.yml +3 -0
- data/examples/consumer.rb +13 -0
- data/examples/producer.rb +10 -0
- data/hutch.gemspec +30 -0
- data/lib/hutch.rb +62 -0
- data/lib/hutch/adapter.rb +11 -0
- data/lib/hutch/adapters/bunny.rb +33 -0
- data/lib/hutch/adapters/march_hare.rb +37 -0
- data/lib/hutch/broker.rb +374 -0
- data/lib/hutch/cli.rb +205 -0
- data/lib/hutch/config.rb +125 -0
- data/lib/hutch/consumer.rb +75 -0
- data/lib/hutch/error_handlers.rb +8 -0
- data/lib/hutch/error_handlers/airbrake.rb +26 -0
- data/lib/hutch/error_handlers/honeybadger.rb +28 -0
- data/lib/hutch/error_handlers/logger.rb +16 -0
- data/lib/hutch/error_handlers/sentry.rb +23 -0
- data/lib/hutch/exceptions.rb +7 -0
- data/lib/hutch/logging.rb +32 -0
- data/lib/hutch/message.rb +31 -0
- data/lib/hutch/serializers/identity.rb +19 -0
- data/lib/hutch/serializers/json.rb +22 -0
- data/lib/hutch/tracers.rb +6 -0
- data/lib/hutch/tracers/newrelic.rb +19 -0
- data/lib/hutch/tracers/null_tracer.rb +15 -0
- data/lib/hutch/version.rb +4 -0
- data/lib/hutch/worker.rb +143 -0
- data/spec/hutch/broker_spec.rb +377 -0
- data/spec/hutch/cli_spec.rb +80 -0
- data/spec/hutch/config_spec.rb +126 -0
- data/spec/hutch/consumer_spec.rb +130 -0
- data/spec/hutch/error_handlers/airbrake_spec.rb +34 -0
- data/spec/hutch/error_handlers/honeybadger_spec.rb +36 -0
- data/spec/hutch/error_handlers/logger_spec.rb +15 -0
- data/spec/hutch/error_handlers/sentry_spec.rb +20 -0
- data/spec/hutch/logger_spec.rb +28 -0
- data/spec/hutch/message_spec.rb +38 -0
- data/spec/hutch/serializers/json_spec.rb +17 -0
- data/spec/hutch/worker_spec.rb +99 -0
- data/spec/hutch_spec.rb +87 -0
- data/spec/spec_helper.rb +40 -0
- metadata +194 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'hutch/cli'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe Hutch::CLI do
|
5
|
+
let(:cli) { Hutch::CLI.new }
|
6
|
+
|
7
|
+
describe "#parse_options" do
|
8
|
+
context "--config" do
|
9
|
+
context "when the config file does not exist" do
|
10
|
+
let(:file) { "/path/to/nonexistant/file" }
|
11
|
+
before { allow(STDERR).to receive(:write) }
|
12
|
+
|
13
|
+
it "bails" do
|
14
|
+
expect {
|
15
|
+
cli.parse_options(["--config=#{file}"])
|
16
|
+
}.to raise_error SystemExit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when the config file exists" do
|
21
|
+
let(:file) do
|
22
|
+
Tempfile.new("hutch-test-config.yaml").to_path
|
23
|
+
end
|
24
|
+
|
25
|
+
it "parses the config" do
|
26
|
+
expect(Hutch::Config).to receive(:load_from_file)
|
27
|
+
cli.parse_options(["--config=#{file}"])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "--mq-tls-key" do
|
33
|
+
context "when the keyfile file does not exist" do
|
34
|
+
let(:file) { "/path/to/nonexistant/file" }
|
35
|
+
before { allow(STDERR).to receive(:write) }
|
36
|
+
|
37
|
+
it "bails" do
|
38
|
+
expect {
|
39
|
+
cli.parse_options(["--mq-tls-key=#{file}"])
|
40
|
+
}.to raise_error SystemExit
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when the keyfile file exists" do
|
45
|
+
let(:file) do
|
46
|
+
Tempfile.new("hutch-test-key.pem").to_path
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sets mq_tls_key to the file" do
|
50
|
+
expect(Hutch::Config).to receive(:mq_tls_key=)
|
51
|
+
cli.parse_options(["--mq-tls-key=#{file}"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "--mq-tls-cert" do
|
57
|
+
context "when the certfile file does not exist" do
|
58
|
+
let(:file) { "/path/to/nonexistant/file" }
|
59
|
+
before { allow(STDERR).to receive(:write) }
|
60
|
+
|
61
|
+
it "bails" do
|
62
|
+
expect {
|
63
|
+
cli.parse_options(["--mq-tls-cert=#{file}"])
|
64
|
+
}.to raise_error SystemExit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when the certfile file exists" do
|
69
|
+
let(:file) do
|
70
|
+
Tempfile.new("hutch-test-cert.pem").to_path
|
71
|
+
end
|
72
|
+
|
73
|
+
it "sets mq_tls_cert to the file" do
|
74
|
+
expect(Hutch::Config).to receive(:mq_tls_cert=)
|
75
|
+
cli.parse_options(["--mq-tls-cert=#{file}"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'hutch/config'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe Hutch::Config do
|
5
|
+
let(:new_value) { 'not-localhost' }
|
6
|
+
|
7
|
+
describe '.get' do
|
8
|
+
context 'for valid attributes' do
|
9
|
+
subject { Hutch::Config.get(:mq_host) }
|
10
|
+
|
11
|
+
context 'with no overridden value' do
|
12
|
+
it { is_expected.to eq('localhost') }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with an overridden value' do
|
16
|
+
before { allow(Hutch::Config).to receive_messages(user_config: { mq_host: new_value }) }
|
17
|
+
it { is_expected.to eq(new_value) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'for invalid attributes' do
|
22
|
+
let(:invalid_get) { ->{ Hutch::Config.get(:invalid_attr) } }
|
23
|
+
specify { expect(invalid_get).to raise_error Hutch::UnknownAttributeError }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.set' do
|
28
|
+
context 'for valid attributes' do
|
29
|
+
before { Hutch::Config.set(:mq_host, new_value) }
|
30
|
+
subject { Hutch::Config.user_config[:mq_host] }
|
31
|
+
|
32
|
+
context 'sets value in user config hash' do
|
33
|
+
it { is_expected.to eq(new_value) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'for invalid attributes' do
|
38
|
+
let(:invalid_set) { ->{ Hutch::Config.set(:invalid_attr, new_value) } }
|
39
|
+
specify { expect(invalid_set).to raise_error Hutch::UnknownAttributeError }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'a magic getter' do
|
44
|
+
context 'for a valid attribute' do
|
45
|
+
it 'calls get' do
|
46
|
+
expect(Hutch::Config).to receive(:get).with(:mq_host)
|
47
|
+
Hutch::Config.mq_host
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'for an invalid attribute' do
|
52
|
+
let(:invalid_getter) { ->{ Hutch::Config.invalid_attr } }
|
53
|
+
specify { expect(invalid_getter).to raise_error NoMethodError }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'a magic setter' do
|
58
|
+
context 'for a valid attribute' do
|
59
|
+
it 'calls set' do
|
60
|
+
expect(Hutch::Config).to receive(:set).with(:mq_host, new_value)
|
61
|
+
Hutch::Config.mq_host = new_value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'for an invalid attribute' do
|
66
|
+
let(:invalid_setter) { ->{ Hutch::Config.invalid_attr = new_value } }
|
67
|
+
specify { expect(invalid_setter).to raise_error NoMethodError }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.load_from_file' do
|
72
|
+
let(:host) { 'broker.yourhost.com' }
|
73
|
+
let(:username) { 'calvin' }
|
74
|
+
let(:file) do
|
75
|
+
Tempfile.new('configs.yaml').tap do |t|
|
76
|
+
t.write(YAML.dump(config_data))
|
77
|
+
t.rewind
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when an attribute is invalid' do
|
82
|
+
let(:config_data) { { random_attribute: 'socks' } }
|
83
|
+
it 'raises an error' do
|
84
|
+
expect {
|
85
|
+
Hutch::Config.load_from_file(file)
|
86
|
+
}.to raise_error(NoMethodError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when attributes are valid' do
|
91
|
+
let(:config_data) { { mq_host: host, mq_username: username } }
|
92
|
+
|
93
|
+
it 'loads in the config data' do
|
94
|
+
Hutch::Config.load_from_file(file)
|
95
|
+
expect(Hutch::Config.mq_host).to eq host
|
96
|
+
expect(Hutch::Config.mq_username).to eq username
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '.load_from_file' do
|
102
|
+
let(:host) { 'localhost' }
|
103
|
+
let(:username) { 'calvin' }
|
104
|
+
let(:file) do
|
105
|
+
Tempfile.new('configs.yaml').tap do |t|
|
106
|
+
t.write(config_contents)
|
107
|
+
t.rewind
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when using ERb' do
|
112
|
+
let(:config_contents) do
|
113
|
+
<<-YAML
|
114
|
+
mq_host: 'localhost'
|
115
|
+
mq_username: '<%= "calvin" %>'
|
116
|
+
YAML
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'loads in the config data' do
|
120
|
+
Hutch::Config.load_from_file(file)
|
121
|
+
expect(Hutch::Config.mq_host).to eq host
|
122
|
+
expect(Hutch::Config.mq_username).to eq username
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hutch::Consumer do
|
4
|
+
around(:each) do |example|
|
5
|
+
isolate_constants do
|
6
|
+
example.run
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:simple_consumer) do
|
11
|
+
unless defined? SimpleConsumer
|
12
|
+
class SimpleConsumer
|
13
|
+
include Hutch::Consumer
|
14
|
+
consume 'hutch.test1'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
SimpleConsumer
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:complex_consumer) do
|
21
|
+
unless defined? ComplexConsumer
|
22
|
+
class ComplexConsumer
|
23
|
+
include Hutch::Consumer
|
24
|
+
consume 'hutch.test1', 'hutch.test2'
|
25
|
+
arguments foo: :bar
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ComplexConsumer
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'module inclusion' do
|
32
|
+
it 'registers the class as a consumer' do
|
33
|
+
expect(Hutch).to receive(:register_consumer) do |klass|
|
34
|
+
expect(klass).to eq(simple_consumer)
|
35
|
+
end
|
36
|
+
|
37
|
+
simple_consumer
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe '.consume' do
|
43
|
+
it 'saves the routing key to the consumer' do
|
44
|
+
expect(simple_consumer.routing_keys).to include 'hutch.test1'
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with multiple routing keys' do
|
48
|
+
it 'registers the class once for each routing key' do
|
49
|
+
expect(complex_consumer.routing_keys).to include 'hutch.test1'
|
50
|
+
expect(complex_consumer.routing_keys).to include 'hutch.test2'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when given the same routing key multiple times' do
|
55
|
+
subject { simple_consumer.routing_keys }
|
56
|
+
before { simple_consumer.consume 'hutch.test1' }
|
57
|
+
|
58
|
+
describe '#length' do
|
59
|
+
subject { super().length }
|
60
|
+
it { is_expected.to eq(1)}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '.queue_name' do
|
66
|
+
let(:queue_name) { 'foo' }
|
67
|
+
|
68
|
+
it 'overrides the queue name' do
|
69
|
+
simple_consumer.queue_name(queue_name)
|
70
|
+
expect(simple_consumer.get_queue_name).to eq(queue_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.arguments' do
|
75
|
+
let(:args) { { foo: :bar} }
|
76
|
+
|
77
|
+
it 'overrides the arguments' do
|
78
|
+
simple_consumer.arguments(args)
|
79
|
+
expect(simple_consumer.get_arguments).to eq(args)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.get_arguments' do
|
85
|
+
|
86
|
+
context 'when defined' do
|
87
|
+
it { expect(complex_consumer.get_arguments).to eq(foo: :bar) }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when not defined' do
|
91
|
+
it { expect(simple_consumer.get_arguments).to eq({}) }
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.get_queue_name' do
|
97
|
+
|
98
|
+
context 'when queue name has been set explicitly' do
|
99
|
+
it 'returns the give queue name' do
|
100
|
+
class Foo
|
101
|
+
include Hutch::Consumer
|
102
|
+
queue_name "bar"
|
103
|
+
end
|
104
|
+
|
105
|
+
expect(Foo.get_queue_name).to eq("bar")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when no queue name has been set' do
|
110
|
+
it 'replaces module separators with colons' do
|
111
|
+
module Foo
|
112
|
+
class Bar
|
113
|
+
include Hutch::Consumer
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
expect(Foo::Bar.get_queue_name).to eq('foo:bar')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'converts camelcase class names to snake case' do
|
121
|
+
class FooBarBAZ
|
122
|
+
include Hutch::Consumer
|
123
|
+
end
|
124
|
+
|
125
|
+
expect(FooBarBAZ.get_queue_name).to eq('foo_bar_baz')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hutch::ErrorHandlers::Airbrake do
|
4
|
+
let(:error_handler) { Hutch::ErrorHandlers::Airbrake.new }
|
5
|
+
|
6
|
+
describe '#handle' do
|
7
|
+
let(:error) do
|
8
|
+
begin
|
9
|
+
raise "Stuff went wrong"
|
10
|
+
rescue RuntimeError => err
|
11
|
+
err
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "logs the error to Airbrake" do
|
16
|
+
message_id = "1"
|
17
|
+
payload = "{}"
|
18
|
+
consumer = double
|
19
|
+
ex = error
|
20
|
+
message = {
|
21
|
+
:error_class => ex.class.name,
|
22
|
+
:error_message => "#{ ex.class.name }: #{ ex.message }",
|
23
|
+
:backtrace => ex.backtrace,
|
24
|
+
:parameters => {
|
25
|
+
:payload => payload,
|
26
|
+
:consumer => consumer,
|
27
|
+
},
|
28
|
+
:cgi_data => ENV.to_hash,
|
29
|
+
}
|
30
|
+
expect(::Airbrake).to receive(:notify_or_ignore).with(ex, message)
|
31
|
+
error_handler.handle(message_id, payload, consumer, ex)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hutch::ErrorHandlers::Honeybadger do
|
4
|
+
let(:error_handler) { Hutch::ErrorHandlers::Honeybadger.new }
|
5
|
+
|
6
|
+
describe '#handle' do
|
7
|
+
let(:error) do
|
8
|
+
begin
|
9
|
+
raise "Stuff went wrong"
|
10
|
+
rescue RuntimeError => err
|
11
|
+
err
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "logs the error to Honeybadger" do
|
16
|
+
message_id = "1"
|
17
|
+
payload = "{}"
|
18
|
+
consumer = double
|
19
|
+
ex = error
|
20
|
+
message = {
|
21
|
+
:error_class => ex.class.name,
|
22
|
+
:error_message => "#{ ex.class.name }: #{ ex.message }",
|
23
|
+
:backtrace => ex.backtrace,
|
24
|
+
:context => {
|
25
|
+
:message_id => message_id,
|
26
|
+
:consumer => consumer
|
27
|
+
},
|
28
|
+
:parameters => {
|
29
|
+
:payload => payload
|
30
|
+
}
|
31
|
+
}
|
32
|
+
expect(::Honeybadger).to receive(:notify_or_ignore).with(message)
|
33
|
+
error_handler.handle(message_id, payload, consumer, ex)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hutch::ErrorHandlers::Logger do
|
4
|
+
let(:error_handler) { Hutch::ErrorHandlers::Logger.new }
|
5
|
+
|
6
|
+
describe '#handle' do
|
7
|
+
let(:error) { double(message: "Stuff went wrong", class: "RuntimeError",
|
8
|
+
backtrace: ["line 1", "line 2"]) }
|
9
|
+
|
10
|
+
it "logs three separate lines" do
|
11
|
+
expect(Hutch::Logging.logger).to receive(:error).exactly(3).times
|
12
|
+
error_handler.handle("1", "{}", double, error)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hutch::ErrorHandlers::Sentry do
|
4
|
+
let(:error_handler) { Hutch::ErrorHandlers::Sentry.new }
|
5
|
+
|
6
|
+
describe '#handle' do
|
7
|
+
let(:error) do
|
8
|
+
begin
|
9
|
+
raise "Stuff went wrong"
|
10
|
+
rescue RuntimeError => err
|
11
|
+
err
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "logs the error to Sentry" do
|
16
|
+
expect(Raven).to receive(:capture_exception).with(error)
|
17
|
+
error_handler.handle("1", "{}", double, error)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|