queue_kit 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/README.md +21 -0
- data/Rakefile +10 -0
- data/lib/queue_kit/clients/command_timeout.rb +43 -0
- data/lib/queue_kit/clients/round_robin_shuffler.rb +67 -0
- data/lib/queue_kit/signal_checker.rb +50 -0
- data/lib/queue_kit/signal_handlers/graceful_quit.rb +14 -0
- data/lib/queue_kit/worker.rb +115 -0
- data/lib/queue_kit.rb +17 -0
- data/queue_kit.gemspec +31 -0
- data/script/bootstrap +6 -0
- data/script/console +7 -0
- data/script/package +8 -0
- data/script/release +18 -0
- data/script/test +6 -0
- data/test/command_timeout_test.rb +39 -0
- data/test/helper.rb +4 -0
- data/test/round_robin_shuffler_test.rb +79 -0
- data/test/worker_test.rb +81 -0
- metadata +68 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# QueueKit
|
2
|
+
|
3
|
+
A set of classes for building a process that pops jobs off a queue. Provides
|
4
|
+
assistance with loops, signal handling, round robin client shuffling, etc.
|
5
|
+
|
6
|
+
Sing this to the tune of "Push It" by Salt-n-Pepa
|
7
|
+
|
8
|
+
Note: This is still considered alpha until QueueKit v0.1.0.
|
9
|
+
|
10
|
+
## TODO?
|
11
|
+
|
12
|
+
* Forked processes
|
13
|
+
* Pausing/resuming workers
|
14
|
+
* More nunes.
|
15
|
+
|
16
|
+
## Shout outs
|
17
|
+
|
18
|
+
* Resque
|
19
|
+
* John Nunemaker
|
20
|
+
* Eric Lindvall
|
21
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module QueueKit
|
2
|
+
module Clients
|
3
|
+
module CommandTimeout
|
4
|
+
def self.with_ivars(klass)
|
5
|
+
mod = self
|
6
|
+
klass.class_eval do
|
7
|
+
include mod
|
8
|
+
attr_accessor :command_timeout_ms
|
9
|
+
attr_accessor :max_command_timeout_ms
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def command_timeout(attempts = 0)
|
14
|
+
timeout = command_timeout_ms
|
15
|
+
timeout += timeout * (attempts / command_clients_size).floor
|
16
|
+
|
17
|
+
if timeout > (max = max_command_timeout_ms)
|
18
|
+
timeout = max
|
19
|
+
end
|
20
|
+
|
21
|
+
timeout
|
22
|
+
end
|
23
|
+
|
24
|
+
def command_timeout_from(options)
|
25
|
+
@command_timeout_ms = options[:command_timeout_ms] || 10
|
26
|
+
@max_command_timeout_ms = options[:max_command_timeout_ms] || 1000
|
27
|
+
end
|
28
|
+
|
29
|
+
def command_timeout_ms
|
30
|
+
10
|
31
|
+
end
|
32
|
+
|
33
|
+
def max_command_timeout_ms
|
34
|
+
1000
|
35
|
+
end
|
36
|
+
|
37
|
+
def command_clients_size
|
38
|
+
1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module QueueKit
|
2
|
+
module Clients
|
3
|
+
module RoundRobinShuffler
|
4
|
+
def self.with_ivars(klass)
|
5
|
+
mod = self
|
6
|
+
klass.class_eval do
|
7
|
+
include mod
|
8
|
+
attr_accessor :commands_per_client
|
9
|
+
|
10
|
+
def command_clients_size
|
11
|
+
@clients.size
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def client_command_with_retries(retries = 10)
|
17
|
+
attempts = 0
|
18
|
+
|
19
|
+
while attempts < retries
|
20
|
+
if data = (yield client, attempts)
|
21
|
+
return data
|
22
|
+
end
|
23
|
+
|
24
|
+
rotate_client
|
25
|
+
attempts += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def client
|
32
|
+
@client_command_count += 1
|
33
|
+
|
34
|
+
if @client_command_count > commands_per_client
|
35
|
+
rotate_client
|
36
|
+
end
|
37
|
+
|
38
|
+
clients[@client_index]
|
39
|
+
end
|
40
|
+
|
41
|
+
def round_robin_from(options)
|
42
|
+
@commands_per_client = options[:commands_per_client] || 100
|
43
|
+
end
|
44
|
+
|
45
|
+
def rotate_client
|
46
|
+
@client_index ||= -1
|
47
|
+
@client_len ||= clients.size
|
48
|
+
|
49
|
+
@client_command_count = 0
|
50
|
+
@client_index += 1
|
51
|
+
|
52
|
+
if @client_index >= @client_len
|
53
|
+
@client_index = 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def commands_per_client
|
58
|
+
100
|
59
|
+
end
|
60
|
+
|
61
|
+
def clients
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module QueueKit
|
2
|
+
class SignalChecker
|
3
|
+
COMMON_SIGNALS = %w(TERM INT)
|
4
|
+
JRUBY_SIGNALS = %w(QUIT USR1)
|
5
|
+
OPTIONAL_SIGNALS = %w(USR2 CONT HUP)
|
6
|
+
|
7
|
+
attr_reader :worker
|
8
|
+
attr_reader :handler
|
9
|
+
|
10
|
+
def self.trap(worker, handler)
|
11
|
+
new(worker, handler).trap_signals
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(worker, handler)
|
15
|
+
@worker = worker
|
16
|
+
@handler = handler
|
17
|
+
end
|
18
|
+
|
19
|
+
def trap_signals(signals = nil)
|
20
|
+
if signals.nil?
|
21
|
+
trap_signals(COMMON_SIGNALS)
|
22
|
+
trap_signals(JRUBY_SIGNALS) unless defined?(JRUBY_VERSION)
|
23
|
+
trap_signals(OPTIONAL_SIGNALS)
|
24
|
+
else
|
25
|
+
signals.each { |sig| trap_signal(sig) }
|
26
|
+
end
|
27
|
+
|
28
|
+
rescue ArgumentError
|
29
|
+
warn "Signals are not supported: #{signals.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def trap_signal(sig)
|
33
|
+
trap_method = "trap_#{sig}"
|
34
|
+
return unless @handler.respond_to?(trap_method)
|
35
|
+
|
36
|
+
debug :setup, sig
|
37
|
+
|
38
|
+
old_handler = trap sig do
|
39
|
+
debug :trap, sig
|
40
|
+
@handler.send(trap_method, @worker)
|
41
|
+
old_handler.call if old_handler.respond_to?(:call)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def debug(key, sig)
|
46
|
+
@worker.debug { ["signals.#{key}", {:signal => sig}] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module QueueKit
|
2
|
+
module Worker
|
3
|
+
def initialize(queue, options = {})
|
4
|
+
@queue = queue
|
5
|
+
@processor = options.fetch(:processor) { method(:process) }
|
6
|
+
@cooler = options.fetch(:cooler) { method(:cool) }
|
7
|
+
@error_handler = options.fetch(:error_handler) { method(:handle_error) }
|
8
|
+
@instrumenter = options.fetch(:instrumenter) { PutsInstrumenter.new }
|
9
|
+
@stopped = true
|
10
|
+
|
11
|
+
if options.fetch(:debug) { false }
|
12
|
+
class << self
|
13
|
+
alias debug force_debug
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(item)
|
19
|
+
raise NotImplementedError, "This worker can't do anything with #{item.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def cool
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_error(err)
|
26
|
+
raise err
|
27
|
+
end
|
28
|
+
|
29
|
+
def trap(signal_handler)
|
30
|
+
SignalChecker.trap(self, signal_handler)
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
start
|
35
|
+
interval_debugger = lambda { "worker.interval" }
|
36
|
+
|
37
|
+
loop do
|
38
|
+
working? ? work : break
|
39
|
+
debug(&interval_debugger)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def procline(string)
|
44
|
+
$0 = "QueueKit-#{QueueKit::VERSION}: #{string}"
|
45
|
+
debug { ["worker.procline", {:message => string}] }
|
46
|
+
end
|
47
|
+
|
48
|
+
def work
|
49
|
+
wrap_error { work! }
|
50
|
+
end
|
51
|
+
|
52
|
+
def work!
|
53
|
+
if item = @queue.pop
|
54
|
+
set_working_procline
|
55
|
+
@processor.call(item)
|
56
|
+
set_popping_procline
|
57
|
+
else
|
58
|
+
@cooler.call
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def wrap_error
|
63
|
+
yield
|
64
|
+
rescue Exception => exception
|
65
|
+
@error_handler.call(exception)
|
66
|
+
end
|
67
|
+
|
68
|
+
def name
|
69
|
+
@name ||= "#{self.class} #{Socket.gethostname}:#{Process.pid}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def start
|
73
|
+
instrument "worker.start"
|
74
|
+
set_popping_procline
|
75
|
+
@stopped = false
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop
|
79
|
+
instrument "worker.stop"
|
80
|
+
@stopped = true
|
81
|
+
end
|
82
|
+
|
83
|
+
def working?
|
84
|
+
!@stopped
|
85
|
+
end
|
86
|
+
|
87
|
+
def instrument(name, payload = nil)
|
88
|
+
(payload ||= {}).update(:worker => self)
|
89
|
+
@instrumenter.instrument("queuekit.#{name}", payload)
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_working_procline
|
93
|
+
procline("Processing since #{Time.now.to_i}")
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_popping_procline
|
97
|
+
@last_job_at = Time.now
|
98
|
+
procline("Waiting since #{@last_job_at.to_i}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def force_debug
|
102
|
+
instrument(*yield)
|
103
|
+
end
|
104
|
+
|
105
|
+
def debug
|
106
|
+
end
|
107
|
+
|
108
|
+
class PutsInstrumenter
|
109
|
+
def instrument(name, payload = nil)
|
110
|
+
puts "[#{Time.now}] #{name}: #{payload.inspect}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
data/lib/queue_kit.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module QueueKit
|
2
|
+
VERSION = "0.0.6"
|
3
|
+
ROOT = File.expand_path("../queue_kit", __FILE__)
|
4
|
+
|
5
|
+
def self.require_lib(*libs)
|
6
|
+
libs.each do |lib|
|
7
|
+
require File.join(ROOT, lib.to_s)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
alias require_libs require_lib
|
13
|
+
end
|
14
|
+
|
15
|
+
require_lib "worker"
|
16
|
+
end
|
17
|
+
|
data/queue_kit.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
lib = "queue_kit"
|
2
|
+
lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__)
|
3
|
+
File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/
|
4
|
+
version = $1
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.specification_version = 2 if spec.respond_to? :specification_version=
|
8
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
9
|
+
|
10
|
+
spec.name = lib
|
11
|
+
spec.version = version
|
12
|
+
|
13
|
+
spec.summary = "Job Queue hobby kit"
|
14
|
+
|
15
|
+
spec.authors = ["Rick Olson"]
|
16
|
+
spec.email = 'technoweenie@gmail.com'
|
17
|
+
spec.homepage = 'https://github.com/technoweenie/queue_kit'
|
18
|
+
spec.licenses = ['MIT']
|
19
|
+
|
20
|
+
spec.files = %w(Gemfile README.md Rakefile)
|
21
|
+
spec.files << "#{lib}.gemspec"
|
22
|
+
spec.files += Dir.glob("lib/**/*.rb")
|
23
|
+
spec.files += Dir.glob("test/**/*.rb")
|
24
|
+
spec.files += Dir.glob("script/*")
|
25
|
+
|
26
|
+
dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL'
|
27
|
+
git_files = `git ls-files -z 2>#{dev_null}`
|
28
|
+
spec.files &= git_files.split("\0") if $?.success?
|
29
|
+
|
30
|
+
spec.test_files = Dir.glob("test/**/*.rb")
|
31
|
+
end
|
data/script/bootstrap
ADDED
data/script/console
ADDED
data/script/package
ADDED
data/script/release
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Usage: script/release
|
3
|
+
# Build the package, tag a commit, push it to origin, and then release the
|
4
|
+
# package publicly.
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
version="$(script/package | grep Version: | awk '{print $2}')"
|
9
|
+
[ -n "$version" ] || exit 1
|
10
|
+
|
11
|
+
git commit --allow-empty -a -m "Release $version"
|
12
|
+
git tag "v$version"
|
13
|
+
git push origin
|
14
|
+
git push origin "v$version"
|
15
|
+
git push legacy
|
16
|
+
git push legacy "v$version"
|
17
|
+
gem push pkg/*-${version}.gem
|
18
|
+
|
data/script/test
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
QueueKit.require_lib 'clients/command_timeout'
|
3
|
+
|
4
|
+
class CommandTimeoutTest < Test::Unit::TestCase
|
5
|
+
include QueueKit::Clients::CommandTimeout
|
6
|
+
|
7
|
+
def test_with_ivars
|
8
|
+
object = FakeQueue.new
|
9
|
+
assert_nil object.command_timeout_ms
|
10
|
+
assert_nil object.max_command_timeout_ms
|
11
|
+
|
12
|
+
object.command_timeout_from({})
|
13
|
+
assert_equal 10, object.command_timeout_ms
|
14
|
+
assert_equal 1000, object.max_command_timeout_ms
|
15
|
+
|
16
|
+
object.command_timeout_from \
|
17
|
+
:command_timeout_ms => 1, :max_command_timeout_ms => 2
|
18
|
+
assert_equal 1, object.command_timeout_ms
|
19
|
+
assert_equal 2, object.max_command_timeout_ms
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_gets_timeout_for_first_attempt
|
23
|
+
assert_equal 10, command_timeout(attempts=0)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_backs_off
|
27
|
+
assert_equal 20, command_timeout(attempts=1)
|
28
|
+
assert_equal 30, command_timeout(attempts=2)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_enforces_max_timeout
|
32
|
+
assert_equal 1000, command_timeout(attempts=1000)
|
33
|
+
end
|
34
|
+
|
35
|
+
class FakeQueue
|
36
|
+
QueueKit::Clients::CommandTimeout.with_ivars(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
QueueKit.require_lib 'clients/round_robin_shuffler'
|
3
|
+
|
4
|
+
class RoundRobinShufflerTest < Test::Unit::TestCase
|
5
|
+
include QueueKit::Clients::RoundRobinShuffler
|
6
|
+
|
7
|
+
attr_reader :clients
|
8
|
+
|
9
|
+
def test_client_command_with_retries
|
10
|
+
clients = []
|
11
|
+
|
12
|
+
set_clients 1, 2
|
13
|
+
|
14
|
+
value = client_command_with_retries 3 do |client|
|
15
|
+
clients << client
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal [1, 2, 1], clients
|
20
|
+
assert_nil value
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_client_command_with_value
|
24
|
+
clients = []
|
25
|
+
|
26
|
+
set_clients 1, 2
|
27
|
+
|
28
|
+
value = client_command_with_retries 3 do |client, attempts|
|
29
|
+
assert_equal clients.size, attempts
|
30
|
+
clients << client
|
31
|
+
client == 2 ? :booya : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_equal [1, 2], clients
|
35
|
+
assert_equal :booya, value
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_with_ivars
|
39
|
+
object = FakeQueue.new
|
40
|
+
assert_nil object.commands_per_client
|
41
|
+
|
42
|
+
object.round_robin_from({})
|
43
|
+
assert_equal 100, object.commands_per_client
|
44
|
+
|
45
|
+
object.round_robin_from :commands_per_client => 1
|
46
|
+
assert_equal 1, object.commands_per_client
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_shuffles_solitary_client
|
50
|
+
set_clients 1
|
51
|
+
|
52
|
+
assert_equal 1, client
|
53
|
+
assert_equal 1, client
|
54
|
+
assert_equal 1, client
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_shuffles_clients
|
58
|
+
set_clients 1, 2
|
59
|
+
|
60
|
+
assert_equal 1, client
|
61
|
+
assert_equal 1, client
|
62
|
+
assert_equal 2, client
|
63
|
+
end
|
64
|
+
|
65
|
+
def commands_per_client
|
66
|
+
2
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_clients(*clients)
|
70
|
+
@client_index = @client_len = nil
|
71
|
+
@clients = clients
|
72
|
+
rotate_client
|
73
|
+
end
|
74
|
+
|
75
|
+
class FakeQueue
|
76
|
+
QueueKit::Clients::RoundRobinShuffler.with_ivars(self)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
data/test/worker_test.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path("../helper", __FILE__)
|
2
|
+
|
3
|
+
class WorkerTest < Test::Unit::TestCase
|
4
|
+
def test_cooler
|
5
|
+
cooled = false
|
6
|
+
cooler = lambda { cooled = true }
|
7
|
+
|
8
|
+
worker = new_worker [], :processor => lambda { |_| fail 'item found?' },
|
9
|
+
:cooler => cooler
|
10
|
+
|
11
|
+
worker.work
|
12
|
+
assert cooled
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_custom_on_error_handler
|
16
|
+
called = false
|
17
|
+
error_handler = lambda do |exc|
|
18
|
+
called = true
|
19
|
+
assert_equal 'booya', exc.message
|
20
|
+
end
|
21
|
+
|
22
|
+
worker = new_worker [1], :processor => lambda { |_| raise 'booya' },
|
23
|
+
:error_handler => error_handler
|
24
|
+
|
25
|
+
worker.work
|
26
|
+
|
27
|
+
assert called
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_default_error_handler
|
31
|
+
processor = lambda do |item|
|
32
|
+
raise item.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
worker = new_worker [1], :processor => processor
|
36
|
+
|
37
|
+
begin
|
38
|
+
worker.work
|
39
|
+
rescue RuntimeError => err
|
40
|
+
assert_equal '1', err.message
|
41
|
+
else
|
42
|
+
fail "no exception raised"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_breaks_when_stopped
|
47
|
+
called = false
|
48
|
+
worker = nil
|
49
|
+
|
50
|
+
processor = lambda do |item|
|
51
|
+
fail "callback called multiple times" if called
|
52
|
+
assert_equal 1, item
|
53
|
+
called = true
|
54
|
+
worker.stop
|
55
|
+
end
|
56
|
+
|
57
|
+
worker = new_worker [1, nil], :processor => processor
|
58
|
+
worker.run
|
59
|
+
|
60
|
+
assert called
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_new_worker_is_not_working
|
64
|
+
assert !new_worker.working?
|
65
|
+
end
|
66
|
+
|
67
|
+
def new_worker(queue = [], options = {})
|
68
|
+
options[:instrumenter] ||= NullInstrumenter.new
|
69
|
+
Worker.new(queue, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
class Worker
|
73
|
+
include QueueKit::Worker
|
74
|
+
end
|
75
|
+
|
76
|
+
class NullInstrumenter
|
77
|
+
def instrument(name, payload = nil)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: queue_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rick Olson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email: technoweenie@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- queue_kit.gemspec
|
24
|
+
- lib/queue_kit/clients/command_timeout.rb
|
25
|
+
- lib/queue_kit/clients/round_robin_shuffler.rb
|
26
|
+
- lib/queue_kit/signal_checker.rb
|
27
|
+
- lib/queue_kit/signal_handlers/graceful_quit.rb
|
28
|
+
- lib/queue_kit/worker.rb
|
29
|
+
- lib/queue_kit.rb
|
30
|
+
- test/command_timeout_test.rb
|
31
|
+
- test/helper.rb
|
32
|
+
- test/round_robin_shuffler_test.rb
|
33
|
+
- test/worker_test.rb
|
34
|
+
- script/bootstrap
|
35
|
+
- script/console
|
36
|
+
- script/package
|
37
|
+
- script/release
|
38
|
+
- script/test
|
39
|
+
homepage: https://github.com/technoweenie/queue_kit
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.3.6
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.23
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: Job Queue hobby kit
|
64
|
+
test_files:
|
65
|
+
- test/command_timeout_test.rb
|
66
|
+
- test/helper.rb
|
67
|
+
- test/round_robin_shuffler_test.rb
|
68
|
+
- test/worker_test.rb
|