isimud 1.3.9 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardoc/checksums +0 -15
  5. data/.yardoc/object_types +0 -0
  6. data/.yardoc/objects/root.dat +0 -0
  7. data/Gemfile +2 -2
  8. data/Gemfile.lock +17 -13
  9. data/LICENSE.txt +1 -1
  10. data/README.md +98 -62
  11. data/Rakefile +1 -1
  12. data/checksum/isimud-1.4.0.gem.sha512 +1 -0
  13. data/doc/Isimud.html +118 -138
  14. data/doc/Isimud/BunnyClient.html +2 -2
  15. data/doc/Isimud/Client.html +2 -2
  16. data/doc/Isimud/Event.html +2 -2
  17. data/doc/Isimud/EventListener.html +208 -88
  18. data/doc/Isimud/EventObserver.html +96 -31
  19. data/doc/Isimud/EventObserver/ClassMethods.html +14 -14
  20. data/doc/Isimud/Generators.html +2 -2
  21. data/doc/Isimud/Logging.html +2 -2
  22. data/doc/Isimud/ModelWatcher.html +2 -2
  23. data/doc/Isimud/ModelWatcher/ClassMethods.html +2 -2
  24. data/doc/Isimud/Railtie.html +2 -2
  25. data/doc/Isimud/TestClient.html +2 -2
  26. data/doc/Isimud/TestClient/Queue.html +74 -2
  27. data/doc/_index.html +2 -195
  28. data/doc/class_list.html +1 -1
  29. data/doc/file.LICENSE.html +3 -3
  30. data/doc/file.README.html +157 -65
  31. data/doc/index.html +157 -65
  32. data/doc/method_list.html +0 -732
  33. data/doc/top-level-namespace.html +2 -12
  34. data/lib/isimud.rb +22 -17
  35. data/lib/isimud/bunny_client.rb +12 -10
  36. data/lib/isimud/client.rb +10 -1
  37. data/lib/isimud/event_listener.rb +6 -6
  38. data/lib/isimud/test_client.rb +12 -12
  39. data/lib/isimud/version.rb +1 -1
  40. data/release +1 -1
  41. data/spec/isimud/bunny_client_spec.rb +28 -27
  42. data/spec/isimud/client_spec.rb +50 -5
  43. metadata +3 -4
  44. metadata.gz.sig +0 -0
  45. data/doc/Isimud/Generators/ConfigGenerator.html +0 -192
  46. data/doc/Isimud/Generators/InitializerGenerator.html +0 -192
@@ -81,16 +81,6 @@
81
81
  </dl>
82
82
  <div class="clear"></div>
83
83
 
84
- <h2>Defined Under Namespace</h2>
85
- <p class="children">
86
-
87
-
88
- <strong class="modules">Modules:</strong> <span class='object_link'><a href="Isimud.html" title="Isimud (module)">Isimud</a></span>
89
-
90
-
91
-
92
-
93
- </p>
94
84
 
95
85
 
96
86
 
@@ -103,9 +93,9 @@
103
93
  </div>
104
94
 
105
95
  <div id="footer">
106
- Generated on Mon Jan 18 17:07:52 2016 by
96
+ Generated on Wed Aug 24 18:59:45 2016 by
107
97
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
- 0.8.7.6 (ruby-2.2.3).
98
+ 0.8.7.6 (ruby-2.3.0).
109
99
  </div>
110
100
 
111
101
  </body>
@@ -3,36 +3,41 @@ require 'active_support/core_ext/module/attribute_accessors'
3
3
 
4
4
  module Isimud
5
5
  include ::ActiveSupport::Configurable
6
- # @!attribute [r] client_type
6
+ # @!attribute [rw] client_type
7
7
  # @return [Enumerable<'bunny', 'test'>] Type of client to use
8
- # @!attribute [r] client_options
8
+ # @!attribute [rw] client_options
9
9
  # @return [Hash] client specific options
10
- # @!attribute [r] default_client
10
+ # @!attribute [rw] default_client
11
11
  # @return [Isimud::Client] default client
