hutch 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ad7051316dda86d731e7422ebd9c126618210e3
4
- data.tar.gz: e5a4e0fa91debe583046b45076b3373768c77d2e
3
+ metadata.gz: 2432ba126d871fbe57468e88a39c6d589a01aa2f
4
+ data.tar.gz: 865526ad5d702eaaea80ee000b6e8ef879b5ac18
5
5
  SHA512:
6
- metadata.gz: 5b7444175bfd7abbbe2219214506a1a884da83f4ba9e0182547acc65ce2e5285691cb786d04985e16be7cabee26acbf94d4e51052a30192f8fbf4cdafb9951f4
7
- data.tar.gz: d0b377c4b5d9f695d0e247c456352209f5e8798d52a3e8c025c88dbdd1598280f6a2766fb7a49d4f5810f64ca0965a141a2a0bb069812d9ecf62d464a26039c4
6
+ metadata.gz: 87d496756e3df55679391977995de5eb836a949cd80ca0ecafb7d99966512c736be01307e72976cad95fb122fcd048c51154a7456c9cfec7b1e4d2080ef9c20f
7
+ data.tar.gz: 5b8f3e3fd23af0451da805d64f90ff8afc5b8ee0be93c04a4fec7be45598c32fc2ad96d59442d660974f8b3d18e47f2e507aa6988b369207721173e5e1812877
@@ -1,5 +1,6 @@
1
+ language: ruby
1
2
  rvm:
2
- - 2.0.0
3
- - 1.9.3
3
+ - "2.0.0"
4
+ - "1.9.3"
4
5
  services:
5
6
  - rabbitmq
