cottontail 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -27,10 +27,10 @@ require 'bunny'
27
27
  class Worker < Cottontail::Base
28
28
  # configure the Bunny client with the default connection (host: "localhost", port: 5672)
29
29
  # and with logging.
30
- set :client, :logging => true
30
+ set :client, { :logging => true }
31
31
 
32
32
  # Declare default direct exchange which is bound to all queues of the type `topic`
33
- set :exchange, "", :type => :topic
33
+ set :exchange, [ "", { :type => :topic } ]
34
34
 
35
35
  # Declare the `test` queue
36
36
  set :queue, "test"
@@ -37,39 +37,13 @@ module Cottontail
37
37
  class << self
38
38
  attr_reader :routes
39
39
 
40
- # convenience method to start the instance
41
- def run; new.run; end
42
-
43
- # Reset the class
44
- def reset!
45
- @settings = {}
46
-
47
- @errors = {}
48
- @routes = {}
49
-
50
- # default logger
51
- set(:logger) { Logger.new(STDOUT) }
52
-
53
- # retry settings
54
- set(:retries) { true }
55
- set(:delay_on_retry) { 2 }
56
-
57
- # default subscribe loop
58
- set :subscribe, {}, proc { |m| route! m }
59
-
60
- # default bunny options
61
- set :client, {}
62
- set :exchange, "default", :type => :topic
63
- set :queue, "default"
64
- end
65
-
66
40
  # Set runtime configuration
67
41
  #
68
42
  # @example
69
- # set :host, "localhost"
70
- # set(:host) { "localhost" } # will be called on first usage
71
- def set( key, *args, &block )
72
- @settings[ key ] = block ? block : args
43
+ # set :logger, Logger.new(STDOUT)
44
+ # set(:logger) { Logger.new(STDOUT) } # will be called on first usage
45
+ def set( key, value = nil, &block )
46
+ @settings[ key ] = block ? block : value
73
47
  end
74
48
 
75
49
  # Override the standard subscribe loop
@@ -80,7 +54,7 @@ module Cottontail
80
54
  # route! message
81
55
  # end
82
56
  def subscribe( options = {}, &block )
83
- set :subscribe, options, compile!("subscribe", &block)
57
+ set :subscribe, [options, compile!("subscribe", &block)]
84
58
  end
85
59
 
86
60
  # Defines routing on class level
@@ -95,14 +69,20 @@ module Cottontail
95
69
 
96
70
  # Define error handlers
97
71
  #
98
- # @example
72
+ # @example Generic route
73
+ # error do
74
+ # puts "an error occured"
75
+ # end
76
+ #
77
+ # @example Error on specific Exception
99
78
  # error RouteNotFound do
100
79
  # puts "Route not found for #{routing_key.inspect}"
101
80
  # end
102
81
  def error( *codes, &block )
103
82
  codes << :default if codes.empty? # the default error handling
104
83
 
105
- codes.each { |c| (@errors[c] ||= []) << compile!("error_#{c}", &block) }
84
+ compiled = compile!("error_#{codes.join("_")}", &block)
85
+ codes.each { |c| @errors[c] = compiled }
106
86
  end
107
87
 
108
88
  # Route on class level (handy for testing)
@@ -129,52 +109,80 @@ module Cottontail
129
109
  # Retrieve the error block for the passed Exception class
130
110
  #
131
111
  # If no class matches, the default will be returned in case it has been set (else nil).
132
- def errors( klass )
112
+ def error_for( klass )
133
113
  @errors[klass] || @errors[:default]
134
114
  end
135
115
 
116
+ # convenience method to start the instance
117
+ def run; new.run; end
136
118
 
137
- private
119
+ # Reset the class
120
+ def reset!
121
+ @settings = {}
138
122
 
139
- def inherited( subclass )
140
- subclass.reset!
141
- super
142
- end
123
+ @errors = {}
124
+ @routes = {}
125
+
126
+ # default logger
127
+ set :logger, Logger.new(STDOUT)
128
+
129
+ # retry settings
130
+ set :retries, true
131
+ set :delay_on_retry, 2
143
132
 
144
- # compiles a given proc to an unbound method to be called later on a different binding
145
- def compile!( name, &block )
146
- define_method name, &block
147
- method = instance_method name
148
- remove_method name
133
+ # default subscribe loop
134
+ set :subscribe, [{}, proc { |m| route! m }]
149
135
 
150
- block.arity == 0 ? proc { |a,p| method.bind(a).call } : proc { |a,*p| method.bind(a).call(*p) }
136
+ # default bunny options
137
+ set :client, {}
138
+ set :exchange, ["default", {:type => :topic}]
139
+ set :queue, "default"
151
140
  end
152
141
 
