queuel 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.cane +3 -0
  2. data/.gitignore +1 -1
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile.lock +112 -0
  6. data/Guardfile +22 -0
  7. data/README.md +17 -11
  8. data/Rakefile +7 -0
  9. data/lib/queuel.rb +50 -27
  10. data/lib/queuel/base/engine.rb +35 -0
  11. data/lib/queuel/base/message.rb +32 -0
  12. data/lib/queuel/base/poller.rb +161 -0
  13. data/lib/queuel/base/queue.rb +59 -0
  14. data/lib/queuel/client.rb +15 -3
  15. data/lib/queuel/configurator.rb +65 -0
  16. data/lib/queuel/introspect.rb +11 -0
  17. data/lib/queuel/iron_mq/engine.rb +11 -17
  18. data/lib/queuel/iron_mq/message.rb +5 -26
  19. data/lib/queuel/iron_mq/poller.rb +8 -111
  20. data/lib/queuel/iron_mq/queue.rb +13 -24
  21. data/lib/queuel/null/engine.rb +3 -7
  22. data/lib/queuel/null/message.rb +1 -16
  23. data/lib/queuel/null/poller.rb +2 -114
  24. data/lib/queuel/null/queue.rb +6 -9
  25. data/lib/queuel/version.rb +1 -1
  26. data/queuel.gemspec +10 -0
  27. data/spec/lib/queuel/base/queue_spec.rb +23 -0
  28. data/spec/lib/queuel/client_spec.rb +30 -0
  29. data/spec/lib/queuel/configurator_spec.rb +32 -0
  30. data/spec/lib/queuel/iron_mq/engine_spec.rb +28 -0
  31. data/spec/lib/queuel/iron_mq/message_spec.rb +2 -2
  32. data/spec/lib/queuel/iron_mq/poller_spec.rb +15 -1
  33. data/spec/lib/queuel/iron_mq/queue_spec.rb +2 -2
  34. data/spec/lib/queuel_spec.rb +12 -7
  35. data/spec/spec_helper.rb +4 -0
  36. data/spec/support/engine_shared_example.rb +4 -2
  37. data/spec/support/message_shared_example.rb +2 -11
  38. data/spec/support/poller_shared_example.rb +23 -57
  39. data/spec/support/queue_shared_example.rb +14 -1
  40. 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
@@ -0,0 +1,11 @@
1
+ module Queuel
2
+ module Introspect
3
+ def module_names
4
+ self.to_s.split("::")[0..-2].join("::")
5
+ end
6
+
7
+ def const_with_nesting(other_name)
8
+ Object.module_eval("#{module_names}::#{other_name}", __FILE__, __LINE__)
9
+ end
10
+ end
11
+ end
@@ -1,40 +1,34 @@
1
+ require "forwardable"
1
2
  module Queuel
2
3
  module IronMq
3
- class Engine
4
- IronMqMissingError = Class.new(StandardError)
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
- def queue(which_queue)
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 client_proper
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 self.new_from_iron_mq_object(message_object)
9
- allocate.tap { |instance|
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 body
26
- @body || message_object && message_object.msg
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
- def initialize(queue, options, block)
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 default_args # intentional direction, force defaults
8
+ options.merge default_options # intentional direction, force defaults
116
9
  end
117
10
 
118
- def default_args
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
@@ -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
- def initialize(client, queue_name)
6
- self.client = client
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 pop(*args, &block)
16
- bare_message = queue_connection.get(*args)
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
- def receive(options = {}, &block)
28
- Poller.new(self, options, block).poll
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
- attr_accessor :client
33
- attr_accessor :name
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)