hutch 0.19.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +11 -0
  4. data/CHANGELOG.md +438 -0
  5. data/Gemfile +22 -0
  6. data/Guardfile +5 -0
  7. data/LICENSE +22 -0
  8. data/README.md +317 -0
  9. data/Rakefile +14 -0
  10. data/bin/hutch +8 -0
  11. data/circle.yml +3 -0
  12. data/examples/consumer.rb +13 -0
  13. data/examples/producer.rb +10 -0
  14. data/hutch.gemspec +30 -0
  15. data/lib/hutch.rb +62 -0
  16. data/lib/hutch/adapter.rb +11 -0
  17. data/lib/hutch/adapters/bunny.rb +33 -0
  18. data/lib/hutch/adapters/march_hare.rb +37 -0
  19. data/lib/hutch/broker.rb +374 -0
  20. data/lib/hutch/cli.rb +205 -0
  21. data/lib/hutch/config.rb +125 -0
  22. data/lib/hutch/consumer.rb +75 -0
  23. data/lib/hutch/error_handlers.rb +8 -0
  24. data/lib/hutch/error_handlers/airbrake.rb +26 -0
  25. data/lib/hutch/error_handlers/honeybadger.rb +28 -0
  26. data/lib/hutch/error_handlers/logger.rb +16 -0
  27. data/lib/hutch/error_handlers/sentry.rb +23 -0
  28. data/lib/hutch/exceptions.rb +7 -0
  29. data/lib/hutch/logging.rb +32 -0
  30. data/lib/hutch/message.rb +31 -0
  31. data/lib/hutch/serializers/identity.rb +19 -0
  32. data/lib/hutch/serializers/json.rb +22 -0
  33. data/lib/hutch/tracers.rb +6 -0
  34. data/lib/hutch/tracers/newrelic.rb +19 -0
  35. data/lib/hutch/tracers/null_tracer.rb +15 -0
  36. data/lib/hutch/version.rb +4 -0
  37. data/lib/hutch/worker.rb +143 -0
  38. data/spec/hutch/broker_spec.rb +377 -0
  39. data/spec/hutch/cli_spec.rb +80 -0
  40. data/spec/hutch/config_spec.rb +126 -0
  41. data/spec/hutch/consumer_spec.rb +130 -0
  42. data/spec/hutch/error_handlers/airbrake_spec.rb +34 -0
  43. data/spec/hutch/error_handlers/honeybadger_spec.rb +36 -0
  44. data/spec/hutch/error_handlers/logger_spec.rb +15 -0
  45. data/spec/hutch/error_handlers/sentry_spec.rb +20 -0
  46. data/spec/hutch/logger_spec.rb +28 -0
  47. data/spec/hutch/message_spec.rb +38 -0
  48. data/spec/hutch/serializers/json_spec.rb +17 -0
  49. data/spec/hutch/worker_spec.rb +99 -0
  50. data/spec/hutch_spec.rb +87 -0
  51. data/spec/spec_helper.rb +40 -0
  52. 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