12
- # @!attribute [r] events_exchange
12
+ # @!attribute [rw] events_exchange
13
13
  # @return [String] AMQP exchange used for publishing Event instances
14
- # @!attribute [r] enable_model_watcher
14
+ # @!attribute [rw] enable_model_watcher
15
15
  # @return [Boolean] when set, send Isimud::ModelWatcher messages
16
- # @!attribute [r] listener_error_limit
17
- # @return [Integer] maximum number of exceptions allowed per hour before listener shuts down (100)
18
- # @!attribute [r] logger
16
+ # @!attribute [rw] listener_error_limit
17
+ # @return [Integer] maximum number of uncaught exceptions allowed per hour before listener shuts down (100)
18
+ # @!attribute [rw] logger
19
19
  # @return [Logger] logger for tracing messages (Rails.logger)
20
- # @!attribute [r] log_level
20
+ # @!attribute [rw] log_level
21
21
  # @return [Symbol] log level (:debug)
22
- # @!attribute [r] model_watcher_exchange
22
+ # @!attribute [rw] model_watcher_exchange
23
23
  # @return [String] AMQP exchange used for publishing ModelWatcher messages
24
- # @!attribute [r] model_watcher_schema
24
+ # @!attribute [rw] model_watcher_schema
25
25
  # @return [String] schema name (Rails.configuration.database_configuration[Rails.env]['database'])
26
- # @!attribute [r] prefetch_count
26
+ # @!attribute [rw] prefetch_count
27
27
  # @return [Integer] number of messages to fetch -- only applies to BunnyClient
28
- # @!attribute [r] retry_failures
29
- # @return [Boolean] when set, if an exception occurs during message processing, requeue it
30
- # @!attribute [r] server
31
- # @return [<String, Hash>] server connection attributes
28
+ # @!attribute [rw] retry_failures
29
+ # @return [Boolean|nil] whether to requeue a message if an exception occurs during processing.
30
+ # When set to nil, the return status from exception handlers is used. (false)
31
+ # @see Isimud::Client#on_exception
32
+ # @!attribute [rw] server
33
+ # @return [<String, Hash>] server connection attributes ()
32
34
  config_accessor :client_type do
33
35
  :bunny
34
36
  end
35
- config_accessor :client_options, :default_client, :enable_model_watcher, :model_watcher_schema, :retry_failures
37
+ config_accessor :client_options, :default_client, :enable_model_watcher, :model_watcher_schema
38
+ config_accessor :retry_failures do
39
+ false
40
+ end
36
41
  config_accessor :listener_error_limit do
37
42
  100
38
43
  end
@@ -58,27 +58,28 @@ module Isimud
58
58
 
59
59
  # Subscribe to messages on the Bunny queue. The provided block will be called each time a message is received.
60
60
  # The message will be acknowledged and deleted from the queue unless an exception is raised from the block.
61
- # In the case that an exception is caught, the message is rejected, and any declared exception handlers will
62
- # be called.
61
+ # In the case that an uncaught exception is raised, the message is rejected, and any declared exception handlers
62
+ # will be called.
63
63
  #
64
64
  # @param [Bunny::Queue] queue Bunny queue
65
- # @param [Hash] options {manual_ack: true} subscription options -- @see Bunny::Queue#subscribe
65
+ # @param [Hash] options {} subscription options -- @see Bunny::Queue#subscribe
66
66
  # @yieldparam [String] payload message text
67
- def subscribe(queue, options = {manual_ack: true}, &block)
68
- current_channel = channel
69
- queue.subscribe(options) do |delivery_info, properties, payload|
67
+ def subscribe(queue, options = {}, &block)
68
+ queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
69
+ current_channel = channel
70
70
  begin
71
- log "Isimud: queue #{queue.name} received #{delivery_info.delivery_tag} routing_key: #{delivery_info.routing_key}"
71
+ log "Isimud: queue #{queue.name} received #{delivery_info.delivery_tag} routing_key: #{delivery_info.routing_key}", :debug
72
72
  Thread.current['isimud_queue_name'] = queue.name
73
73
  Thread.current['isimud_delivery_info'] = delivery_info
74
74
  Thread.current['isimud_properties'] = properties
75
75
  block.call(payload)
76
- log "Isimud: queue #{queue.name} finished with #{delivery_info.delivery_tag}, acknowledging"
76
+ log "Isimud: queue #{queue.name} finished with #{delivery_info.delivery_tag}, acknowledging", :debug
77
77
  current_channel.ack(delivery_info.delivery_tag)
78
78
  rescue => e
79
79
  log("Isimud: queue #{queue.name} error processing #{delivery_info.delivery_tag} payload #{payload.inspect}: #{e.class.name} #{e.message}\n #{e.backtrace.join("\n ")}", :warn)
80
- current_channel.reject(delivery_info.delivery_tag, Isimud.retry_failures)
81
- run_exception_handlers(e)
80
+ retry_status = run_exception_handlers(e)
81
+ log "Isimud: rejecting #{delivery_info.delivery_tag} requeue=#{retry_status}", :warn
82
+ current_channel.reject(delivery_info.delivery_tag, retry_status)
82
83
  end
83
84
  end
84
85
  end
@@ -142,6 +143,7 @@ module Isimud
142
143
  # e.g. "user.goal.complete"
143
144
  # @see http://rubybunny.info/articles/exchanges.html
144
145
  def publish(exchange, routing_key, payload)
146
+ log "Isimud::BunnyClient#publish: exchange=#{exchange} routing_key=#{routing_key}", :debug
145
147
  channel.topic(exchange, durable: true).publish(payload, routing_key: routing_key, persistent: true)
146
148
  end
147
149
 
@@ -39,8 +39,17 @@ module Isimud
39
39
 
40
40
  # Call each of the exception handlers declared by #on_exception.
41
41
  # @param [Exception] exception
42
+ # @return [Boolean] true if message should be requeued, false otherwise
42
43
  def run_exception_handlers(exception)
43
- exception_handlers.each{|handler| handler.call(exception)}
44
+ status = true
45
+ exception_handlers.each do |handler|
46
+ status &&= begin
47
+ handler.call(exception)
48
+ rescue
49
+ nil
50
+ end
51
+ end
52
+ Isimud.retry_failures.nil? ? status : Isimud.retry_failures
44
53
  end
45
54
 
46
55
  def publish(exchange, routing_key, payload)
@@ -84,8 +84,8 @@ module Isimud
84
84
  @events_exchange = options[:events_exchange]
85
85
  @models_exchange = options[:models_exchange]
86
86
  @name = options[:name]
87
- @observer_mutex = Thread::Mutex.new
88
- @error_counter_mutex = Thread::Mutex.new
87
+ @observer_mutex = Mutex.new
88
+ @error_counter_mutex = Mutex.new
89
89
  @status = STATUS_INITIALIZE
90
90
  end
91
91
 
@@ -171,16 +171,16 @@ module Isimud
171
171
 
172
172
  def start_event_thread
173
173
  Thread.new do
174
- log 'EventListener: starting event_thread', :info
174
+ log 'EventListener: starting event_thread'
175
175
  until shutdown? do
176
176
  begin
177
177
  bind_queues
178
- log 'EventListener: event_thread bind_queues finished', :info
178
+ log 'EventListener: event_thread finished'
179
179
  @status = STATUS_RUNNING
180
180
  Thread.stop
181
181
  rescue => e
182
- log "EventListener: error in event thread: #{e.message}\n #{e.backtrace.join("\n ")}", :warn
183
182
  count_error(e)
183
+ log 'EventListener: resetting queues', :warn
184
184
  @observer_queue = nil
185
185
  client.reset
186
186
  end
@@ -201,7 +201,7 @@ module Isimud
201
201
  end
202
202
 
203
203
  def start_error_counter_thread
204
- log 'EventListener: starting error counter', :info
204
+ log 'EventListener: starting error counter'
205
205
  @error_count = 0
206
206
  Thread.new do
207
207
  while true
@@ -1,11 +1,11 @@
1
1
  module Isimud
2
2
 
