queuel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.irbrc ADDED
@@ -0,0 +1,2 @@
1
+ $:<< "./lib"
2
+ require 'queuel'
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p392@queuel"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.20.4 (stable)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ for __hook in "${rvm_path:-$HOME/.rvm}/hooks/after_use"*
27
+ do
28
+ if [[ -f "${__hook}" && -x "${__hook}" && -s "${__hook}" ]]
29
+ then \. "${__hook}" || true
30
+ fi
31
+ done
32
+ unset __hook
33
+ if (( ${rvm_use_flag:=1} >= 2 )) # display only when forced
34
+ then
35
+ if [[ $- == *i* ]] # check for interactive shells
36
+ then echo "Using: \E[32m$GEM_HOME\E[0m" # show the user the ruby and gemset they are using in green
37
+ else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
38
+ fi
39
+ fi
40
+ else
41
+ # If the environment file has not yet been created, use the RVM CLI to select.
42
+ rvm --create "$environment_id" || {
43
+ echo "Failed to create RVM environment '${environment_id}'."
44
+ return 1
45
+ }
46
+ fi
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - jruby-19mode
6
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in queuel.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jon Phenow
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Queuel
2
+ [![Build Status](https://travis-ci.org/sportngin/queuel.png?branch=master)](https://travis-ci.org/sportngin/queuel)
3
+
4
+ Queuel is a kewl, lite wrapper around Queue interfaces. Currently it implements:
5
+
6
+ * IronMQ
7
+ * Null pattern
8
+
9
+ Each of these should reliably implement:
10
+
11
+ * `push`
12
+ * `pop`
13
+ * `receive`
14
+
15
+ Along with some further conveniences.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile as well as the proper Gem for
20
+ your queuing:
21
+
22
+ ```ruby
23
+ gem 'iron_mq'
24
+ # IronMQ recommends `gem "typhoeus"` as well for some speed benefits
25
+ gem 'queuel'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ You will then want to configure:
33
+
34
+ ```ruby
35
+ Queuel.configure do
36
+ # Optional, but a queue must be selected before running put/pop/receive
37
+ default_queue :venues
38
+
39
+ # requirement depends on your Queue
40
+ credentials token: 'asdufasdf8a7sd8fa7sdf', project_id: 'project_id'
41
+
42
+ # currently only [:iron_mq, :null] available
43
+ engine :iron_mq
44
+ end
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### General Queue API
50
+
51
+ ```ruby
52
+ # Using default Queue
53
+ Queuel.pop
54
+ Queuel.push "My message to you"
55
+ Queuel.receive do |message|
56
+ puts "I received #{message.body}" # NOTE the message interface may change, this is currently not wrapped by the gem
57
+ end
58
+
59
+ # With the non-default queue
60
+ Queuel.with("officials").pop
61
+ Queuel.with("officials").push "My message to you"
62
+ Queuel.with("officials").receive do |message|
63
+ puts "I received #{message.body}" # NOTE the message interface may change, this is currently not wrapped by the gem
64
+ end
65
+
66
+ # Timeout the receiving
67
+ Queuel.receive poll_timeout: 60 do |message|
68
+ puts "I received #{message.body}" # NOTE the message interface may change, this is currently not wrapped by the gem
69
+ end
70
+
71
+ # Don't receive more than 10 nil messages
72
+ Queuel.receive max_consecutive_fails: 10 do |message|
73
+ puts "I received #{message.body}" # NOTE the message interface may change, this is currently not wrapped by the gem
74
+ end
75
+
76
+ # Break on nil
77
+ Queuel.receive break_if_nil: true do |message|
78
+ puts "I received #{message.body}" # NOTE the message interface may change, this is currently not wrapped by the gem
79
+ end
80
+ ```
81
+
82
+ ### The message
83
+
84
+ ```ruby
85
+ message.id # => ID of the message
86
+ message.body # => Message body
87
+ message.delete # => Delete the message
88
+ ```
89
+
90
+ ## TODO
91
+
92
+ * Implement AMQP
93
+ * Configureable exponential back-off on `receive`
94
+
95
+ ## Contributing
96
+
97
+ 1. Fork it
98
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
99
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
100
+ 4. Push to the branch (`git push origin my-new-feature`)
101
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/queuel.rb ADDED
@@ -0,0 +1,68 @@
1
+ require "queuel/version"
2
+ require "forwardable"
3
+ require "queuel/null/message"
4
+ require "queuel/null/engine"
5
+ require "queuel/null/poller"
6
+ require "queuel/null/queue"
7
+ require "queuel/iron_mq/message"
8
+ require "queuel/iron_mq/engine"
9
+ require "queuel/iron_mq/poller"
10
+ require "queuel/iron_mq/queue"
11
+ require "queuel/client"
12
+
13
+ module Queuel
14
+ class << self
15
+ extend Forwardable
16
+ def_delegators :client, :push, :pop, :receive, :with
17
+ def_delegators :config, :credentials, :default_queue
18
+ alias << pop
19
+ end
20
+
21
+ def self.engine
22
+ requires
23
+ Object.module_eval("::#{const_name}", __FILE__, __LINE__)
24
+ end
25
+
26
+ def self.configure(&block)
27
+ config.instance_eval &block
28
+ end
29
+
30
+ def self.config
31
+ @config ||= Configurator.new
32
+ end
33
+
34
+ def self.client
35
+ Client.new engine, credentials
36
+ end
37
+
38
+ def self.engines
39
+ {
40
+ iron_mq: { require: 'iron_mq', const: "IronMq" },
41
+ null: { const: "Null" }
42
+ }
43
+ end
44
+
45
+ def self.requires
46
+ require engines[config.engine][:require] if engines.fetch(config.engine, {})[:require]
47
+ end
48
+
49
+ def self.const_name
50
+ "Queuel::#{engines.fetch(config.engine, {}).fetch(:const, nil) || engines[:null][:const]}::Engine"
51
+ end
52
+
53
+ class Configurator
54
+ def self.param(*params)
55
+ params.each do |name|
56
+ attr_accessor name
57
+ define_method name do |*values|
58
+ value = values.first
59
+ value ? self.send("#{name}=", value) : instance_variable_get("@#{name}")
60
+ end
61
+ end
62
+ end
63
+
64
+ param :credentials
65
+ param :engine
66
+ param :default_queue
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ module Queuel
2
+ class Client
3
+ extend Forwardable
4
+ def_delegators :queue_connection, :push, :pop, :receive
5
+
6
+ def initialize(engine, credentials, init_queue = nil)
7
+ self.engine = engine
8
+ self.credentials = credentials
9
+ self.given_queue = init_queue
10
+ end
11
+
12
+ def with(change_queue = nil)
13
+ self.clone.tap { |client| client.given_queue = change_queue }
14
+ end
15
+
16
+ def queue
17
+ bare = (given_queue || Queuel.default_queue)
18
+ bare.to_s unless bare.nil?
19
+ end
20
+
21
+ protected
22
+ attr_accessor :given_queue
23
+
24
+ private
25
+
26
+ def queue_connection
27
+ engine_client.queue queue
28
+ end
29
+
30
+ def engine_client
31
+ @engine_client ||= engine.new credentials
32
+ end
33
+
34
+ attr_accessor :credentials
35
+ attr_accessor :engine
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ module Queuel
2
+ 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
10
+
11
+ def queue(which_queue)
12
+ memoized_queues[which_queue.to_s] ||= Queue.new(client, which_queue)
13
+ end
14
+
15
+ private
16
+ attr_accessor :credentials
17
+ attr_accessor :memoized_queues
18
+
19
+ def client
20
+ @client ||= client_proper.new credentials
21
+ end
22
+
23
+ def try_typhoeus
24
+ require 'typhoeus'
25
+ rescue LoadError
26
+ false
27
+ end
28
+
29
+ def client_proper
30
+ if defined?(::IronMQ::Client)
31
+ try_typhoeus
32
+ ::IronMQ::Client
33
+ else
34
+ begin
35
+ require 'iron_mq'
36
+ ::IronMQ::Client
37
+ rescue LoadError
38
+ raise(IronMqMissingError)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ require 'forwardable'
2
+ module Queuel
3
+ module IronMq
4
+ class Message
5
+ extend Forwardable
6
+ def_delegators :message_object, :delete
7
+
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
23
+ end
24
+
25
+ def body
26
+ @body || message_object && message_object.msg
27
+ end
28
+
29
+ [:id, :queue].each do |delegate|
30
+ define_method(delegate) do
31
+ instance_variable_get("@#{delegate}") || message_object && message_object.public_send(delegate)
32
+ end
33
+
34
+ private
35
+ attr_writer delegate
36
+ end
37
+
38
+ private
39
+ attr_accessor :message_object
40
+ attr_writer :body
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,123 @@
1
+ require 'timeout'
2
+ module Queuel
3
+ 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
+
31
+ 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
+
114
+ def built_options
115
+ options.merge default_args # intentional direction, force defaults
116
+ end
117
+
118
+ def default_args
119
+ { n: 1 }
120
+ end
121
+ end
122
+ end
123
+ end