queuel 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.cane +3 -0
- data/.gitignore +1 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile.lock +112 -0
- data/Guardfile +22 -0
- data/README.md +17 -11
- data/Rakefile +7 -0
- data/lib/queuel.rb +50 -27
- data/lib/queuel/base/engine.rb +35 -0
- data/lib/queuel/base/message.rb +32 -0
- data/lib/queuel/base/poller.rb +161 -0
- data/lib/queuel/base/queue.rb +59 -0
- data/lib/queuel/client.rb +15 -3
- data/lib/queuel/configurator.rb +65 -0
- data/lib/queuel/introspect.rb +11 -0
- data/lib/queuel/iron_mq/engine.rb +11 -17
- data/lib/queuel/iron_mq/message.rb +5 -26
- data/lib/queuel/iron_mq/poller.rb +8 -111
- data/lib/queuel/iron_mq/queue.rb +13 -24
- data/lib/queuel/null/engine.rb +3 -7
- data/lib/queuel/null/message.rb +1 -16
- data/lib/queuel/null/poller.rb +2 -114
- data/lib/queuel/null/queue.rb +6 -9
- data/lib/queuel/version.rb +1 -1
- data/queuel.gemspec +10 -0
- data/spec/lib/queuel/base/queue_spec.rb +23 -0
- data/spec/lib/queuel/client_spec.rb +30 -0
- data/spec/lib/queuel/configurator_spec.rb +32 -0
- data/spec/lib/queuel/iron_mq/engine_spec.rb +28 -0
- data/spec/lib/queuel/iron_mq/message_spec.rb +2 -2
- data/spec/lib/queuel/iron_mq/poller_spec.rb +15 -1
- data/spec/lib/queuel/iron_mq/queue_spec.rb +2 -2
- data/spec/lib/queuel_spec.rb +12 -7
- data/spec/spec_helper.rb +4 -0
- data/spec/support/engine_shared_example.rb +4 -2
- data/spec/support/message_shared_example.rb +2 -11
- data/spec/support/poller_shared_example.rb +23 -57
- data/spec/support/queue_shared_example.rb +14 -1
- metadata +163 -4
@@ -0,0 +1,59 @@
|
|
1
|
+
module Queuel
|
2
|
+
module Base
|
3
|
+
class Queue
|
4
|
+
extend Introspect
|
5
|
+
|
6
|
+
def initialize(client, queue_name)
|
7
|
+
self.client = client
|
8
|
+
self.name = queue_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def peek(options = {})
|
12
|
+
raise NotImplementedError, "must implement #peek"
|
13
|
+
end
|
14
|
+
|
15
|
+
def push(message)
|
16
|
+
raise NotImplementedError, "must implement #push"
|
17
|
+
end
|
18
|
+
|
19
|
+
def pop(options = {}, &block)
|
20
|
+
bare_message = pop_bare_message(options)
|
21
|
+
unless bare_message.nil?
|
22
|
+
build_new_message(bare_message).tap { |message|
|
23
|
+
if block_given? && message.present?
|
24
|
+
message.delete if yield(message)
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def receive(options = {}, &block)
|
31
|
+
poller_klass.new(self, block, options, thread_count).poll
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
attr_accessor :client
|
36
|
+
attr_accessor :name
|
37
|
+
|
38
|
+
def thread_count
|
39
|
+
Queuel.receiver_threads || 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def pop_bare_message(options = {})
|
43
|
+
raise NotImplementedError, "must implement bare Message getter"
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_new_message(bare_message)
|
47
|
+
message_klass.new(bare_message)
|
48
|
+
end
|
49
|
+
|
50
|
+
def message_klass
|
51
|
+
self.class.const_with_nesting("Message")
|
52
|
+
end
|
53
|
+
|
54
|
+
def poller_klass
|
55
|
+
self.class.const_with_nesting("Poller")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/queuel/client.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
module Queuel
|
2
|
+
NoQueueGivenError = Class.new StandardError
|
2
3
|
class Client
|
3
|
-
extend Forwardable
|
4
|
-
def_delegators :queue_connection, :push, :pop, :receive
|
5
|
-
|
6
4
|
def initialize(engine, credentials, init_queue = nil)
|
7
5
|
self.engine = engine
|
8
6
|
self.credentials = credentials
|
9
7
|
self.given_queue = init_queue
|
10
8
|
end
|
11
9
|
|
10
|
+
[:push, :pop, :receive].each do |operation|
|
11
|
+
define_method(operation) do |*args, &block|
|
12
|
+
with_queue { queue_connection.public_send(operation, *args, &block) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
12
16
|
def with(change_queue = nil)
|
13
17
|
self.clone.tap { |client| client.given_queue = change_queue }
|
14
18
|
end
|
@@ -23,6 +27,14 @@ module Queuel
|
|
23
27
|
|
24
28
|
private
|
25
29
|
|
30
|
+
def with_queue
|
31
|
+
if queue.nil? || queue.to_s.strip.empty?
|
32
|
+
raise NoQueueGivenError, "Must select a queue with #with or set a default_queue"
|
33
|
+
else
|
34
|
+
yield
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
26
38
|
def queue_connection
|
27
39
|
engine_client.queue queue
|
28
40
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "mono_logger"
|
2
|
+
module Queuel
|
3
|
+
class Configurator
|
4
|
+
InvalidConfigurationError = Class.new StandardError
|
5
|
+
private
|
6
|
+
attr_accessor :option_values
|
7
|
+
|
8
|
+
def self.option_values
|
9
|
+
@option_values ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.define_param_accessors(param_name)
|
13
|
+
define_method param_name do |*values|
|
14
|
+
value = values.first
|
15
|
+
value ? self.send("#{param_name}=", value) : retrieve(param_name)
|
16
|
+
end
|
17
|
+
define_method "#{param_name}=" do |value|
|
18
|
+
validate!(param_name, value) &&
|
19
|
+
instance_variable_set("@#{param_name}", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate(param_name, value)
|
24
|
+
validator = self.class.option_values[param_name].fetch(:validate) { {} }[:validator] || ->(val) { true }
|
25
|
+
validator.call value
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate!(param_name, value)
|
29
|
+
message = self.class.option_values[param_name].fetch(:validate) { {} }[:message]
|
30
|
+
message ||= "#{value} is not a valid value for #{param_name}"
|
31
|
+
validate(param_name, value) || raise(InvalidConfigurationError, message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def retrieve(param)
|
35
|
+
if instance_variable_defined?("@#{param}")
|
36
|
+
instance_variable_get("@#{param}")
|
37
|
+
else
|
38
|
+
self.class.option_values[param][:default]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.param(param_name, options = {})
|
43
|
+
attr_accessor param_name
|
44
|
+
self.option_values[param_name] = options
|
45
|
+
define_param_accessors param_name
|
46
|
+
public param_name
|
47
|
+
public "#{param_name}="
|
48
|
+
end
|
49
|
+
|
50
|
+
public
|
51
|
+
|
52
|
+
param :credentials
|
53
|
+
param :engine
|
54
|
+
param :default_queue
|
55
|
+
param :receiver_threads, default: 1
|
56
|
+
param :logger, default: MonoLogger.new(STDOUT), validate: {
|
57
|
+
message: "Logger must respond to #{%w[info warn debug level level]}",
|
58
|
+
validator: ->(logger) {
|
59
|
+
%w[info warn debug level].all? { |msg| logger.respond_to? msg } &&
|
60
|
+
logger.respond_to?(:level)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
param :log_level, default: MonoLogger::ERROR
|
64
|
+
end
|
65
|
+
end
|
@@ -1,40 +1,34 @@
|
|
1
|
+
require "forwardable"
|
1
2
|
module Queuel
|
2
3
|
module IronMq
|
3
|
-
class Engine
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(credentials = {})
|
7
|
-
self.credentials = credentials
|
8
|
-
self.memoized_queues = {}
|
9
|
-
end
|
4
|
+
class Engine < Base::Engine
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :Queuel, :logger
|
10
7
|
|
11
|
-
|
12
|
-
memoized_queues[which_queue.to_s] ||= Queue.new(client, which_queue)
|
13
|
-
end
|
8
|
+
IronMqMissingError = Class.new(StandardError)
|
14
9
|
|
15
10
|
private
|
16
|
-
attr_accessor :credentials
|
17
|
-
attr_accessor :memoized_queues
|
18
|
-
|
19
|
-
def client
|
20
|
-
@client ||= client_proper.new credentials
|
21
|
-
end
|
22
11
|
|
23
12
|
def try_typhoeus
|
24
13
|
require 'typhoeus'
|
14
|
+
true
|
25
15
|
rescue LoadError
|
16
|
+
logger.warn "Typhoeus not found..."
|
17
|
+
logger.warn "Typhoeus is recommended for IronMQ"
|
26
18
|
false
|
27
19
|
end
|
28
20
|
|
29
|
-
def
|
21
|
+
def client_klass
|
30
22
|
if defined?(::IronMQ::Client)
|
31
23
|
try_typhoeus
|
32
24
|
::IronMQ::Client
|
33
25
|
else
|
34
26
|
begin
|
27
|
+
logger.info "Loading IronMQ..."
|
35
28
|
require 'iron_mq'
|
36
29
|
::IronMQ::Client
|
37
30
|
rescue LoadError
|
31
|
+
logger.error "Couldn't find iron_mq gem"
|
38
32
|
raise(IronMqMissingError)
|
39
33
|
end
|
40
34
|
end
|
@@ -1,43 +1,22 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
module Queuel
|
3
3
|
module IronMq
|
4
|
-
class Message
|
4
|
+
class Message < Base::Message
|
5
5
|
extend Forwardable
|
6
|
-
def_delegators :message_object, :delete
|
7
6
|
|
8
|
-
def
|
9
|
-
|
10
|
-
instance.send :initialize_from_iron_mq_object, message_object
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize_from_iron_mq_object(message_object)
|
15
|
-
self.message_object = message_object
|
16
|
-
end
|
17
|
-
private :initialize_from_iron_mq_object
|
18
|
-
|
19
|
-
def initialize(id, body, queue = nil)
|
20
|
-
self.id = id
|
21
|
-
self.body = body
|
22
|
-
self.queue = queue
|
7
|
+
def body
|
8
|
+
@body || message_object && message_object.body
|
23
9
|
end
|
24
10
|
|
25
|
-
def
|
26
|
-
|
11
|
+
def delete
|
12
|
+
message_object.delete
|
27
13
|
end
|
28
14
|
|
29
15
|
[:id, :queue].each do |delegate|
|
30
16
|
define_method(delegate) do
|
31
17
|
instance_variable_get("@#{delegate}") || message_object && message_object.public_send(delegate)
|
32
18
|
end
|
33
|
-
|
34
|
-
private
|
35
|
-
attr_writer delegate
|
36
19
|
end
|
37
|
-
|
38
|
-
private
|
39
|
-
attr_accessor :message_object
|
40
|
-
attr_writer :body
|
41
20
|
end
|
42
21
|
end
|
43
22
|
end
|
@@ -1,123 +1,20 @@
|
|
1
|
-
require 'timeout'
|
2
1
|
module Queuel
|
3
2
|
module IronMq
|
4
|
-
class Poller
|
5
|
-
|
6
|
-
self.queue = queue
|
7
|
-
self.options = options || {}
|
8
|
-
self.block = block
|
9
|
-
self.tries = 0
|
10
|
-
self.continue_looping = true
|
11
|
-
end
|
12
|
-
|
13
|
-
def poll
|
14
|
-
choose_looper do |msg|
|
15
|
-
if msg.nil?
|
16
|
-
tried
|
17
|
-
quit_looping! if break_if_nil? || maxed_tried?
|
18
|
-
sleep(sleep_time)
|
19
|
-
else
|
20
|
-
reset_tries
|
21
|
-
block.call msg
|
22
|
-
msg.delete
|
23
|
-
end
|
24
|
-
!msg.nil?
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
protected
|
29
|
-
attr_accessor :tries
|
30
|
-
|
3
|
+
class Poller < Base::Poller
|
4
|
+
# Public: poll
|
31
5
|
private
|
32
|
-
attr_accessor :queue
|
33
|
-
attr_accessor :args
|
34
|
-
attr_accessor :options
|
35
|
-
attr_accessor :block
|
36
|
-
attr_accessor :continue_looping
|
37
|
-
|
38
|
-
def choose_looper(&loop_block)
|
39
|
-
timeout? ? timeout_looper(loop_block) : looper(loop_block)
|
40
|
-
end
|
41
|
-
|
42
|
-
def timeout_looper(loop_block)
|
43
|
-
Timeout.timeout(timeout) { looper(loop_block) }
|
44
|
-
rescue Timeout::Error
|
45
|
-
false
|
46
|
-
end
|
47
|
-
|
48
|
-
def looper(loop_block)
|
49
|
-
while continue_looping? do
|
50
|
-
loop_block.call(pop_new_message)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def continue_looping?
|
55
|
-
!!continue_looping
|
56
|
-
end
|
57
|
-
|
58
|
-
def quit_looping!
|
59
|
-
self.continue_looping = false
|
60
|
-
end
|
61
|
-
|
62
|
-
def timeout
|
63
|
-
options[:poll_timeout].to_i
|
64
|
-
end
|
65
|
-
|
66
|
-
def timeout?
|
67
|
-
timeout > 0
|
68
|
-
end
|
69
|
-
|
70
|
-
def pop_new_message
|
71
|
-
queue.pop built_options
|
72
|
-
end
|
73
|
-
|
74
|
-
def start_sleep_time
|
75
|
-
0.1
|
76
|
-
end
|
77
|
-
|
78
|
-
def sleep_time
|
79
|
-
tries < 30 ? (start_sleep_time * tries) : 3
|
80
|
-
end
|
81
|
-
|
82
|
-
def reset_tries
|
83
|
-
self.tries = 0
|
84
|
-
end
|
85
|
-
|
86
|
-
def maxed_tried?
|
87
|
-
tries >= max_fails if max_fails_given?
|
88
|
-
end
|
89
|
-
|
90
|
-
def max_fails_given?
|
91
|
-
max_fails > 0
|
92
|
-
end
|
93
|
-
|
94
|
-
def max_fails
|
95
|
-
options[:max_consecutive_fails].to_i
|
96
|
-
end
|
97
|
-
|
98
|
-
def tried
|
99
|
-
self.tries += 1
|
100
|
-
end
|
101
|
-
|
102
|
-
def break_if_nil?
|
103
|
-
!!options.fetch(:break_if_nil, false)
|
104
|
-
end
|
105
|
-
|
106
|
-
def option_keys
|
107
|
-
%w[break_if_nil poll_timeout max_consecutive_fails]
|
108
|
-
end
|
109
|
-
|
110
|
-
def my_options
|
111
|
-
options.select { |key,_| option_keys.include? key.to_s }
|
112
|
-
end
|
113
6
|
|
114
7
|
def built_options
|
115
|
-
options.merge
|
8
|
+
options.merge default_options # intentional direction, force defaults
|
116
9
|
end
|
117
10
|
|
118
|
-
def
|
11
|
+
def default_options
|
119
12
|
{ n: 1 }
|
120
13
|
end
|
14
|
+
|
15
|
+
def peek_options
|
16
|
+
{ n: self.workers }
|
17
|
+
end
|
121
18
|
end
|
122
19
|
end
|
123
20
|
end
|
data/lib/queuel/iron_mq/queue.rb
CHANGED
@@ -1,36 +1,25 @@
|
|
1
1
|
require 'queuel/iron_mq/poller'
|
2
|
+
require 'queuel/base/queue'
|
3
|
+
require 'forwardable'
|
2
4
|
module Queuel
|
3
5
|
module IronMq
|
4
|
-
class Queue
|
5
|
-
|
6
|
-
|
7
|
-
self.name = queue_name
|
8
|
-
end
|
9
|
-
|
10
|
-
# For IronMQ it should just be (message)
|
11
|
-
def push(*args)
|
12
|
-
queue_connection.post *args
|
13
|
-
end
|
6
|
+
class Queue < Base::Queue
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :queue_connection, :peek
|
14
9
|
|
15
|
-
def
|
16
|
-
|
17
|
-
unless bare_message.nil?
|
18
|
-
Message.new_from_iron_mq_object(bare_message).tap { |message|
|
19
|
-
if block_given? && !message.nil?
|
20
|
-
yield message
|
21
|
-
message.delete
|
22
|
-
end
|
23
|
-
}
|
24
|
-
end
|
10
|
+
def peek(options = {})
|
11
|
+
Array(queue_connection.peek(options))
|
25
12
|
end
|
26
13
|
|
27
|
-
|
28
|
-
|
14
|
+
# For IronMQ it should just be (message)
|
15
|
+
def push(message)
|
16
|
+
queue_connection.post message
|
29
17
|
end
|
30
18
|
|
31
19
|
private
|
32
|
-
|
33
|
-
|
20
|
+
def pop_bare_message(options = {})
|
21
|
+
queue_connection.get options
|
22
|
+
end
|
34
23
|
|
35
24
|
def queue_connection
|
36
25
|
@queue_connection ||= client.queue(name)
|