3
- # Interface for a messaging client that is suitable for testing. No network connections are involved.
4
- # Note that all message deliveries are handled in a synchronous manner. When a message is published to the
5
- # client, each declared queue is examined and, if the message's routing key matches any of the patterns bound to the
6
- # queue, the queue's block is called with the message. Any uncaught exceptions raised within a message processing
7
- # block will cause any declared exception handlers to be run. However, the message will not be re-placed onto the
8
- # queue should this occur.
3
+ # Interface for a messaging client that is suitable for testing. No network connections are used.
4
+ # All message deliveries are handled in a synchronous manner. When a message is published to the client, each declared
5
+ # queue is examined and, if the message's routing key matches any of the patterns bound to the queue, the queue's
6
+ # block is called with the message. Any uncaught exceptions raised within a message processing
7
+ # block will cause any declared exception handlers to be run. However, the message will not be re-queued should this
8
+ # occur.
9
9
  class TestClient < Isimud::Client
10
10
  attr_accessor :queues
11
11
 
@@ -14,11 +14,11 @@ module Isimud
14
14
  attr_reader :name, :bindings, :client
15
15
  attr_accessor :proc
16
16
 
17
- def initialize(client, name, proc = Proc.new{ |_| } )
18
- @client = client
19
- @name = name
20
- @bindings = Hash.new{ |hash, key| hash[key] = Set.new }
21
- @proc = proc
17
+ def initialize(client, name, proc = Proc.new { |_|})
18
+ @client = client
19
+ @name = name
20
+ @bindings = Hash.new { |hash, key| hash[key] = Set.new }
21
+ @proc = proc
22
22
  end
23
23
 
24
24
  def bind(exchange, opts = {})
@@ -44,7 +44,7 @@ module Isimud
44
44
  end
45
45
 
46
46
  def make_regexp(key)
