vx-common-amqp 0.2.6
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +276 -0
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/bin/vx-consumers +12 -0
- data/lib/vx/common/amqp/cli.rb +88 -0
- data/lib/vx/common/amqp/config.rb +74 -0
- data/lib/vx/common/amqp/consumer/ack.rb +19 -0
- data/lib/vx/common/amqp/consumer/configuration.rb +119 -0
- data/lib/vx/common/amqp/consumer/publish.rb +32 -0
- data/lib/vx/common/amqp/consumer/subscribe.rb +67 -0
- data/lib/vx/common/amqp/consumer.rb +70 -0
- data/lib/vx/common/amqp/formatter.rb +105 -0
- data/lib/vx/common/amqp/mixins/callbacks.rb +35 -0
- data/lib/vx/common/amqp/mixins/logger.rb +17 -0
- data/lib/vx/common/amqp/session.rb +154 -0
- data/lib/vx/common/amqp/supervisor/threaded.rb +171 -0
- data/lib/vx/common/amqp/testing.rb +54 -0
- data/lib/vx/common/amqp/version.rb +7 -0
- data/lib/vx/common/amqp.rb +68 -0
- data/spec/integration/multi_threaded_spec.rb +89 -0
- data/spec/integration/threaded_supervisor_spec.rb +85 -0
- data/spec/lib/amqp/config_spec.rb +32 -0
- data/spec/lib/amqp/consumer_spec.rb +316 -0
- data/spec/lib/amqp/formatter_spec.rb +47 -0
- data/spec/lib/amqp/mixins/callbacks_spec.rb +26 -0
- data/spec/lib/amqp/session_spec.rb +144 -0
- data/spec/lib/amqp/supervisor/threaded_spec.rb +124 -0
- data/spec/lib/amqp_spec.rb +9 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/amqp.rb +15 -0
- data/spec/support/ignore_me_error.rb +1 -0
- data/vx-common-amqp.gemspec +30 -0
- metadata +178 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Vx
|
2
|
+
module Common
|
3
|
+
module AMQP
|
4
|
+
module Consumer::Publish
|
5
|
+
|
6
|
+
def publish(message, options = nil)
|
7
|
+
session.open
|
8
|
+
|
9
|
+
options ||= {}
|
10
|
+
options[:routing_key] = routing_key if routing_key && !options.key?(:routing_key)
|
11
|
+
options[:headers] = headers if headers && !options.key?(:headers)
|
12
|
+
options[:content_type] ||= content_type || config.content_type
|
13
|
+
|
14
|
+
x = declare_exchange
|
15
|
+
|
16
|
+
run_callbacks(:publish, message: message, exchange: x, name: consumer_id) do
|
17
|
+
m = serialize_message message, options[:content_type]
|
18
|
+
x.publish m, options
|
19
|
+
end
|
20
|
+
|
21
|
+
debug "published #{message.inspect} to #{x.name}"
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def serialize_message(message, content_type)
|
26
|
+
Common::AMQP::Formatter.pack(content_type, message)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Vx
|
2
|
+
module Common
|
3
|
+
module AMQP
|
4
|
+
module Consumer::Subscribe
|
5
|
+
|
6
|
+
def subscribe
|
7
|
+
session.open
|
8
|
+
|
9
|
+
session.with_channel do
|
10
|
+
x = declare_exchange
|
11
|
+
q = declare_queue
|
12
|
+
|
13
|
+
run_callbacks(:subscribe, exchange: x, queue: q, name: consumer_id) do
|
14
|
+
debug "subscribing to #{q.name}:#{x.name} using #{bind_options.inspect}"
|
15
|
+
q.bind(x, bind_options)
|
16
|
+
debug "successfuly subscribed to #{q.name}:#{x.name}"
|
17
|
+
|
18
|
+
subscription_loop q
|
19
|
+
end
|
20
|
+
|
21
|
+
debug "shutdown"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def subscription_loop(q)
|
28
|
+
loop do
|
29
|
+
break if shutdown?
|
30
|
+
|
31
|
+
delivery_info, properties, payload = q.pop(ack: ack)
|
32
|
+
|
33
|
+
if payload
|
34
|
+
result = nil
|
35
|
+
|
36
|
+
debug "recieve ##{delivery_info.delivery_tag.to_i} #{payload.inspect}"
|
37
|
+
result = run_instance delivery_info, properties, payload
|
38
|
+
debug "done ##{delivery_info.delivery_tag.to_i}"
|
39
|
+
|
40
|
+
break if result == :shutdown
|
41
|
+
else
|
42
|
+
sleep config.pool_timeout
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_instance(delivery_info, properties, payload)
|
48
|
+
payload = deserialize_message properties, payload
|
49
|
+
|
50
|
+
run_callbacks :recieve, payload: payload, name: consumer_id do
|
51
|
+
new.tap do |inst|
|
52
|
+
inst.properties = properties
|
53
|
+
inst.delivery_info = delivery_info
|
54
|
+
end.perform payload
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def deserialize_message(properties, payload)
|
59
|
+
Common::AMQP::Formatter.unpack properties[:content_type],
|
60
|
+
model,
|
61
|
+
payload
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
module Common
|
5
|
+
module AMQP
|
6
|
+
module Consumer
|
7
|
+
|
8
|
+
autoload :Configuration, File.expand_path("../consumer/configuration", __FILE__)
|
9
|
+
autoload :Publish, File.expand_path("../consumer/publish", __FILE__)
|
10
|
+
autoload :Subscribe, File.expand_path("../consumer/subscribe", __FILE__)
|
11
|
+
autoload :Sleep, File.expand_path("../consumer/sleep", __FILE__)
|
12
|
+
autoload :Ack, File.expand_path("../consumer/ack", __FILE__)
|
13
|
+
|
14
|
+
include Common::AMQP::Consumer::Ack
|
15
|
+
include Common::AMQP::Logger
|
16
|
+
|
17
|
+
attr_accessor :delivery_info
|
18
|
+
attr_accessor :properties
|
19
|
+
attr_accessor :channel
|
20
|
+
|
21
|
+
@@classes = []
|
22
|
+
|
23
|
+
def self.included(base)
|
24
|
+
base.extend ClassMethods
|
25
|
+
@@classes << base.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.classes
|
29
|
+
@@classes
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
|
34
|
+
include Common::AMQP::Consumer::Configuration
|
35
|
+
include Common::AMQP::Consumer::Publish
|
36
|
+
include Common::AMQP::Consumer::Subscribe
|
37
|
+
include Common::AMQP::Logger
|
38
|
+
include Common::AMQP::Callbacks
|
39
|
+
|
40
|
+
def shutdown?
|
41
|
+
Common::AMQP.shutdown?
|
42
|
+
end
|
43
|
+
|
44
|
+
def shutdown
|
45
|
+
Common::AMQP.shutdown
|
46
|
+
end
|
47
|
+
|
48
|
+
def session
|
49
|
+
Common::AMQP.session
|
50
|
+
end
|
51
|
+
|
52
|
+
def config
|
53
|
+
Common::AMQP.config
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def declare_exchange
|
59
|
+
session.declare_exchange(exchange_name, exchange_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def declare_queue
|
63
|
+
session.declare_queue(queue_name, queue_options)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Vx
|
5
|
+
module Common
|
6
|
+
module AMQP
|
7
|
+
|
8
|
+
class Formatter
|
9
|
+
|
10
|
+
@@formats = {}
|
11
|
+
|
12
|
+
class Format
|
13
|
+
|
14
|
+
attr_reader :content_type
|
15
|
+
|
16
|
+
def initialize(content_type)
|
17
|
+
@content_type = content_type
|
18
|
+
end
|
19
|
+
|
20
|
+
def pack(&block)
|
21
|
+
@pack = block if block_given?
|
22
|
+
@pack
|
23
|
+
end
|
24
|
+
|
25
|
+
def unpack(&block)
|
26
|
+
@unpack = block if block_given?
|
27
|
+
@unpack
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
|
34
|
+
def formats
|
35
|
+
@@formats
|
36
|
+
end
|
37
|
+
|
38
|
+
def define(content_type, &block)
|
39
|
+
fmt = Format.new content_type
|
40
|
+
fmt.instance_eval(&block)
|
41
|
+
formats.merge! content_type => fmt
|
42
|
+
end
|
43
|
+
|
44
|
+
def lookup(content_type)
|
45
|
+
formats[content_type]
|
46
|
+
end
|
47
|
+
|
48
|
+
def pack(content_type, body)
|
49
|
+
if fmt = lookup(content_type)
|
50
|
+
fmt.pack.call(body)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def unpack(content_type, model, body)
|
55
|
+
if fmt = lookup(content_type)
|
56
|
+
fmt.unpack.call(body, model)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
define 'text/plain' do
|
63
|
+
|
64
|
+
pack do |body|
|
65
|
+
body.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
unpack do |body, _|
|
69
|
+
body
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
define 'application/json' do
|
74
|
+
|
75
|
+
pack do |body|
|
76
|
+
body.to_json
|
77
|
+
end
|
78
|
+
|
79
|
+
unpack do |payload, model|
|
80
|
+
if model && model.respond_to?(:from_json)
|
81
|
+
model.from_json payload
|
82
|
+
else
|
83
|
+
JSON.parse(payload)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
define 'application/x-protobuf' do
|
89
|
+
|
90
|
+
pack do |object|
|
91
|
+
object.encode.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
unpack do |payload, model|
|
95
|
+
raise ModelDoesNotExists unless model
|
96
|
+
model.decode payload
|
97
|
+
end
|
98
|
+
|
99
|
+
class ModelDoesNotExists < Exception ; end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Vx
|
2
|
+
module Common
|
3
|
+
module AMQP
|
4
|
+
module Callbacks
|
5
|
+
|
6
|
+
def run_callbacks(name, *args)
|
7
|
+
before = "before_#{name}".to_sym
|
8
|
+
after = "after_#{name}".to_sym
|
9
|
+
if f = Common::AMQP.config.callbacks[before]
|
10
|
+
f.call(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
rs = yield if block_given?
|
14
|
+
|
15
|
+
if f = Common::AMQP.config.callbacks[after]
|
16
|
+
f.call(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
rs
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_on_error_callback(e)
|
23
|
+
if f = Common::AMQP.config.callbacks[:on_error]
|
24
|
+
begin
|
25
|
+
f.call e
|
26
|
+
rescue Exception => e
|
27
|
+
$stderr.puts "ERROR on error callback: #{e.inspect}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Vx
|
5
|
+
module Common
|
6
|
+
module AMQP
|
7
|
+
class Session
|
8
|
+
|
9
|
+
include Common::AMQP::Logger
|
10
|
+
|
11
|
+
CHANNEL_KEY = :vx_amqp_channel
|
12
|
+
|
13
|
+
@@session_lock = Mutex.new
|
14
|
+
|
15
|
+
attr_reader :conn
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def shutdown
|
19
|
+
@shutdown = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def shutdown?
|
23
|
+
@shutdown == true
|
24
|
+
end
|
25
|
+
|
26
|
+
def resume
|
27
|
+
@shutdown = false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
if open?
|
33
|
+
@@session_lock.synchronize do
|
34
|
+
info "closing connection"
|
35
|
+
begin
|
36
|
+
conn.close
|
37
|
+
rescue Bunny::ChannelError => e
|
38
|
+
warn e
|
39
|
+
end
|
40
|
+
info "wait..."
|
41
|
+
while conn.status != :closed
|
42
|
+
sleep 0.01
|
43
|
+
end
|
44
|
+
@conn = nil
|
45
|
+
info "connection closed"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def open
|
51
|
+
return self if open?
|
52
|
+
|
53
|
+
@@session_lock.synchronize do
|
54
|
+
self.class.resume
|
55
|
+
|
56
|
+
@conn ||= Bunny.new config.url, heartbeat: :server
|
57
|
+
|
58
|
+
unless conn.open?
|
59
|
+
info "connecting to #{conn_info}"
|
60
|
+
conn.start
|
61
|
+
info "wait connection to #{conn_info}"
|
62
|
+
while conn.connecting?
|
63
|
+
sleep 0.01
|
64
|
+
end
|
65
|
+
info "connected successfuly (#{server_name})"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def open?
|
73
|
+
conn && conn.open? && conn.status == :open
|
74
|
+
end
|
75
|
+
|
76
|
+
def declare_exchange(name, options = nil)
|
77
|
+
assert_connection_is_open
|
78
|
+
|
79
|
+
options ||= {}
|
80
|
+
name ||= config.default_exchange_name
|
81
|
+
ch = options.delete(:channel) || channel
|
82
|
+
type, opts = get_exchange_type_and_options options
|
83
|
+
ch.exchange name, opts.merge(type: type)
|
84
|
+
end
|
85
|
+
|
86
|
+
def declare_queue(name, options = nil)
|
87
|
+
assert_connection_is_open
|
88
|
+
|
89
|
+
options ||= {}
|
90
|
+
ch = options.delete(:channel) || channel
|
91
|
+
name, opts = get_queue_name_and_options(name, options)
|
92
|
+
ch.queue name, opts
|
93
|
+
end
|
94
|
+
|
95
|
+
def channel
|
96
|
+
assert_connection_is_open
|
97
|
+
|
98
|
+
Thread.current[CHANNEL_KEY] || conn.default_channel
|
99
|
+
end
|
100
|
+
|
101
|
+
def with_channel
|
102
|
+
assert_connection_is_open
|
103
|
+
|
104
|
+
old,new = nil
|
105
|
+
begin
|
106
|
+
old,new = Thread.current[CHANNEL_KEY], conn.create_channel
|
107
|
+
Thread.current[CHANNEL_KEY] = new
|
108
|
+
yield
|
109
|
+
ensure
|
110
|
+
Thread.current[CHANNEL_KEY] = old
|
111
|
+
new.close if new && new.open?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def conn_info
|
116
|
+
if conn
|
117
|
+
"#{conn.user}:#{conn.host}:#{conn.port}/#{conn.vhost}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def server_name
|
122
|
+
if conn
|
123
|
+
p = conn.server_properties || {}
|
124
|
+
"#{p["product"]}/#{p["version"]}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def config
|
129
|
+
Common::AMQP.config
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def get_exchange_type_and_options(options)
|
135
|
+
options = config.default_exchange_options.merge(options || {})
|
136
|
+
type = options.delete(:type) || config.default_exchange_type
|
137
|
+
[type, options]
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_queue_name_and_options(name, options)
|
141
|
+
name ||= AMQ::Protocol::EMPTY_STRING
|
142
|
+
[name, config.default_queue_options.merge(options || {})]
|
143
|
+
end
|
144
|
+
|
145
|
+
def assert_connection_is_open
|
146
|
+
open? || raise(ConnectionDoesNotExist.new "you need to run #{to_s}#open")
|
147
|
+
end
|
148
|
+
|
149
|
+
class ConnectionDoesNotExist < ::Exception ; end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
module Common
|
5
|
+
module AMQP
|
6
|
+
class Supervisor::Threaded
|
7
|
+
|
8
|
+
include Common::AMQP::Logger
|
9
|
+
include Common::AMQP::Callbacks
|
10
|
+
|
11
|
+
POOL_INTERVAL = 0.5
|
12
|
+
|
13
|
+
Task = Struct.new(:object, :method, :id) do
|
14
|
+
|
15
|
+
attr_accessor :thread, :attempt, :start_at
|
16
|
+
|
17
|
+
def alive?
|
18
|
+
!!(thread && thread.alive?)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
%{#<Task
|
23
|
+
object=#{object.to_s}
|
24
|
+
method=#{method.inspect}
|
25
|
+
id=#{id.inspect}
|
26
|
+
alive=#{alive?}
|
27
|
+
attempt=#{attempt}
|
28
|
+
start_at=#{start_at}> }.gsub("\n", ' ').gsub(/ +/, ' ').strip
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SpawnAttemptsLimitReached < ::Exception ; end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
|
36
|
+
@@shutdown = false
|
37
|
+
|
38
|
+
def build(tasks)
|
39
|
+
supervisor = new
|
40
|
+
tasks.each_pair do |k,v|
|
41
|
+
v.to_i.times do |n|
|
42
|
+
supervisor.add k, :subscribe, n
|
43
|
+
end
|
44
|
+
end
|
45
|
+
supervisor
|
46
|
+
end
|
47
|
+
|
48
|
+
def resume
|
49
|
+
@@shutdown = false
|
50
|
+
end
|
51
|
+
|
52
|
+
def shutdown?
|
53
|
+
@@shutdown
|
54
|
+
end
|
55
|
+
|
56
|
+
def shutdown
|
57
|
+
@@shutdown = true
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
self.class.resume
|
64
|
+
@tasks = Array.new
|
65
|
+
end
|
66
|
+
|
67
|
+
def add(object, method, id)
|
68
|
+
@tasks.push Task.new(object, method, id).freeze
|
69
|
+
end
|
70
|
+
|
71
|
+
def size
|
72
|
+
@tasks.size
|
73
|
+
end
|
74
|
+
|
75
|
+
def shutdown?
|
76
|
+
self.class.shutdown?
|
77
|
+
end
|
78
|
+
|
79
|
+
def shutdown
|
80
|
+
self.class.shutdown
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_async
|
84
|
+
Thread.new { run }.tap{|t| t.abort_on_exception = true }
|
85
|
+
end
|
86
|
+
|
87
|
+
def run
|
88
|
+
start_all_threads
|
89
|
+
|
90
|
+
loop do
|
91
|
+
task = @tasks.shift
|
92
|
+
break unless task
|
93
|
+
|
94
|
+
case
|
95
|
+
when shutdown?
|
96
|
+
log_thread_error task
|
97
|
+
when task.alive?
|
98
|
+
@tasks.push task
|
99
|
+
else
|
100
|
+
process_fail task
|
101
|
+
end
|
102
|
+
|
103
|
+
sleep POOL_INTERVAL unless shutdown?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def process_fail(task)
|
110
|
+
log_thread_error task
|
111
|
+
if check_attempt task
|
112
|
+
@tasks.push create_thread(task, task.attempt + 1)
|
113
|
+
else
|
114
|
+
raise SpawnAttemptsLimitReached
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def start_all_threads
|
119
|
+
started_tasks = Array.new
|
120
|
+
while task = @tasks.shift
|
121
|
+
started_tasks.push create_thread(task, 0)
|
122
|
+
end
|
123
|
+
while task = started_tasks.shift
|
124
|
+
@tasks.push task
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_thread(task, attempt)
|
129
|
+
attempt = 0 if reset_attempt?(task)
|
130
|
+
task.dup.tap do |new_task|
|
131
|
+
new_task.thread = Thread.new(new_task) do |t|
|
132
|
+
Thread.current[:vx_amqp_consumer_id] = t.id
|
133
|
+
t.object.send t.method
|
134
|
+
end
|
135
|
+
new_task.thread.abort_on_exception = false
|
136
|
+
new_task.attempt = attempt
|
137
|
+
new_task.start_at = Time.now
|
138
|
+
new_task.freeze
|
139
|
+
debug "spawn #{new_task.inspect}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def log_thread_error(task)
|
144
|
+
return unless task.thread
|
145
|
+
|
146
|
+
begin
|
147
|
+
task.thread.value
|
148
|
+
nil
|
149
|
+
rescue Exception => e
|
150
|
+
STDERR.puts "#{e.inspect} in #{task.inspect}"
|
151
|
+
STDERR.puts e.backtrace.join("\n")
|
152
|
+
run_on_error_callback(e)
|
153
|
+
e
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def reset_attempt?(task)
|
158
|
+
return true unless task.start_at
|
159
|
+
|
160
|
+
interval = 60
|
161
|
+
(task.start_at + interval) < Time.now
|
162
|
+
end
|
163
|
+
|
164
|
+
def check_attempt(task)
|
165
|
+
task.attempt.to_i <= Common::AMQP.config.spawn_attempts.to_i
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path("../../amqp", __FILE__)
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
module Common
|
5
|
+
module AMQP
|
6
|
+
module Testing
|
7
|
+
|
8
|
+
extend self
|
9
|
+
|
10
|
+
@@messages = Hash.new { |h,k| h[k] = [] }
|
11
|
+
@@messages_and_options = Hash.new { |h,k| h[k] = [] }
|
12
|
+
|
13
|
+
def messages
|
14
|
+
@@messages
|
15
|
+
end
|
16
|
+
|
17
|
+
def messages_and_options
|
18
|
+
@@messages_and_options
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear
|
22
|
+
messages.clear
|
23
|
+
messages_and_options.clear
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Consumer::Publish
|
28
|
+
alias_method :real_publish, :publish
|
29
|
+
|
30
|
+
def publish(message, options = nil)
|
31
|
+
options ||= {}
|
32
|
+
Testing.messages[exchange_name] << message
|
33
|
+
Testing.messages_and_options[exchange_name] << [message, options]
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
module Consumer
|
40
|
+
module ClassMethods
|
41
|
+
|
42
|
+
def messages
|
43
|
+
Testing.messages[exchange_name]
|
44
|
+
end
|
45
|
+
|
46
|
+
def messages_and_options
|
47
|
+
Testing.messages_and_options[exchange_name]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|