@@ -1,3 +1,7 @@
1
+ ## 0.7.0 — unreleased
2
+
3
+ - [Global properties can now be specified](https://github.com/gocardless/hutch/pull/62) for publishing
4
+
1
5
  ## 0.6.0 - November 4, 2013
2
6
 
3
7
  - Metadata can now be passed in to `#publish`
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 GoCardless
1
+ Copyright (c) 2013-2014 GoCardless
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -61,6 +61,21 @@ class FailedPaymentConsumer
61
61
  end
62
62
  ```
63
63
 
64
+ By default, the queue name will be named using the consumer class. You can set
65
+ the queue name explicitly by using the `queue_name` method:
66
+
67
+ ```ruby
68
+ class FailedPaymentConsumer
69
+ include Hutch::Consumer
70
+ consume 'gc.ps.payment.failed'
71
+ queue_name 'failed_payments'
72
+
73
+ def process(message)
74
+ mark_payment_as_failed(message[:id])
75
+ end
76
+ end
77
+ ```
78
+
64
79
  If you are using Hutch with Rails and want to make Hutch log to the Rails
65
80
  logger rather than `stdout`, add this to `config/initializers/hutch.rb`
66
81
 
@@ -68,7 +83,8 @@ logger rather than `stdout`, add this to `config/initializers/hutch.rb`
68
83
  Hutch::Logging.logger = Rails.logger
69
84
  ```
70
85
 
71
- [topic-docs]: http://www.rabbitmq.com/tutorials/tutorial-five-python.html
86
+ See this [RabbitMQ tutorial on topic exchanges](http://www.rabbitmq.com/tutorials/tutorial-five-ruby.html)
87
+ to learn more.
72
88
 
73
89
 
74
90
  ## Running Hutch
@@ -81,7 +97,7 @@ $ hutch -h
81
97
  usage: hutch [options]
82
98
  --mq-host HOST Set the RabbitMQ host
83
99
  --mq-port PORT Set the RabbitMQ port
84
- --mq-tls Use TLS for the AMQP connection
100
+ -t, --[no-]mq-tls Use TLS for the AMQP connection
85
101
  --mq-tls-cert FILE Certificate for TLS client verification
86
102
  --mq-tls-key FILE Private key for TLS client verification
87
103
  --mq-exchange EXCHANGE Set the RabbitMQ exchange
@@ -90,7 +106,7 @@ usage: hutch [options]
90
106
  --mq-password PASSWORD Set the RabbitMQ password
91
107
  --mq-api-host HOST Set the RabbitMQ API host
92
108
  --mq-api-port PORT Set the RabbitMQ API port
93
- --mq-api-ssl Use SSL for the RabbitMQ API
109
+ -s, --[no-]mq-api-ssl Use SSL for the RabbitMQ API
94
110
  --config FILE Load Hutch configuration from a file
95
111
  --require PATH Require a Rails app or path
96
112
  --[no-]autoload-rails Require the current rails app directory
@@ -1,7 +1,7 @@
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', '~> 1.0.0'
4
+ gem.add_runtime_dependency 'bunny', '~> 1.1.0'
5
5
  gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
6
6
  gem.add_runtime_dependency 'multi_json', '~> 1.5'
7
7
  gem.add_development_dependency 'rspec', '~> 2.12.0'
@@ -21,10 +21,18 @@ module Hutch
21
21
  Hutch::Logging.logger
22
22
  end
23
23
 
24
- def self.connect(config = Hutch::Config)
24
+ def self.global_properties=(properties)
25
+ @global_properties = properties
26
+ end
27
+
28
+ def self.global_properties
29
+ @global_properties ||= {}
30
+ end
31
+
32
+ def self.connect(options = {}, config = Hutch::Config)
25
33
  unless connected?
26
34
  @broker = Hutch::Broker.new(config)
27
- @broker.connect
35
+ @broker.connect(options)
28
36
  @connected = true
29
37
  end
30
38
  end
@@ -37,8 +45,8 @@ module Hutch
37
45
  @connected
38
46
  end
39
47
 
40
- def self.publish(routing_key, message)
41
- @broker.publish(routing_key, message)
48
+ def self.publish(*args)
49
+ broker.publish(*args)
42
50
  end
43
51
  end
44
52
 
@@ -14,9 +14,9 @@ module Hutch
14
14
  @config = config || Hutch::Config
15
15
  end
16
16
 
17
- def connect
17
+ def connect(options = {})
18
18
  set_up_amqp_connection
19
- set_up_api_connection
19
+ set_up_api_connection if options.fetch(:enable_http_api_use, true)
20
20
 
21
21
  if block_given?
22
22
  yield
@@ -34,69 +34,72 @@ module Hutch
34
34
  # channel we use for talking to RabbitMQ. It also ensures the existance of
35
35
  # the exchange we'll be using.
36
36
  def set_up_amqp_connection
37
- host, port, vhost = @config[:mq_host], @config[:mq_port]
38
- username, password = @config[:mq_username], @config[:mq_password]
39
- vhost, tls = @config[:mq_vhost], @config[:mq_tls]
40
- tls_key, tls_cert = @config[:mq_tls_key], @config[:mq_tls_cert]
37
+ open_connection!
38
+ open_channel!
39
+
40
+ exchange_name = @config[:mq_exchange]
41
+ logger.info "using topic exchange '#{exchange_name}'"
42
+
43
+ with_bunny_precondition_handler('exchange') do
44
+ @exchange = @channel.topic(exchange_name, durable: true)
45
+ end
46
+ end
47
+
48
+ def open_connection!
49
+ host = @config[:mq_host]
50
+ port = @config[:mq_port]
51
+ vhost = @config[:mq_vhost]
52
+ username = @config[:mq_username]
53
+ password = @config[:mq_password]
54
+ tls = @config[:mq_tls]
55
+ tls_key = @config[:mq_tls_cert]
56
+ tls_cert = @config[:mq_tls_key]
41
57
  protocol = tls ? "amqps://" : "amqp://"
42
- uri = "#{username}:#{password}@#{host}:#{port}/#{vhost.sub(/^\//, '')}"
58
+ uri = "#{username}:#{password}@#{host}:#{port}/#{vhost.sub(/^\//, '')}"
59
+ sanitized_uri = "#{protocol}#{host}:#{port}"
43
60
  logger.info "connecting to rabbitmq (#{protocol}#{uri})"
44
61
 
45
62
  @connection = Bunny.new(host: host, port: port, vhost: vhost,
46
63
  tls: tls, tls_key: tls_key, tls_cert: tls_cert,
47
64
  username: username, password: password,
48
- heartbeat: 1, automatically_recover: true,
65
+ heartbeat: 30, automatically_recover: true,
49
66
  network_recovery_interval: 1)
50
- @connection.start
51
67
 
52
- logger.info 'opening rabbitmq channel'
53
- @channel = @connection.create_channel
68
+ with_bunny_connection_handler(sanitized_uri) do
69
+ @connection.start
70
+ end
54
71
 
55
- exchange_name = @config[:mq_exchange]
56
- logger.info "using topic exchange '#{exchange_name}'"
57
- @exchange = @channel.topic(exchange_name, durable: true)
58
- rescue Bunny::TCPConnectionFailed => ex
59
- logger.error "amqp connection error: #{ex.message.downcase}"
60
- uri = "#{protocol}#{host}:#{port}"
61
- raise ConnectionError.new("couldn't connect to rabbitmq at #{uri}")
62
- rescue Bunny::PreconditionFailed => ex
63
- logger.error ex.message
64
- raise WorkerSetupError.new('could not create exchange due to a type ' +
65
- 'conflict with an existing exchange, ' +
66
- 'remove the existing exchange and try again')
72
+ @connection
73
+ end
74
+
75
+ def open_channel!
76
+ logger.info 'opening rabbitmq channel'
77
+ @channel = connection.create_channel
67
78
  end
68
79
 
69
80
  # Set up the connection to the RabbitMQ management API. Unfortunately, this
70
81
  # is necessary to do a few things that are impossible over AMQP. E.g.
71
82
  # listing queues and bindings.
72
83
  def set_up_api_connection
73
- host, port = @config[:mq_api_host], @config[:mq_api_port]
74
- username, password = @config[:mq_username], @config[:mq_password]
75
- ssl = @config[:mq_api_ssl]
76
-
77
- protocol = ssl ? "https://" : "http://"
78
- management_uri = "#{protocol}#{username}:#{password}@#{host}:#{port}/"
79
- logger.info "connecting to rabbitmq management api (#{management_uri})"
80
-
81
- @api_client = CarrotTop.new(host: host, port: port,
82
- user: username, password: password,
83
- ssl: ssl)
84
- @api_client.exchanges
85
- rescue Errno::ECONNREFUSED => ex
86
- logger.error "api connection error: #{ex.message.downcase}"
87
- raise ConnectionError.new("couldn't connect to api at #{management_uri}")
88
- rescue Net::HTTPServerException => ex
89
- logger.error "api connection error: #{ex.message.downcase}"
90
- if ex.response.code == '401'
91
- raise AuthenticationError.new('invalid api credentials')
92
- else
93
- raise
84
+ logger.info "connecting to rabbitmq management api (#{api_config.management_uri})"
85
+
86
+ with_authentication_error_handler do
87
+ with_connection_error_handler do
88
+ @api_client = CarrotTop.new(host: api_config.host, port: api_config.port,
89
+ user: api_config.username, password: api_config.password,
90
+ ssl: api_config.ssl)
91
+ @api_client.exchanges
92
+ end
94
93
  end
95
94
  end
96
95
 
97
- # Create / get a durable queue.
96
+ # Create / get a durable queue and apply namespace if it exists.
98
97
  def queue(name)
99
- @channel.queue(name, durable: true)
98
+ with_bunny_precondition_handler('queue') do
99
+ namespace = @config[:namespace].to_s.downcase.gsub(/\W|:/, "")
100
+ name = name.prepend(namespace + ":") unless namespace.empty?
101
+ channel.queue(name, durable: true)
102
+ end
100
103
  end
101
104
 
102
105
  # Return a mapping of queue names to the routing keys they're bound to.
@@ -149,35 +152,81 @@ module Hutch
149
152
  end
150
153
 
151
154
  def publish(routing_key, message, properties = {})
152
- payload = JSON.dump(message)
153
-
154
- unless @connection
155
- msg = "Unable to publish - no connection to broker. " +
156
- "Message: #{message.inspect}, Routing key: #{routing_key}."
157
- logger.error(msg)
158
- raise PublishError, msg
159
- end
160
-
161
- unless @connection.open?
162
- msg = "Unable to publish - connection is closed. " +
163
- "Message: #{message.inspect}, Routing key: #{routing_key}."
164
- logger.error(msg)
165
- raise PublishError, msg
166
- end
155
+ ensure_connection!(routing_key, message)
167
156
 
168
157
  non_overridable_properties = {
169
- routing_key: routing_key, timestamp: Time.now.to_i
158
+ routing_key: routing_key,
159
+ timestamp: Time.now.to_i,
160
+ content_type: 'application/json'
170
161
  }
171
162
  properties[:message_id] ||= generate_id
172
163
 
173
164
  logger.info("publishing message '#{message.inspect}' to #{routing_key}")
174
- @exchange.publish(payload, {persistent: true}.
165
+ @exchange.publish(JSON.dump(message), {persistent: true}.
175
166
  merge(properties).
167
+ merge(global_properties).
176
168
  merge(non_overridable_properties))
177
169
  end
178
170
 
179
171
  private
180
172
 
173
+ def raise_publish_error(reason, routing_key, message)
174
+ msg = "Unable to publish - #{reason}. Message: #{message.inspect}, Routing key: #{routing_key}."
175
+ logger.error(msg)
176
+ raise PublishError, msg
177
+ end
178
+
179
+ def ensure_connection!(routing_key, message)
180
+ raise_publish_error('no connection to broker', routing_key, message) unless @connection
181
+ raise_publish_error('connection is closed', routing_key, message) unless @connection.open?
182
+ end
183
+
184
+ def api_config
185
+ @api_config ||= OpenStruct.new.tap do |config|
186
+ config.host = @config[:mq_api_host]
187
+ config.port = @config[:mq_api_port]
188
+ config.username = @config[:mq_username]
189
+ config.password = @config[:mq_password]
190
+ config.ssl = @config[:mq_api_ssl]
191
+ config.protocol = config.ssl ? "https://" : "http://"
192
+ config.management_uri = "#{config.protocol}#{config.username}:#{config.password}@#{config.host}:#{config.port}/"
193
+ end
194
+ end
195
+
196
+ def with_authentication_error_handler
197
+ yield
198
+ rescue Net::HTTPServerException => ex
199
+ logger.error "api connection error: #{ex.message.downcase}"
200
+ if ex.response.code == '401'
201
+ raise AuthenticationError.new('invalid api credentials')
202
+ else
203
+ raise
204
+ end
205
+ end
206
+
207
+ def with_connection_error_handler
208
+ yield
209
+ rescue Errno::ECONNREFUSED => ex
210
+ logger.error "api connection error: #{ex.message.downcase}"
211
+ raise ConnectionError.new("couldn't connect to api at #{api_config.management_uri}")
212
+ end
213
+
214
+ def with_bunny_precondition_handler(item)
215
+ yield
216
+ rescue Bunny::PreconditionFailed => ex
217
+ logger.error ex.message
218
+ s = "RabbitMQ responded with Precondition Failed when creating this #{item}. " +
219
+ "Perhaps it is being redeclared with non-matching attributes"
220
+ raise WorkerSetupError.new(s)
221
+ end
222
+
223
+ def with_bunny_connection_handler(uri)
224
+ yield
225
+ rescue Bunny::TCPConnectionFailed => ex
226
+ logger.error "amqp connection error: #{ex.message.downcase}"
227
+ raise ConnectionError.new("couldn't connect to rabbitmq at #{uri}")
228
+ end
229
+
181
230
  def work_pool_threads
182
231
  @channel.work_pool.threads || []
183
232
  end
@@ -185,6 +234,10 @@ module Hutch
185
234
  def generate_id
186
235
  SecureRandom.uuid
187
236
  end
237
+
238
+ def global_properties
239
+ Hutch.global_properties.respond_to?(:call) ? Hutch.global_properties.call : Hutch.global_properties
240
+ end
188
241
  end
189
242
  end
190
243
 
@@ -97,7 +97,7 @@ module Hutch
97
97
  Hutch::Config.mq_port = port
98
98
  end
99
99
 
100
- opts.on('--mq-tls', 'Use TLS for the AMQP connection') do |tls|
100
+ opts.on("-t", "--[no-]mq-tls", 'Use TLS for the AMQP connection') do |tls|
101
101
  Hutch::Config.mq_tls = tls
102
102
  end
103
103
 
@@ -138,7 +138,7 @@ module Hutch
138
138
  Hutch::Config.mq_api_port = port
139
139
  end
140
140
 
141
- opts.on('--mq-api-ssl', 'Use SSL for the RabbitMQ API') do |api_ssl|
141
+ opts.on("-s", "--[no-]mq-api-ssl", 'Use SSL for the RabbitMQ API') do |api_ssl|
142
142
  Hutch::Config.mq_api_ssl = api_ssl
143
143
  end
144
144
 
@@ -166,6 +166,10 @@ module Hutch
166
166
  Hutch::Config.log_level = Logger::DEBUG
167
167
  end
168
168
 
169
+ opts.on('--namespace NAMESPACE', 'Queue namespace') do |namespace|
170
+ Hutch::Config.namespace = namespace
171
+ end
172
+
169
173
  opts.on('--version', 'Print the version and exit') do
170
174
  puts "hutch v#{VERSION}"
171
175
  exit 0
@@ -24,7 +24,8 @@ module Hutch
24
24
  log_level: Logger::INFO,
25
25
  require_paths: [],
26
26
  autoload_rails: true,
27
- error_handlers: [Hutch::ErrorHandlers::Logger.new]
27
+ error_handlers: [Hutch::ErrorHandlers::Logger.new],
28
+ namespace: nil
28
29
  }
29
30
  end
30
31
 
@@ -15,12 +15,19 @@ module Hutch
15
15
  @routing_keys = self.routing_keys.union(routing_keys)
16
16
  end
17
17
 
18
+ # Explicitly set the queue name
19
+ def queue_name(name)
20
+ @queue_name = name
21
+ end
22
+
18
23
  # The RabbitMQ queue name for the consumer. This is derived from the
19
24
  # fully-qualified class name. Module separators are replaced with single
20
25
  # colons, camelcased class names are converted to snake case.
21
- def queue_name
26
+ def get_queue_name
27
+ return @queue_name unless @queue_name.nil?
22
28
  queue_name = self.name.gsub(/::/, ':')
23
- queue_name.gsub(/([^A-Z:])([A-Z])/) { "#{$1}_#{$2}" }.downcase
29
+ queue_name.gsub!(/([^A-Z:])([A-Z])/) { "#{$1}_#{$2}" }
30
+ queue_name.downcase
24
31
  end
25
32
 
26
33
  # Accessor for the consumer's routing key.
@@ -1,4 +1,4 @@
1
1
  module Hutch
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
4
4
 
@@ -24,11 +24,6 @@ module Hutch
24
24
  # Take a break from Thread#join every 0.1 seconds to check if we've
25
25
  # been sent any signals
26
26
  handle_signals until @broker.wait_on_threads(0.1)
27
- rescue Bunny::PreconditionFailed => ex
28
- logger.error ex.message
29
- raise WorkerSetupError.new('could not create queue due to a type ' +
30
- 'conflict with an existing queue, remove ' +
31
- 'the existing queue and try again')
32
27
  end
33
28
 
34
29
  # Register handlers for SIG{QUIT,TERM,INT} to shut down the worker
@@ -67,7 +62,7 @@ module Hutch
67
62
  # Bind a consumer's routing keys to its queue, and set up a subscription to
68
63
  # receive messages sent to the queue.
69
64
  def setup_queue(consumer)
70
- queue = @broker.queue(consumer.queue_name)
65
+ queue = @broker.queue(consumer.get_queue_name)
71
66
  @broker.bind_queue(queue, consumer.routing_keys)
72
67
 
73
68
  queue.subscribe(ack: true) do |delivery_info, properties, payload|
@@ -31,6 +31,15 @@ describe Hutch::Broker do
31
31
  broker.connect { }
32
32
  end
33
33
  end
34
+
35
+ context "with options" do
36
+ let(:options) { { enable_http_api_use: false } }
37
+
38
+ it "doesnt set up api" do
39
+ broker.should_not_receive(:set_up_api_connection)
40
+ broker.connect options
41
+ end
42
+ end
34
43
  end
35
44
 
36
45
  describe '#set_up_amqp_connection', rabbitmq: true do
@@ -68,6 +77,17 @@ describe Hutch::Broker do
68
77
  end
69
78
  end
70
79
 
80
+ describe '#queue' do
81
+ let(:channel) { double('Channel') }
82
+ before { broker.stub(:channel) { channel } }
83
+
84
+ it 'applies a global namespace' do
85
+ config[:namespace] = 'service'
86
+ broker.channel.should_receive(:queue).with { |*args| args.first == 'service:test' }
87
+ broker.queue('test')
88
+ end
89
+ end
90
+
71
91
  describe '#bindings', rabbitmq: true do
72
92
  around { |example| broker.connect { example.run } }
73
93
  subject { broker.bindings }
@@ -88,7 +108,9 @@ describe Hutch::Broker do
88
108
  end
89
109
 
90
110
  describe '#bind_queue' do
111
+
91
112
  around { |example| broker.connect { example.run } }
113
+
92
114
  let(:routing_keys) { %w( a b c ) }
93
115
  let(:queue) { double('Queue', bind: nil, unbind: nil, name: 'consumer') }
94
116
  before { broker.stub(bindings: { 'consumer' => ['d'] }) }
@@ -145,10 +167,47 @@ describe Hutch::Broker do
145
167
  broker.publish('test.key', 'message')
146
168
  end
147
169
 
148
- it "allows passing message properties" do
170
+ it 'sets default properties' do
171
+ broker.exchange.should_receive(:publish).with(
172
+ JSON.dump("message"),
173
+ hash_including(
174
+ persistent: true,
175
+ routing_key: 'test.key',
176
+ content_type: 'application/json'
177
+ )
178
+ )
179
+
180
+ broker.publish('test.key', 'message')
181
+ end
182
+
183
+ it 'allows passing message properties' do
149
184
  broker.exchange.should_receive(:publish).once
150
185
  broker.publish('test.key', 'message', {expiration: "2000", persistent: false})
151
186
  end
187
+
188
+ context 'when there are global properties' do
189
+ context 'as a hash' do
190
+ before do
191
+ Hutch.stub global_properties: { app_id: 'app' }
192
+ end
193
+
194
+ it 'merges the properties' do
195
+ broker.exchange.should_receive(:publish).with('"message"', hash_including(app_id: 'app'))
196
+ broker.publish('test.key', 'message')
197
+ end
198
+ end
199
+
200
+ context 'as a callable object' do
201
+ before do
202
+ Hutch.stub global_properties: proc { { app_id: 'app' } }
203
+ end
204
+
205
+ it 'calls the proc and merges the properties' do
206
+ broker.exchange.should_receive(:publish).with('"message"', hash_including(app_id: 'app'))
207
+ broker.publish('test.key', 'message')
208
+ end
209
+ end
210
+ end
152
211
  end
153
212
 
154
213
  context 'without a valid connection' do
@@ -58,22 +58,42 @@ describe Hutch::Consumer do
58
58
  end
59
59
 
60
60
  describe '.queue_name' do
61
- it 'replaces module separators with colons' do
62
- module Foo
63
- class Bar
61
+ it 'overrides the queue name' do
62
+
63
+ end
64
+ end
65
+
66
+ describe '.get_queue_name' do
67
+
68
+ context 'when queue name has been set explicitly' do
69
+ it 'returns the give queue name' do
70
+ class Foo
64
71
  include Hutch::Consumer
72
+ queue_name "bar"
65
73
  end
66
- end
67
74
 
68
- Foo::Bar.queue_name.should == 'foo:bar'
75
+ Foo.get_queue_name.should == "bar"
76
+ end
69
77
  end
70
78
 
71
- it 'converts camelcase class names to snake case' do
72
- class FooBarBAZ
73
- include Hutch::Consumer
79
+ context 'when no queue name has been set' do
80
+ it 'replaces module separators with colons' do
81
+ module Foo
82
+ class Bar
83
+ include Hutch::Consumer
84
+ end
85
+ end
86
+
87
+ Foo::Bar.get_queue_name.should == 'foo:bar'
74
88
  end
75
89
 
76
- FooBarBAZ.queue_name.should == 'foo_bar_baz'
90
+ it 'converts camelcase class names to snake case' do
91
+ class FooBarBAZ
92
+ include Hutch::Consumer
93
+ end
94
+
95
+ FooBarBAZ.get_queue_name.should == 'foo_bar_baz'
96
+ end
77
97
  end
78
98
  end
79
99
  end
@@ -4,7 +4,7 @@ require 'hutch/worker'
4
4
  describe Hutch::Worker do
5
5
  before { Raven.as_null_object }
6
6
  let(:consumer) { double('Consumer', routing_keys: %w( a b c ),
7
- queue_name: 'consumer') }
7
+ get_queue_name: 'consumer') }
8
8
  let(:consumers) { [consumer, double('Consumer')] }