47
- Regexp.new(key.gsub(/\./, "\\.").gsub(/\*/, '.*'))
47
+ Regexp.new(key.gsub(/\./, "\\.").gsub(/\*/, '[^.]*').gsub(/#/, '.*'))
48
48
  end
49
49
 
50
50
  def has_matching_key?(exchange, route)
@@ -1,3 +1,3 @@
1
1
  module Isimud
2
- VERSION = '1.3.9'
2
+ VERSION = '1.4.1'
3
3
  end
data/release CHANGED
@@ -27,4 +27,4 @@ puts 'Publishing to RubyGems'.cyan
27
27
  run! "gem push #{built_gem_path}"
28
28
 
29
29
  puts 'Testing install'.cyan
30
- run! "gem install #{gem_name} -v #{Isimud::VERSION} -P MediumSecurity"
30
+ run! "gem install #{gem_version} -P MediumSecurity"
@@ -1,17 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Isimud::BunnyClient do
4
- before(:each) do
5
- @exchange_name = 'isimud_test'
6
- @url = 'amqp://guest:guest@localhost'
7
- end
8
-
9
- let!(:client) { Isimud::BunnyClient.new(@url) }
4
+ let!(:exchange_name) { 'isimud_test' }
5
+ let!(:url) { 'amqp://guest:guest@localhost' }
6
+ let!(:client) { Isimud::BunnyClient.new(url) }
10
7
  let!(:connection) { client.connection }
11
8
 
12
9
  describe '#initialize' do
13
10
  it 'sets the broker URL' do
14
- expect(client.url).to eq(@url)
11
+ expect(client.url).to eq(url)
15
12
  end
16
13
  end
17
14
 
@@ -20,48 +17,52 @@ describe Isimud::BunnyClient do
20
17
  let(:proc) { Proc.new { puts('hello') } }
21
18
  let(:keys) { %w(foo.bar baz.*) }
22
19
  let(:queue_name) { 'my_queue' }
20
+
23
21
  before do
24
- Isimud.logger = Logger.new(STDOUT)
22
+ client.connect
23
+ @block_called = Array.new
25
24
  end
25
+
26
26
  after do
27
27
  client.delete_queue(queue_name)
28
+ client.close
28
29
  end
29
30
 
30
-
31
31
  it 'creates a new queue' do
32
- consumer = client.bind('my_queue', @exchange_name, keys, &proc)
32
+ consumer = client.bind(queue_name, exchange_name, keys, &proc)
33
33
  expect(consumer).to be_a Bunny::Consumer
34
- expect(consumer.queue_name).to eq('my_queue')
34
+ expect(consumer.queue_name).to eq(queue_name)
35
35
  end
36
36
 
37
37
  context 'when a block is passed to the call' do
38
38
  it 'binds specified routing keys and subscribes to the specified exchange' do
39
- queue = double('queue', name: 'my_queue', bind: 'ok')
40
- expect(client).to receive(:find_queue).with('my_queue', durable: true).and_return(queue)
39
+ queue = double('queue', name: queue_name, bind: 'ok')
40
+ expect(client).to receive(:find_queue).with(queue_name, durable: true).and_return(queue)
41
41
  expect(queue).to receive(:subscribe).with({manual_ack: true})
42
- keys.each { |key| expect(queue).to receive(:bind).with(@exchange_name, routing_key: key, nowait: false).once }
43
- client.bind('my_queue', @exchange_name, *keys, &proc)
42
+ keys.each { |key| expect(queue).to receive(:bind).with(exchange_name, routing_key: key, nowait: false).once }
43
+ client.bind(queue_name, exchange_name, *keys, &proc)
44
44
  end
45
45
  end
46
46
 
47
47
  context 'when a block is NOT passed' do
48
- it 'binds specified routing keys BUT does not subscribes to the specified exchange' do
49
- queue = double('queue', name: 'my_queue', bind: 'ok')
50
- expect(client).to receive(:find_queue).with('my_queue', durable: true).and_return(queue)
48
+ it 'binds specified routing keys BUT does not subscribe to the specified exchange' do
49
+ queue = double('queue', name: queue_name, bind: 'ok')
50
+ expect(client).to receive(:find_queue).with(queue_name, durable: true).and_return(queue)
51
51
  expect(queue).not_to receive(:subscribe).with(manual_ack: true)
52
- keys.each { |key| expect(queue).to receive(:bind).with(@exchange_name, routing_key: key, nowait: false).once }
53
- client.bind('my_queue', @exchange_name, *keys)
52
+ keys.each { |key| expect(queue).to receive(:bind).with(exchange_name, routing_key: key, nowait: false).once }
53
+ client.bind(queue_name, exchange_name, *keys)
54
54
  end
55
55
  end
56
56
 
57
57
  it 'calls block when a message is received' do
58
- @block_called = Array.new
59
- queue_name = 'test_queue'
60
- client.bind("#{queue_name}", @exchange_name, 'my.test.key') do |payload|
58
+ client.create_queue(queue_name, exchange_name)
59
+ client.channel.wait_for_confirms
60
+ client.bind(queue_name, exchange_name, 'my.test.key') do |payload|
61
61
  @block_called << payload
62
62
  end
63
- client.publish(@exchange_name, 'my.test.key', "Hi there")
64
- sleep(1)
63
+ client.channel.wait_for_confirms
64
+ client.publish(exchange_name, 'my.test.key', "Hi there")
65
+ client.channel.wait_for_confirms
65
66
  expect(@block_called).to eq ['Hi there']
66
67
  end
67
68
  end
@@ -134,9 +135,9 @@ describe Isimud::BunnyClient do
134
135
  it 'sends the data with the appropriate routing key to the exchange' do
135
136
  payload = {a: '123', b: 'this is b'}
136
137
  topic = double(:topic)
137
- expect(channel).to receive(:topic).with(@exchange_name, durable: true).and_return(topic)
138
+ expect(channel).to receive(:topic).with(exchange_name, durable: true).and_return(topic)
138
139
  expect(topic).to receive(:publish).with(payload, routing_key: 'foo.bar.baz', persistent: true)
139
- client.publish(@exchange_name, 'foo.bar.baz', payload)
140
+ client.publish(exchange_name, 'foo.bar.baz', payload)
140
141
  end
141
142
  end
142
143
 
@@ -4,13 +4,15 @@ describe Isimud::Client do
4
4
  let(:client) { Isimud::Client.new }
5
5
 
6
6
  before do
7
- @exceptions_1 = Array.new
8
- @exceptions_2 = Array.new
7
+ @exceptions_1 = Array.new
8
+ @exceptions_2 = Array.new
9
+ @return_status_1 = true
10
+ @return_status_2 = false
9
11
  end
10
12
 
11
13
  describe 'exception handling' do
12
- let!(:exception_handler_1) { Proc.new { |e| @exceptions_1 << e } }
13
- let!(:exception_handler_2) { Proc.new { |e| @exceptions_2 << e } }
14
+ let(:exception_handler_1) { Proc.new { |e| @exceptions_1 << e; @return_status_1 } }
15
+ let(:exception_handler_2) { Proc.new { |e| @exceptions_2 << e; @return_status_2 } }
14
16
  let!(:exception) { double(:exception) }
15
17
 
16
18
  context 'with one handler added' do
@@ -28,13 +30,56 @@ describe Isimud::Client do
28
30
  before do
29
31
  client.on_exception(&exception_handler_1)
30
32
  client.on_exception(&exception_handler_2)
31
- client.run_exception_handlers(exception)
32
33
  end
33
34
 
34
35
  it 'calls both exception handlers' do
36
+ client.run_exception_handlers(exception)
35
37
  expect(@exceptions_1).to include(exception)
36
38
  expect(@exceptions_2).to include(exception)
37
39
  end
40
+
41
+
42
+ context 'with Isimud.retry_failures set to false' do
43
+ before do
44
+ Isimud.retry_failures = false
45
+ end
46
+
47
+ it 'does not requeue message' do
48
+ expect(client.run_exception_handlers(exception)).to be_falsey
49
+ end
50
+ end
51
+
52
+ context 'with Isimud.retry_failures set to true regardless of handler return status' do
53
+ before do
54
+ Isimud.retry_failures = true
55
+ end
56
+
57
+ it 'requeues message' do
58
+ expect(client.run_exception_handlers(exception)).to be_truthy
59
+ end
60
+ end
61
+
62
+ context 'with Isimud.retry_failures nil' do
63
+ before do
64
+ Isimud.retry_failures = nil
65
+ end
66
+
67
+ context 'and all exception handlers return truthy status' do
68
+ before do
69
+ @return_status_2 = true
70
+ end
71
+
72
+ it 'requeues message' do
73
+ expect(client.run_exception_handlers(exception)).to be_truthy
74
+ end
75
+ end
76
+
77
+ context 'and any exception handler returns falsey status' do
78
+ it 'does not requeue message if any handler returns false' do
79
+ expect(client.run_exception_handlers(exception)).to be_falsey
80
+ end
81
+ end
82
+ end
38
83
  end
39
84
  end
40
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isimud
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.9
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - George Feil
@@ -31,7 +31,7 @@ cert_chain:
31
31
  wSKk31gq6KY7rgi11mcLvVzwSuwZFqnonAmu/uin3cbXcOUkqNsyzZ0LQLXNKiwi
32
32
  k0RgrsSNtQ8hcsVSQla0Ot+X3WpFI4ZuIfSLFA==
33
33
  -----END CERTIFICATE-----
34
- date: 2016-09-14 00:00:00.000000000 Z
34
+ date: 2016-08-25 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: activerecord
@@ -114,6 +114,7 @@ files:
114
114
  - Rakefile
115
115
  - certs/gfeil.pem
116
116
  - checksum/isimud-1.3.8.gem.sha512
117
+ - checksum/isimud-1.4.0.gem.sha512
117
118
  - config.ru
118
119
  - config/tddium.yml
119
120
  - doc/Isimud.html
@@ -124,8 +125,6 @@ files:
124
125
  - doc/Isimud/EventObserver.html
125
126
  - doc/Isimud/EventObserver/ClassMethods.html
126
127
  - doc/Isimud/Generators.html
127
- - doc/Isimud/Generators/ConfigGenerator.html
128
- - doc/Isimud/Generators/InitializerGenerator.html
129
128
  - doc/Isimud/Logging.html
130
129
  - doc/Isimud/ModelWatcher.html
131
130
  - doc/Isimud/ModelWatcher/ClassMethods.html