turnpike 0.0.1
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/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/README.md +32 -0
- data/Rakefile +10 -0
- data/lib/turnpike.rb +121 -0
- data/test/turnpike_test.rb +131 -0
- data/turnpike.gemspec +31 -0
- metadata +102 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use 1.9.2@turnpike
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Turnpike
|
2
|
+
|
3
|
+
Turnpike is a Redis-backed processing queue.
|
4
|
+
|
5
|
+
# Usage
|
6
|
+
|
7
|
+
Set up:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require "turnpike"
|
11
|
+
|
12
|
+
queue = Turnpike.new(:processing_queue)
|
13
|
+
```
|
14
|
+
|
15
|
+
Queue:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
queue << "unit of work"
|
19
|
+
```
|
20
|
+
|
21
|
+
Process:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
queue.each do |work|
|
25
|
+
# create value
|
26
|
+
end
|
27
|
+
|
28
|
+
# alternatively
|
29
|
+
queue.each_slice(10) do |slice)
|
30
|
+
# create value
|
31
|
+
end
|
32
|
+
```
|
data/Rakefile
ADDED
data/lib/turnpike.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
# A Redis-backed queue.
|
4
|
+
class Turnpike
|
5
|
+
REDIS_VERSION = Redis.current.info['redis_version']
|
6
|
+
TIMEOUT = ENV['TURNPIKE_TIMEOUT'] || 2
|
7
|
+
VERSION = '0.0.1'
|
8
|
+
|
9
|
+
# The name of the queue.
|
10
|
+
attr :name
|
11
|
+
|
12
|
+
# Creates a new queue.
|
13
|
+
#
|
14
|
+
# Takes an optional name.
|
15
|
+
def initialize(name = 'default')
|
16
|
+
@name = "turnpike:#{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Removes all queued items.
|
20
|
+
def clear
|
21
|
+
redis.del(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Calls block once for each queued item.
|
25
|
+
#
|
26
|
+
# Takes an optional boolean argument to specify if the command should block
|
27
|
+
# the connection when the queue is empty. This argument defaults to false.
|
28
|
+
def each(blocking = false, &block)
|
29
|
+
while item = shift(blocking)
|
30
|
+
block.call(item)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Iterates the given block for each slice of `n` queued items.
|
35
|
+
def each_slice(n, blocking = false, &block)
|
36
|
+
slice = []
|
37
|
+
|
38
|
+
each(blocking) do |item|
|
39
|
+
slice << item
|
40
|
+
if slice.size == n
|
41
|
+
yield slice
|
42
|
+
slice = []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
yield slice unless slice.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns `true` if the queue is empty.
|
50
|
+
def empty?
|
51
|
+
length == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the length of the queue.
|
55
|
+
def length
|
56
|
+
redis.llen(name)
|
57
|
+
end
|
58
|
+
alias size length
|
59
|
+
|
60
|
+
# Returns an array of items currently queued.
|
61
|
+
#
|
62
|
+
# `start` is an integer and indicates the start offset, 0 being the first
|
63
|
+
# queued item. If negative, it indicates the offset from the end, -1 being
|
64
|
+
# the last queued item.
|
65
|
+
#
|
66
|
+
# `count` is also an integer and indicates the number of items to return.
|
67
|
+
def peek(start, count)
|
68
|
+
redis.lrange(name, start, count)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Retrieves the last queued item.
|
72
|
+
#
|
73
|
+
# Takes an optional boolean argument to specify if the command should block
|
74
|
+
# the connection when the queue is empty. This argument defaults to false.
|
75
|
+
def pop(blocking = false)
|
76
|
+
if blocking
|
77
|
+
redis.brpop(name, TIMEOUT)[1] rescue nil
|
78
|
+
else
|
79
|
+
redis.rpop(name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Pushes items to the end of the queue.
|
84
|
+
def push(*items)
|
85
|
+
# Up until Redis 2.3, `rpush` accepts a single value.
|
86
|
+
if REDIS_VERSION < '2.3'
|
87
|
+
items.each { |item| redis.rpush(name, item) }
|
88
|
+
else
|
89
|
+
redis.rpush(name, items)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
alias << push
|
93
|
+
|
94
|
+
# Retrieves the first queued item.
|
95
|
+
#
|
96
|
+
# Takes an optional boolean argument to specify if the command should block
|
97
|
+
# the connection when the queue is empty. This argument defaults to false.
|
98
|
+
def shift(blocking = false)
|
99
|
+
if blocking
|
100
|
+
redis.blpop(name, TIMEOUT)[1] rescue nil
|
101
|
+
else
|
102
|
+
redis.lpop(name)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Pushes items to the front of the queue.
|
107
|
+
def unshift(*items)
|
108
|
+
# Up until Redis 2.3, `rpush` accepts a single value.
|
109
|
+
if REDIS_VERSION < '2.3'
|
110
|
+
items.each { |item| redis.lpush(name, item) }
|
111
|
+
else
|
112
|
+
redis.lpush(name, items)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def redis
|
119
|
+
Redis.current
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
require File.expand_path('../lib/turnpike', File.dirname(__FILE__))
|
7
|
+
|
8
|
+
class TestTurnpike < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
Redis.current.flushall
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_emptiness
|
14
|
+
queue = Turnpike.new
|
15
|
+
assert(queue.empty?)
|
16
|
+
|
17
|
+
queue << 1
|
18
|
+
assert(!queue.empty?)
|
19
|
+
|
20
|
+
queue.clear
|
21
|
+
assert(queue.empty?)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_pushing_items
|
25
|
+
queue = Turnpike.new
|
26
|
+
queue.push(1)
|
27
|
+
assert_equal(1, queue.length)
|
28
|
+
|
29
|
+
queue.push(2, 3)
|
30
|
+
assert_equal(3, queue.length)
|
31
|
+
assert_equal(['1', '2', '3'], queue.peek(0, 3))
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_unshifting_items
|
35
|
+
queue = Turnpike.new
|
36
|
+
queue.unshift(1)
|
37
|
+
assert_equal(1, queue.length)
|
38
|
+
|
39
|
+
queue.unshift(2, 3)
|
40
|
+
assert_equal(3, queue.length)
|
41
|
+
assert_equal(['3', '2', '1'], queue.peek(0, 3))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_popping_items
|
45
|
+
queue = Turnpike.new
|
46
|
+
queue.push(1, 2)
|
47
|
+
assert_equal('2', queue.pop)
|
48
|
+
assert_equal('1', queue.pop)
|
49
|
+
assert_equal(nil, queue.pop)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_shifting_items
|
53
|
+
queue = Turnpike.new
|
54
|
+
queue.push(1, 2)
|
55
|
+
assert_equal('1', queue.shift)
|
56
|
+
assert_equal('2', queue.shift)
|
57
|
+
assert_equal(nil, queue.shift)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_enumeration
|
61
|
+
queue = Turnpike.new
|
62
|
+
queue.push(1, 2)
|
63
|
+
items = []
|
64
|
+
queue.each { |item| items << item }
|
65
|
+
assert_equal(['1', '2'], items)
|
66
|
+
|
67
|
+
queue.push(1, 2, 3, 4)
|
68
|
+
slices = []
|
69
|
+
queue.each_slice(3) { |slice| slices << slice }
|
70
|
+
assert_equal([['1', '2', '3'], ['4']], slices)
|
71
|
+
|
72
|
+
queue.push(1, 2)
|
73
|
+
slices = []
|
74
|
+
queue.each_slice(2) { |slice| slices << slice }
|
75
|
+
assert_equal([['1', '2']], slices)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_aliases
|
79
|
+
queue = Turnpike.new
|
80
|
+
queue << 1
|
81
|
+
assert_equal(1, queue.size)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_multiple_queues
|
85
|
+
queue1 = Turnpike.new("foo")
|
86
|
+
queue2 = Turnpike.new("bar")
|
87
|
+
|
88
|
+
queue1.push(1)
|
89
|
+
queue2.push(2, 3)
|
90
|
+
|
91
|
+
assert_equal(1, queue1.length)
|
92
|
+
assert_equal(2, queue2.length)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_blocking_pop
|
96
|
+
queue = Turnpike.new
|
97
|
+
started_at = Time.now.to_i
|
98
|
+
Thread.new do
|
99
|
+
sleep(1)
|
100
|
+
queue.push(1)
|
101
|
+
end
|
102
|
+
assert_equal(0, queue.length)
|
103
|
+
assert_equal('1', queue.pop(true))
|
104
|
+
assert_equal(1, Time.now.to_i - started_at)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_blocking_shift
|
108
|
+
queue = Turnpike.new
|
109
|
+
started_at = Time.now.to_i
|
110
|
+
Thread.new do
|
111
|
+
sleep(1)
|
112
|
+
queue.push(1)
|
113
|
+
end
|
114
|
+
assert_equal(0, queue.length)
|
115
|
+
assert_equal('1', queue.shift(true))
|
116
|
+
assert_equal(1, Time.now.to_i - started_at)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_timeout
|
120
|
+
queue = Turnpike.new
|
121
|
+
thread = Thread.new do
|
122
|
+
sleep(3)
|
123
|
+
queue.push(1)
|
124
|
+
end
|
125
|
+
assert_equal(0, queue.length)
|
126
|
+
assert_equal(nil, queue.shift(true))
|
127
|
+
|
128
|
+
thread.join
|
129
|
+
assert_equal(1, queue.length)
|
130
|
+
end
|
131
|
+
end
|
data/turnpike.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require "./lib/turnpike"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "turnpike"
|
6
|
+
s.version = Turnpike::VERSION
|
7
|
+
s.authors = ["Paper Cavalier"]
|
8
|
+
s.email = ["code@papercavalier.com"]
|
9
|
+
s.homepage = ""
|
10
|
+
s.summary = %q{Turnpike is a Redis-backed processing queue.}
|
11
|
+
s.description = %q{Turnpike is a Redis-backed processing queue.}
|
12
|
+
|
13
|
+
s.rubyforge_project = "turnpike"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
{
|
21
|
+
'redis' => '~> 2.2'
|
22
|
+
}.each do |lib, version|
|
23
|
+
s.add_runtime_dependency lib, version
|
24
|
+
end
|
25
|
+
|
26
|
+
{
|
27
|
+
'rake' => '~> 0.9'
|
28
|
+
}.each do |lib, version|
|
29
|
+
s.add_development_dependency lib, version
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: turnpike
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Paper Cavalier
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-07-15 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 2
|
30
|
+
version: "2.2"
|
31
|
+
type: :runtime
|
32
|
+
prerelease: false
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rake
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 9
|
44
|
+
version: "0.9"
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Turnpike is a Redis-backed processing queue.
|
49
|
+
email:
|
50
|
+
- code@papercavalier.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- .rvmrc
|
60
|
+
- CHANGELOG.md
|
61
|
+
- Gemfile
|
62
|
+
- README.md
|
63
|
+
- Rakefile
|
64
|
+
- lib/turnpike.rb
|
65
|
+
- test/turnpike_test.rb
|
66
|
+
- turnpike.gemspec
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: ""
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: -65682769668407542
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: -65682769668407542
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project: turnpike
|
97
|
+
rubygems_version: 1.3.7
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Turnpike is a Redis-backed processing queue.
|
101
|
+
test_files:
|
102
|
+
- test/turnpike_test.rb
|