9
9
  let(:broker) { Hutch::Broker.new }
10
10
  subject(:worker) { Hutch::Worker.new(broker, consumers) }
@@ -24,7 +24,7 @@ describe Hutch::Worker do
24
24
  before { broker.stub(queue: queue, bind_queue: nil) }
25
25
 
26
26
  it 'creates a queue' do
27
- broker.should_receive(:queue).with(consumer.queue_name).and_return(queue)
27
+ broker.should_receive(:queue).with(consumer.get_queue_name).and_return(queue)
28
28
  worker.setup_queue(consumer)
29
29
  end
30
30
 
@@ -12,5 +12,50 @@ describe Hutch do
12
12
  Hutch.consumers.should include consumer_b
13
13
  end
14
14
  end
15
+
16
+ describe '.connect' do
17
+ context 'not connected' do
18
+ let(:options) { double 'options' }
19
+ let(:config) { double 'config' }
20
+ let(:broker) { double 'broker' }
21
+ let(:action) { Hutch.connect(options, config) }
22
+
23
+ it 'passes options and config' do
24
+ Hutch::Broker.should_receive(:new).with(config).and_return broker
25
+ broker.should_receive(:connect).with options
26
+
27
+ action
28
+ end
29
+
30
+ it 'sets @connect' do
31
+ action
32
+
33
+ expect(Hutch.connected?).to be_true
34
+ end
35
+ end
36
+
37
+ context 'connected' do
38
+ before { Hutch.stub(:connected?).and_return true }
39
+
40
+ it 'does not reconnect' do
41
+ Hutch::Broker.should_not_receive :new
42
+ Hutch.connect
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#publish' do
48
+ let(:broker) { double(Hutch::Broker) }
49
+ let(:args) { ['test.key', 'message', { headers: { foo: 'bar' } }] }
50
+
51
+ before do
52
+ Hutch.stub broker: broker
53
+ end
54
+
55
+ it 'delegates to Hutch::Broker#publish' do
56
+ broker.should_receive(:publish).with(*args)
57
+ Hutch.publish(*args)
58
+ end
59
+ end
15
60
  end
16
61
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.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: 2013-11-04 00:00:00.000000000 Z
11
+ date: 2014-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.0
19
+ version: 1.1.0
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.0.0
26
+ version: 1.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: carrot-top
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  version: '0'
147
147
  requirements: []
148
148
  rubyforge_project:
149
- rubygems_version: 2.0.3
149
+ rubygems_version: 2.1.11
150
150
  signing_key:
151
151
  specification_version: 4
152
152
  summary: Easy inter-service communication using RabbitMQ.
@@ -162,3 +162,4 @@ test_files:
162
162
  - spec/hutch/worker_spec.rb
163
163
  - spec/hutch_spec.rb
164
164
  - spec/spec_helper.rb
165
+ has_rdoc: