queuel 0.0.1 → 0.1.0
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.
- 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)
|