142
+
143
+ private
144
+
145
+ def inherited( subclass )
146
+ subclass.reset!
147
+ super
148
+ end
149
+
150
+ # compiles a given proc to an unbound method to be called later on a different binding
151
+ def compile!( name, &block )
152
+ define_method name, &block
153
+ method = instance_method name
154
+ remove_method name
155
+
156
+ block.arity == 0 ? proc { |a,p| method.bind(a).call } : proc { |a,*p| method.bind(a).call(*p) }
157
+ end
158
+
153
159
  end
154
160
 
161
+
155
162
  def initialize
156
163
  reset!
157
164
  end
158
165
 
159
166
  # Starts the consumer service and enters the subscribe loop.
160
167
  def run
168
+ # establish connection and bind routing keys
161
169
  logger.debug "[Cottontail] Connecting to client"
162
- @client = Bunny.new( *settings(:client) )
170
+ @client = Bunny.new( settings(:client) )
163
171
  @client.start
164
172
 
165
173
  logger.debug "[Cottontail] Declaring exchange"
166
174
  exchange = @client.exchange( *settings(:exchange) )
167
175
 
168
176
  logger.debug "[Cottontail] Declaring queue"
169
- queue = @client.queue( *settings(:queue) )
177
+ queue = @client.queue( settings(:queue) )
170
178
 
171
179
  routes.keys.each do |key|
172
180
  logger.debug "[Cottontail] Binding #{key.inspect} to exchange"
173
181
  queue.bind( exchange, :key => key )
174
182
  end
175
183
 
176
- logger.debug "[Cottontail] Entering subscribe loop"
177
- subscribe! queue
184
+ # enter the subscribe loop
185
+ subscribe!( queue )
178
186
  rescue => e
179
187
  @client.stop if @client
180
188
  reset!
@@ -189,35 +197,26 @@ module Cottontail
189
197
  retry
190
198
  end
191
199
 
192
- # performs the routing of the given AMQP message.
200
+ # Performs the routing of the given AMQP message
201
+ #
202
+ # The method will raise an error if no route was found or an exception
203
+ # was raised within matched routing block.
193
204
  def route!( message )
194
205
  @message = message
195
- process!
196
- rescue => err
197
- @last_error = err
198
206
 
199
- if errors(err.class).nil?
200
- raise( err, caller )
207
+ if block = routes[routing_key]
208
+ block.call(self)
201
209
  else
202
- errors(err.class).each { |block| block.call(self) }
210
+ raise Cottontail::RouteNotFound.new(routing_key)
203
211
  end
204
212
  end
205
213
 
206
214
 
207
215
  private
208
216
 
209
- def process!
210
- key = @message[:delivery_details][:routing_key]
211
-
212
- raise Cottontail::RouteNotFound.new(key) unless block = routes[key]
213
- block.call(self)
214
- end
215
-
217
+ # Retrieve routes
216
218
  def routes; self.class.routes; end
217
219
 
218
- # Retrieve errors
219
- def errors(code); self.class.errors(code); end
220
-
221
220
  # Retrieve settings
222
221
  def settings(key); self.class.settings(key); end
223
222
 
@@ -231,17 +230,39 @@ module Cottontail
231
230
  prepare_client_settings!
232
231
  end
233
232
 
233
+ # Handles the subscribe loop on the queue.
234
234
  def subscribe!( queue )
235
+ logger.debug "[Cottontail] Entering subscribe loop"
236
+
235
237
  options, block = settings(:subscribe)
238
+ queue.subscribe( options ) do |m|
239
+ with_error_handling!( m, &block )
240
+ end
241
+ end
236
242
 
237
- queue.subscribe( options ) { |m| block.call(self, m) }
243
+ # Gracefully handles the given message.
244
+ #
245
+ # @param [Message] m The RabbitMQ message to be handled
246
+ def with_error_handling!( m, &block )
247
+ block.call(self, m)
248
+ rescue => err
249
+ @last_error = err
250
+
251
+ if block = self.class.error_for(err.class)
252
+ block.call(self)
253
+ else
254
+ # if no defined exception handling block could be found, then re-raise
255
+ raise( err, caller )
256
+ end
257
+ ensure
258
+ @last_error = nil # unset error after handling
238
259
  end
239
260
 
240
261
  # The bunny gem itself is not able to handle multiple hosts - although multiple RabbitMQ instances may run in parralel.
241
262
  #
242
263
  # You may pass :hosts as option when settings the client in order to cycle through them in case a connection was lost.
243
264
  def prepare_client_settings!
244
- return {} unless options = settings(:client) and options = options.first
265
+ return {} unless options = settings(:client)
245
266
 
246
267
  if hosts = options[:hosts]
247
268
  host, port = hosts.shift
@@ -253,7 +274,6 @@ module Cottontail
253
274
  options
254
275
  end
255
276
 
256
-
257
277
  public
258
278
 
259
279
  # === Perform the initial setup
@@ -1,3 +1,3 @@
1
1
  module Cottontail
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: cottontail
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.1
5
+ version: 0.1.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Rudolf Schmidt
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-02-16 00:00:00 Z
13
+ date: 2012-02-29 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bunny