isimud 0.7.0 → 1.3.1
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 +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -2
- data/.ruby-version +1 -1
- data/.yardoc/checksums +10 -10
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +63 -73
- data/LICENSE.txt +19 -0
- data/README.md +51 -3
- data/Rakefile +5 -0
- data/doc/Isimud/BunnyClient.html +882 -179
- data/doc/Isimud/Client.html +236 -18
- data/doc/Isimud/Event.html +211 -95
- data/doc/Isimud/EventListener.html +325 -307
- data/doc/Isimud/EventObserver/ClassMethods.html +14 -14
- data/doc/Isimud/EventObserver.html +418 -36
- data/doc/Isimud/Generators/ConfigGenerator.html +2 -2
- data/doc/Isimud/Generators/InitializerGenerator.html +2 -2
- data/doc/Isimud/Generators.html +2 -2
- data/doc/Isimud/Logging.html +3 -3
- data/doc/Isimud/ModelWatcher/ClassMethods.html +3 -3
- data/doc/Isimud/ModelWatcher.html +2 -2
- data/doc/Isimud/Railtie.html +2 -2
- data/doc/Isimud/TestClient/Queue.html +374 -71
- data/doc/Isimud/TestClient.html +169 -161
- data/doc/Isimud.html +80 -76
- data/doc/_index.html +5 -2
- data/doc/file.LICENSE.html +73 -0
- data/doc/file.README.html +131 -7
- data/doc/file_list.html +3 -0
- data/doc/index.html +131 -7
- data/doc/method_list.html +183 -105
- data/doc/top-level-namespace.html +2 -2
- data/isimud.gemspec +18 -16
- data/lib/isimud/bunny_client.rb +85 -32
- data/lib/isimud/client.rb +23 -7
- data/lib/isimud/event.rb +11 -14
- data/lib/isimud/event_listener.rb +123 -65
- data/lib/isimud/event_observer.rb +70 -10
- data/lib/isimud/model_watcher.rb +1 -1
- data/lib/isimud/test_client.rb +54 -26
- data/lib/isimud/version.rb +1 -1
- data/lib/isimud.rb +1 -1
- data/spec/internal/app/models/company.rb +8 -10
- data/spec/internal/app/models/user.rb +11 -1
- data/spec/internal/db/schema.rb +5 -0
- data/spec/isimud/bunny_client_spec.rb +21 -9
- data/spec/isimud/client_spec.rb +40 -0
- data/spec/isimud/event_listener_spec.rb +50 -22
- data/spec/isimud/event_observer_spec.rb +107 -16
- data/spec/isimud/model_watcher_spec.rb +18 -23
- data/spec/isimud/test_client_spec.rb +43 -8
- data/spec/isimud_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -0
- metadata +19 -35
- checksums.yaml.gz.sig +0 -0
- data/certs/gfeil.pem +0 -21
- data/release +0 -31
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -2
@@ -4,6 +4,9 @@ require 'active_support/concern'
|
|
4
4
|
require 'active_support/core_ext/module/attribute_accessors'
|
5
5
|
|
6
6
|
# Module for attaching and listening to events
|
7
|
+
#
|
8
|
+
# Note: the following columns must be defined in your model:
|
9
|
+
# :exchange_routing_keys text
|
7
10
|
module Isimud
|
8
11
|
module EventObserver
|
9
12
|
extend ::ActiveSupport::Concern
|
@@ -17,9 +20,19 @@ module Isimud
|
|
17
20
|
Mutex.new
|
18
21
|
end
|
19
22
|
|
23
|
+
included do
|
24
|
+
register_class
|
25
|
+
before_save :set_routing_keys
|
26
|
+
serialize :exchange_routing_keys, Array
|
27
|
+
after_commit :create_queue, on: :create, if: :enable_listener?, prepend: true
|
28
|
+
after_commit :update_queue, on: :update, prepend: true
|
29
|
+
after_commit :delete_queue, on: :destroy, prepend: true
|
30
|
+
include Isimud::ModelWatcher unless self.include?(Isimud::ModelWatcher)
|
31
|
+
end
|
32
|
+
|
20
33
|
# Event handling hook. Override in your class.
|
21
34
|
def handle_event(event)
|
22
|
-
|
35
|
+
logger.warn("Isimud::EventObserver#handle_event not implemented for #{event_queue_name}")
|
23
36
|
end
|
24
37
|
|
25
38
|
# Routing keys that are bound to the event queue. Override in your subclass
|
@@ -32,16 +45,18 @@ module Isimud
|
|
32
45
|
true
|
33
46
|
end
|
34
47
|
|
35
|
-
# Exchange used for listening to events. Override in your subclass if you want to specify an alternative exchange
|
36
|
-
# events. Otherwise
|
48
|
+
# Exchange used for listening to events. Override in your subclass if you want to specify an alternative exchange.
|
37
49
|
def observed_exchange
|
38
50
|
nil
|
39
51
|
end
|
40
52
|
|
41
53
|
# Create or attach to a queue on the specified exchange. When an event message that matches the observer's routing keys
|
42
54
|
# is received, parse the event and call handle_event on same.
|
43
|
-
|
44
|
-
|
55
|
+
# Returns the consumer for the observer
|
56
|
+
def observe_events(client)
|
57
|
+
return unless enable_listener?
|
58
|
+
queue = client.kind_of?(Isimud::TestClient) ? create_queue(client) : client.find_queue(event_queue_name)
|
59
|
+
client.subscribe(queue) do |message|
|
45
60
|
event = Event.parse(message)
|
46
61
|
handle_event(event)
|
47
62
|
end
|
@@ -51,6 +66,56 @@ module Isimud
|
|
51
66
|
self.class.event_queue_name(id)
|
52
67
|
end
|
53
68
|
|
69
|
+
def isimud_client
|
70
|
+
Isimud.client
|
71
|
+
end
|
72
|
+
|
73
|
+
# Activate the queues for an observer. This will create the observer queue and send an update message on the
|
74
|
+
# instance, which will trigger EventListener instances to set up consumers. This is useful for situations when
|
75
|
+
# an observer is to be made active without an update.
|
76
|
+
def activate_observer(client = isimud_client)
|
77
|
+
create_queue(client)
|
78
|
+
isimud_send_action_message(:update)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Deactivate the queues for an observer. This will destroy the observer queue and send an update message on the
|
82
|
+
# instance, which will trigger EventListener instances to cancel consumers. Note that enable_listener? should
|
83
|
+
# resolve to false in order for the EventListener to cancel corresponding event consumers.
|
84
|
+
def deactivate_observer(client = isimud_client)
|
85
|
+
delete_queue(client)
|
86
|
+
isimud_send_action_message(:update)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def create_queue(client = isimud_client)
|
92
|
+
exchange = observed_exchange || Isimud.events_exchange
|
93
|
+
log "Isimud::EventObserver: creating queue #{event_queue_name} on exchange #{exchange} with bindings [#{exchange_routing_keys.join(',')}]"
|
94
|
+
client.create_queue(event_queue_name, exchange, routing_keys: exchange_routing_keys)
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_queue
|
98
|
+
if enable_listener?
|
99
|
+
routing_key_changes = previous_changes[:exchange_routing_keys]
|
100
|
+
prev_keys = routing_key_changes.try(:[], 0) || []
|
101
|
+
current_keys = routing_key_changes.try(:[], 1) || exchange_routing_keys
|
102
|
+
queue = isimud_client.find_queue(event_queue_name)
|
103
|
+
exchange = observed_exchange || Isimud.events_exchange
|
104
|
+
(prev_keys - current_keys).each { |key| queue.unbind(exchange, routing_key: key) }
|
105
|
+
(current_keys).each { |key| queue.bind(exchange, routing_key: key) }
|
106
|
+
else
|
107
|
+
isimud_client.delete_queue(event_queue_name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def delete_queue
|
112
|
+
isimud_client.delete_queue(event_queue_name)
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_routing_keys
|
116
|
+
self.exchange_routing_keys = routing_keys
|
117
|
+
end
|
118
|
+
|
54
119
|
module ClassMethods
|
55
120
|
# Method used to retrieve active observers. Override in your EventObserver class
|
56
121
|
def find_active_observers
|
@@ -76,10 +141,5 @@ module Isimud
|
|
76
141
|
end
|
77
142
|
end
|
78
143
|
end
|
79
|
-
|
80
|
-
included do
|
81
|
-
include Isimud::ModelWatcher unless self.include?(Isimud::ModelWatcher)
|
82
|
-
register_class
|
83
|
-
end
|
84
144
|
end
|
85
145
|
end
|
data/lib/isimud/model_watcher.rb
CHANGED
@@ -135,7 +135,7 @@ module Isimud
|
|
135
135
|
id: id,
|
136
136
|
timestamp: (updated_at || Time.now).utc
|
137
137
|
}
|
138
|
-
payload[:attributes] = isimud_attribute_data
|
138
|
+
payload[:attributes] = isimud_attribute_data
|
139
139
|
routing_key = isimud_model_watcher_routing_key(action)
|
140
140
|
log "Isimud::ModelWatcher#publish: exchange #{isimud_model_watcher_exchange} routing_key #{routing_key} payload #{payload.inspect}"
|
141
141
|
Isimud.client.publish(isimud_model_watcher_exchange, routing_key, payload.to_json)
|
data/lib/isimud/test_client.rb
CHANGED
@@ -1,42 +1,66 @@
|
|
1
1
|
module Isimud
|
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.
|
2
9
|
class TestClient < Isimud::Client
|
3
10
|
attr_accessor :queues
|
4
11
|
|
5
12
|
class Queue
|
6
13
|
include Isimud::Logging
|
7
|
-
attr_reader :name, :
|
14
|
+
attr_reader :name, :bindings
|
15
|
+
attr_accessor :proc
|
8
16
|
|
9
|
-
def initialize(name,
|
17
|
+
def initialize(name, proc = Proc.new{ |_| } )
|
10
18
|
@name = name
|
11
|
-
@
|
12
|
-
@
|
19
|
+
@bindings = Hash.new{ |hash, key| hash[key] = Set.new }
|
20
|
+
@proc = proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def bind(exchange, opts = {})
|
24
|
+
routing_key = opts[:routing_key]
|
25
|
+
log "TestClient: adding routing key #{routing_key} for exchange #{exchange} to queue #{name}"
|
26
|
+
@bindings[exchange] << routing_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def cancel
|
30
|
+
@proc = nil
|
13
31
|
end
|
14
32
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
@
|
33
|
+
def delete(opts = {})
|
34
|
+
log "TestClient: delete queue #{name}"
|
35
|
+
@bindings.clear
|
36
|
+
@proc = nil
|
19
37
|
end
|
20
38
|
|
21
|
-
def unbind(exchange)
|
22
|
-
|
39
|
+
def unbind(exchange, opts = {})
|
40
|
+
routing_key = opts[:routing_key]
|
41
|
+
log "TestClient: removing routing key #{routing_key} for exchange #{exchange} from queue #{name}"
|
42
|
+
@bindings[exchange].delete(routing_key)
|
23
43
|
end
|
24
44
|
|
25
|
-
def
|
26
|
-
|
45
|
+
def make_regexp(key)
|
46
|
+
Regexp.new(key.gsub(/\./, "\\.").gsub(/\*/, '.*'))
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_matching_key?(exchange, route)
|
50
|
+
@bindings[exchange].any? { |key| route =~ make_regexp(key) }
|
27
51
|
end
|
28
52
|
|
29
53
|
def deliver(data)
|
30
54
|
begin
|
31
|
-
@
|
55
|
+
@proc.try(:call, data)
|
32
56
|
rescue => e
|
33
57
|
log "TestClient: error delivering message: #{e.message}\n #{e.backtrace.join("\n ")}", :error
|
34
|
-
|
58
|
+
run_exception_handlers(e)
|
35
59
|
end
|
36
60
|
end
|
37
61
|
end
|
38
62
|
|
39
|
-
def initialize(connection = nil, options =
|
63
|
+
def initialize(connection = nil, options = {})
|
40
64
|
self.queues = Hash.new
|
41
65
|
end
|
42
66
|
|
@@ -59,29 +83,33 @@ module Isimud
|
|
59
83
|
queues.delete(queue_name)
|
60
84
|
end
|
61
85
|
|
62
|
-
def bind(queue_name, exchange_name, *keys, &
|
63
|
-
create_queue(queue_name, exchange_name, routing_keys: keys
|
86
|
+
def bind(queue_name, exchange_name, *keys, &block)
|
87
|
+
queue = create_queue(queue_name, exchange_name, routing_keys: keys)
|
88
|
+
subscribe(queue, &block)
|
64
89
|
end
|
65
90
|
|
66
|
-
def
|
67
|
-
|
68
|
-
queue.unbind(exchange_name)
|
69
|
-
keys.each { |key| queue.bind(exchange_name, routing_key: key) }
|
91
|
+
def find_queue(queue_name, options = {})
|
92
|
+
queues[queue_name] ||= Queue.new(queue_name)
|
70
93
|
end
|
71
94
|
|
72
|
-
def create_queue(queue_name, exchange_name, options = {}
|
95
|
+
def create_queue(queue_name, exchange_name, options = {})
|
73
96
|
keys = options[:routing_keys] || []
|
74
97
|
log "Isimud::TestClient: Binding queue #{queue_name} for keys #{keys.inspect}"
|
75
|
-
queue =
|
98
|
+
queue = find_queue(queue_name)
|
76
99
|
keys.each do |k|
|
77
100
|
queue.bind(exchange_name, routing_key: k)
|
78
101
|
end
|
79
102
|
queue
|
80
103
|
end
|
81
104
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
105
|
+
def subscribe(queue, options = {}, &block)
|
106
|
+
queue.proc = block
|
107
|
+
queue
|
108
|
+
end
|
109
|
+
|
110
|
+
def publish(exchange, routing_key, payload)
|
111
|
+
log "Isimud::TestClient: Delivering message exchange: #{exchange} key: #{routing_key} payload: #{payload}"
|
112
|
+
call_queues = queues.values.select { |queue| queue.has_matching_key?(exchange, routing_key) }
|
85
113
|
call_queues.each do |queue|
|
86
114
|
log "Isimud::TestClient: Queue #{queue.name} matches routing key #{routing_key}"
|
87
115
|
queue.deliver(payload)
|
data/lib/isimud/version.rb
CHANGED
data/lib/isimud.rb
CHANGED
@@ -13,6 +13,10 @@ class Company < ActiveRecord::Base
|
|
13
13
|
active
|
14
14
|
end
|
15
15
|
|
16
|
+
def observed_exchange
|
17
|
+
'isimud.test.events'
|
18
|
+
end
|
19
|
+
|
16
20
|
def routing_keys
|
17
21
|
["*.User.create", "*.User.destroy"]
|
18
22
|
end
|
@@ -20,15 +24,9 @@ class Company < ActiveRecord::Base
|
|
20
24
|
def handle_event(event)
|
21
25
|
user = User.find(event.parameters[:id])
|
22
26
|
return unless user.company_id == id
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
when 'destroy'
|
28
|
-
reload
|
29
|
-
update_attributes!(user_count: user_count - 1)
|
30
|
-
else
|
31
|
-
raise "unexpected action: #{event.action}"
|
32
|
-
end
|
27
|
+
raise "unexpected action: #{event.action}" unless ['create','destroy' ].include?(event.action.to_s)
|
28
|
+
self.user_count = User.where(company: self).count
|
29
|
+
self.total_points = user_count * points_per_user
|
30
|
+
save!
|
33
31
|
end
|
34
32
|
end
|
@@ -5,7 +5,9 @@ class User < ActiveRecord::Base
|
|
5
5
|
|
6
6
|
belongs_to :company
|
7
7
|
|
8
|
-
attr_accessor :events
|
8
|
+
attr_accessor :events
|
9
|
+
|
10
|
+
serialize :keys, Array
|
9
11
|
|
10
12
|
scope :active, -> {where('deactivated != ?', true)}
|
11
13
|
|
@@ -18,6 +20,14 @@ class User < ActiveRecord::Base
|
|
18
20
|
'test'
|
19
21
|
end
|
20
22
|
|
23
|
+
def routing_keys
|
24
|
+
keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def enable_listener?
|
28
|
+
!deactivated
|
29
|
+
end
|
30
|
+
|
21
31
|
watch_attributes :key, :login_count
|
22
32
|
|
23
33
|
def key
|
data/spec/internal/db/schema.rb
CHANGED
@@ -5,6 +5,9 @@ ActiveRecord::Schema.define do
|
|
5
5
|
t.string :url
|
6
6
|
t.integer :user_count, default: 0, null: false
|
7
7
|
t.boolean :active, default: true, null: false
|
8
|
+
t.string :exchange_routing_keys
|
9
|
+
t.integer :points_per_user, default: 1, null: false
|
10
|
+
t.integer :total_points, default: 0, null: false
|
8
11
|
t.timestamps
|
9
12
|
end
|
10
13
|
|
@@ -17,6 +20,8 @@ ActiveRecord::Schema.define do
|
|
17
20
|
t.boolean :is_admin
|
18
21
|
t.boolean :deactivated
|
19
22
|
t.integer :login_count, default: 0, null: false
|
23
|
+
t.string :keys
|
24
|
+
t.string :exchange_routing_keys
|
20
25
|
t.timestamps
|
21
26
|
end
|
22
27
|
end
|
@@ -29,17 +29,29 @@ describe Isimud::BunnyClient do
|
|
29
29
|
|
30
30
|
|
31
31
|
it 'creates a new queue' do
|
32
|
-
|
33
|
-
expect(
|
34
|
-
expect(
|
32
|
+
consumer = client.bind('my_queue', @exchange_name, keys, &proc)
|
33
|
+
expect(consumer).to be_a Bunny::Consumer
|
34
|
+
expect(consumer.queue_name).to eq('my_queue')
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when a block is passed to the call' do
|
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)
|
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)
|
44
|
+
end
|
35
45
|
end
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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)
|
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)
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
57
|
it 'calls block when a message is received' do
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Isimud::Client do
|
4
|
+
let(:client) { Isimud::Client.new }
|
5
|
+
|
6
|
+
before do
|
7
|
+
@exceptions_1 = Array.new
|
8
|
+
@exceptions_2 = Array.new
|
9
|
+
end
|
10
|
+
|
11
|
+
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) { double(:exception) }
|
15
|
+
|
16
|
+
context 'with one handler added' do
|
17
|
+
before do
|
18
|
+
client.on_exception(&exception_handler_1)
|
19
|
+
client.run_exception_handlers(exception)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'calls the exception handler' do
|
23
|
+
expect(@exceptions_1).to include(exception)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with two handlers added' do
|
28
|
+
before do
|
29
|
+
client.on_exception(&exception_handler_1)
|
30
|
+
client.on_exception(&exception_handler_2)
|
31
|
+
client.run_exception_handlers(exception)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'calls both exception handlers' do
|
35
|
+
expect(@exceptions_1).to include(exception)
|
36
|
+
expect(@exceptions_2).to include(exception)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -26,8 +26,8 @@ describe Isimud::EventListener do
|
|
26
26
|
it 'applies defaults' do
|
27
27
|
listener = Isimud::EventListener.new
|
28
28
|
expect(listener.events_exchange).to eq('events')
|
29
|
-
expect(listener.models_exchange).to eq('
|
30
|
-
expect(listener.error_limit).to eq(
|
29
|
+
expect(listener.models_exchange).to eq('isimud.test.events')
|
30
|
+
expect(listener.error_limit).to eq(50)
|
31
31
|
expect(listener.error_interval).to eq(1.hour)
|
32
32
|
expect(listener.name).to eq('combustion-listener')
|
33
33
|
end
|
@@ -52,35 +52,63 @@ describe Isimud::EventListener do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
describe '
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
company.
|
60
|
-
|
55
|
+
describe '#handle_observer_event' do
|
56
|
+
context 'handling observer destroy messages' do
|
57
|
+
it 'purges the queue for a destroy observer' do
|
58
|
+
expect(listener).to have_observer(company)
|
59
|
+
company.destroy
|
60
|
+
expect(listener).not_to have_observer(company)
|
61
|
+
end
|
61
62
|
end
|
62
|
-
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
context 'handling new observer messages' do
|
65
|
+
it 'registers a new observer' do
|
66
|
+
another_company = Company.create!(name: 'Apple', active: true)
|
67
|
+
expect(listener).to have_observer(another_company)
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
it 'does not register an observer when listening is disabled' do
|
71
|
+
another_company = Company.create!(name: 'Apple', active: false)
|
72
|
+
expect(listener).not_to have_observer(another_company)
|
73
|
+
end
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
context 'handling observer update messages' do
|
77
|
+
it 'reloads the observer and processes events accordingly' do
|
78
|
+
company.update_attributes!(points_per_user: 2)
|
79
|
+
expect(listener).to have_observer(company)
|
80
|
+
expect {
|
81
|
+
User.create!(company: company, first_name: 'Larry', last_name: 'Page')
|
82
|
+
company.reload
|
83
|
+
}.to change(company, :user_count).by(1)
|
84
|
+
expect(company.total_points).to eql(company.user_count * 2)
|
85
|
+
end
|
86
|
+
end
|
77
87
|
|
78
|
-
|
88
|
+
context 'handling messages' do
|
89
|
+
it 'dispatches events to observer' do
|
90
|
+
expect {
|
91
|
+
User.create!(company: company, first_name: 'Larry', last_name: 'Page')
|
92
|
+
company.reload
|
93
|
+
}.to change(company, :user_count)
|
94
|
+
end
|
95
|
+
end
|
79
96
|
end
|
80
97
|
|
81
98
|
describe 'handling errors' do
|
82
|
-
|
83
|
-
|
99
|
+
before do
|
100
|
+
@some_company = Company.create!(name: 'Apple', active: true)
|
101
|
+
end
|
102
|
+
xit 'counts errors' do
|
103
|
+
# Verify that the listener's error count starts from zero
|
104
|
+
expect(listener.error_count).to eql 0
|
105
|
+
|
106
|
+
# Verify that the listener's error count increased to one with the first new exception
|
107
|
+
# Verify that the listener's error count increased to two with the second new exception
|
108
|
+
end
|
109
|
+
xit 'triggers a shutdown if errors exceed limit' do
|
110
|
+
# Verify that the listener shuts down / stops listening when the threshold is exceeded
|
111
|
+
end
|
84
112
|
end
|
85
113
|
end
|
86
114
|
end
|
@@ -1,32 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
1
3
|
describe Isimud::EventObserver do
|
2
|
-
|
4
|
+
before do
|
5
|
+
Isimud.model_watcher_exchange = 'isimud.test.events'
|
6
|
+
@client = Isimud.client
|
7
|
+
end
|
8
|
+
|
9
|
+
#let(:client) { Isimud.client }
|
3
10
|
let(:exchange_name) { 'events' }
|
4
|
-
let(:
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
11
|
+
let(:company) { Company.create!(name: 'Keas', description: 'Health Engagement Platform', url: 'http://keas.com') }
|
12
|
+
let(:keys) { %w(a.b.c d.*.f) }
|
13
|
+
let(:user_params) { {first_name: 'Geo',
|
14
|
+
last_name: 'Feil',
|
15
|
+
encrypted_password: "itsasecret",
|
16
|
+
keys: keys,
|
17
|
+
email: 'george.feil@keas.com'} }
|
9
18
|
let(:time) { 1.hour.ago }
|
10
19
|
let(:params) { {'a' => 'foo', 'b' => 123} }
|
11
|
-
let!(:evented) { User.create(routing_keys: ['model.Company.*.create', 'model.Event.*.report']) }
|
12
|
-
let(:event) { Isimud::Event.new(user, eventful, action: :create, occurred_at: time, parameters: params) }
|
13
|
-
|
14
20
|
|
15
21
|
it 'registers class in observed_models' do
|
16
22
|
expect(Isimud::EventObserver.observed_models).to include(User)
|
17
23
|
end
|
18
24
|
|
19
|
-
describe '
|
20
|
-
before
|
21
|
-
|
25
|
+
describe 'when created' do
|
26
|
+
before do
|
27
|
+
@queue = double(:queue)
|
28
|
+
@exchange = Isimud.events_exchange
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates observer queue' do
|
32
|
+
expect(@client).to receive(:find_queue).and_return(@queue)
|
33
|
+
keys.each { |key| expect(@queue).to receive(:bind).with(@exchange, routing_key: key) }
|
34
|
+
User.create(user_params)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'does not create observer queue when listener is not enabled' do
|
38
|
+
expect(@client).not_to receive(:create_queue)
|
39
|
+
User.create(user_params.merge(deactivated: true))
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'sets exchange_routing_keys' do
|
43
|
+
user = User.create(user_params)
|
44
|
+
expect(user.exchange_routing_keys).to eql(user.routing_keys)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'when modified' do
|
49
|
+
before do
|
50
|
+
@exchange = Isimud.events_exchange
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'for an instance that is made active on update' do
|
54
|
+
before do
|
55
|
+
@user = User.create(user_params.merge(deactivated: true))
|
56
|
+
@queue = @client.find_queue(@user.event_queue_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'creates the queue' do
|
60
|
+
expect(@client).to receive(:find_queue).with(@user.event_queue_name).and_call_original
|
61
|
+
@user.update_attributes(deactivated: false)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'binds the routing keys' do
|
65
|
+
@user.exchange_routing_keys.each do |k|
|
66
|
+
expect(@queue).to receive(:bind).with(@exchange, routing_key: k).and_call_original
|
67
|
+
end
|
68
|
+
@user.update_attributes(deactivated: false)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'for an already active instance' do
|
73
|
+
before do
|
74
|
+
@user = User.create(user_params)
|
75
|
+
@queue = @client.find_queue(@user.event_queue_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'binds new keys' do
|
79
|
+
@user.exchange_routing_keys.each do |k|
|
80
|
+
expect(@queue).to receive(:bind).with(@exchange, routing_key: k).and_call_original
|
81
|
+
end
|
82
|
+
expect(@queue).to receive(:bind).with(@exchange, routing_key: 'some_other_value').and_call_original
|
83
|
+
@user.transaction do
|
84
|
+
@user.keys << 'some_other_value'
|
85
|
+
@user.save!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'removes old keys' do
|
90
|
+
expect(@queue).to receive(:unbind).with(@exchange, routing_key: 'a.b.c')
|
91
|
+
expect(@queue).not_to receive(:unbind).with(@exchange, routing_key: 'd.*.f')
|
92
|
+
@user.keys.delete('a.b.c')
|
93
|
+
@user.save!
|
94
|
+
end
|
22
95
|
end
|
96
|
+
end
|
23
97
|
|
24
|
-
|
25
|
-
|
26
|
-
|
98
|
+
describe 'when destroyed' do
|
99
|
+
before do
|
100
|
+
@exchange = Isimud.events_exchange
|
101
|
+
@user = User.create(user_params)
|
27
102
|
end
|
28
103
|
|
29
|
-
it '
|
104
|
+
it 'removes the queue' do
|
105
|
+
expect(@client).to receive(:delete_queue).with("combustion.user.#{@user.id}")
|
106
|
+
@user.destroy
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#observe_events' do
|
111
|
+
before do
|
112
|
+
@user = User.create(user_params.merge(keys: ['model.Company.*.create']))
|
113
|
+
@user.observe_events(@client)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'parses messages and dispatches to the handle_event method' do
|
117
|
+
event = Isimud::Event.new(@user, company, exchange: @exchange, action: :create, occurred_at: time, parameters: params)
|
118
|
+
event.publish
|
119
|
+
expect(@user.events).not_to be_empty
|
120
|
+
end
|
30
121
|
end
|
31
122
|
|
32
123
|
end
|