turnpike 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -2
- data/Gemfile +1 -1
- data/README.md +32 -10
- data/lib/turnpike.rb +3 -8
- data/lib/turnpike/base.rb +31 -0
- data/lib/turnpike/queue.rb +6 -37
- data/lib/turnpike/unique_queue.rb +59 -0
- data/lib/turnpike/version.rb +1 -1
- data/test/turnpike_test.rb +76 -34
- data/turnpike.gemspec +4 -3
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71c0caf5486b4a3dd29477c55ad64dd46a524413
|
4
|
+
data.tar.gz: 74a7c074fb111de0d96a35b5d8de8ac75d509270
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80372b97d3b71ec35006cd72c933d2466de7334cf983496ba64c942e19ed8f81e3f577efe6a3bf44e31a665de96f759c53cf2824c946f57ea3d0f1dd05ff55b8
|
7
|
+
data.tar.gz: 641c1458dc324d58e98369c04bf8e403e403be5acbac3747236cadcffe0edba72c176c8e68a8d0d57edf43ede2a30d2b1972c9333c7fa9e1c82d43456826f194
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source '
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
gemspec
|
data/README.md
CHANGED
@@ -2,19 +2,41 @@
|
|
2
2
|
|
3
3
|
![turnpike][nj]
|
4
4
|
|
5
|
-
Turnpike
|
5
|
+
Turnpike is a minimal [Redis][red]-backed FIFO queue in Ruby.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Push and pop:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
q = Turnpike.call('queue name')
|
13
|
+
q.push('foo', 'bar', 'baz', 'qux') # => 4
|
14
|
+
q.pop # => 'foo'
|
15
|
+
```
|
16
|
+
|
17
|
+
Pop multiple items:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
q.pop(2) # => ['bar', 'baz']
|
21
|
+
```
|
22
|
+
|
23
|
+
Push payload to the front of the queue:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
q.unshift('foo') # => 2
|
27
|
+
q.pop # => 'foo"'
|
28
|
+
```
|
29
|
+
|
30
|
+
Use a queue with set-like properties to ensure uniqueness of queued items:
|
6
31
|
|
7
32
|
```ruby
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
queue.unshift(1)
|
13
|
-
queue.pop(4) # => [1, 2, 3, 4]
|
33
|
+
q = Turnpike.call('queue name', unique: true)
|
34
|
+
q.push('foo', 'bar') # => 2
|
35
|
+
q.push('bar') # => 2
|
36
|
+
q.pop(3) # => ['foo', 'bar']
|
14
37
|
```
|
15
38
|
|
16
|
-
Turnpike requires Redis 2.
|
39
|
+
Turnpike requires Ruby 2.0 and Redis 2.6 or higher.
|
17
40
|
|
18
41
|
[nj]: http://f.cl.ly/items/33242X323P3M1t1G400H/turnpike.jpg
|
19
|
-
[
|
20
|
-
[msgpack]: http://msgpack.org/
|
42
|
+
[red]: http://redis.io/
|
data/lib/turnpike.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
require 'turnpike/queue'
|
2
|
+
require 'turnpike/unique_queue'
|
2
3
|
|
3
|
-
# A Redis-backed queue.
|
4
4
|
module Turnpike
|
5
|
-
|
6
|
-
|
7
|
-
# name - A queue name that responds to to_s.
|
8
|
-
#
|
9
|
-
# Returns a Turnpike::Queue.
|
10
|
-
def self.[](name)
|
11
|
-
Queue.new(name)
|
5
|
+
def self.call(name = 'default', unique: false)
|
6
|
+
(unique ? UniqueQueue : Queue).new(name)
|
12
7
|
end
|
13
8
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'msgpack'
|
3
|
+
require 'redis'
|
4
|
+
|
5
|
+
module Turnpike
|
6
|
+
class Base
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegator :Redis, :current, :redis
|
10
|
+
def_delegators :MessagePack, :pack, :unpack
|
11
|
+
|
12
|
+
attr :name
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = "turnpike:#{name}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def clear
|
19
|
+
redis.del(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty?
|
23
|
+
size == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def pop(*); raise NotImplementedError; end
|
27
|
+
def push(*); raise NotImplementedError; end
|
28
|
+
def size; raise NotImplementedError; end
|
29
|
+
def unshift(*); raise NotImplementedError; end
|
30
|
+
end
|
31
|
+
end
|
data/lib/turnpike/queue.rb
CHANGED
@@ -1,32 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
require 'redis'
|
1
|
+
require 'turnpike/base'
|
3
2
|
|
4
3
|
module Turnpike
|
5
|
-
|
6
|
-
|
7
|
-
# Returns a String name.
|
8
|
-
attr :name
|
9
|
-
|
10
|
-
# Create a new queue.
|
11
|
-
#
|
12
|
-
# name - A queue name that responds to to_s.
|
13
|
-
def initialize(name = 'queue')
|
14
|
-
@name = "turnpike:#{name}"
|
15
|
-
end
|
16
|
-
|
17
|
-
# Remove all items from the queue.
|
18
|
-
#
|
19
|
-
# Returns Integer number of items that were removed.
|
20
|
-
def clear
|
21
|
-
redis.del(name)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Returns whether the queue is empty.
|
25
|
-
def empty?
|
26
|
-
size == 0
|
27
|
-
end
|
28
|
-
|
29
|
-
# Pop one or more items from the front of the queue.
|
4
|
+
class Queue < Base
|
5
|
+
# Pop one or more items from the queue.
|
30
6
|
#
|
31
7
|
# n - Integer number of items to pop.
|
32
8
|
#
|
@@ -35,7 +11,7 @@ module Turnpike
|
|
35
11
|
items = []
|
36
12
|
n.times do
|
37
13
|
break unless item = redis.lpop(name)
|
38
|
-
items <<
|
14
|
+
items << unpack(item)
|
39
15
|
end
|
40
16
|
|
41
17
|
n == 1 ? items.first : items
|
@@ -47,10 +23,9 @@ module Turnpike
|
|
47
23
|
#
|
48
24
|
# Returns the Integer size of the queue after the operation.
|
49
25
|
def push(*items)
|
50
|
-
redis.rpush(name, items.map { |i|
|
26
|
+
redis.rpush(name, items.map { |i| pack(i) })
|
51
27
|
end
|
52
28
|
|
53
|
-
# Syntactic sugar.
|
54
29
|
alias << push
|
55
30
|
|
56
31
|
# Returns the Integer size of the queue.
|
@@ -64,13 +39,7 @@ module Turnpike
|
|
64
39
|
#
|
65
40
|
# Returns the Integer size of the queue after the operation.
|
66
41
|
def unshift(*items)
|
67
|
-
redis.lpush(name, items.map { |i|
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def redis
|
73
|
-
Redis.current
|
42
|
+
redis.lpush(name, items.map { |i| pack(i) })
|
74
43
|
end
|
75
44
|
end
|
76
45
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'turnpike/base'
|
3
|
+
|
4
|
+
module Turnpike
|
5
|
+
class UniqueQueue < Base
|
6
|
+
ZPOP = <<-EOF
|
7
|
+
local res = redis.call('zrange', KEYS[1], 0, KEYS[2] - 1)
|
8
|
+
redis.pcall('zrem', KEYS[1], unpack(res))
|
9
|
+
return res
|
10
|
+
EOF
|
11
|
+
|
12
|
+
ZPOP_SHA = Digest::SHA1.hexdigest(ZPOP)
|
13
|
+
|
14
|
+
# Pop one or more items from the queue.
|
15
|
+
#
|
16
|
+
# n - Integer number of items to pop.
|
17
|
+
#
|
18
|
+
# Returns a String item, an Array of items, or nil if the queue is empty.
|
19
|
+
def pop(n = 1)
|
20
|
+
items = begin
|
21
|
+
redis.evalsha(ZPOP_SHA, [name, n])
|
22
|
+
rescue Redis::CommandError
|
23
|
+
redis.eval(ZPOP, [name, n])
|
24
|
+
end
|
25
|
+
items.map! { |i| unpack(i) }
|
26
|
+
|
27
|
+
n == 1 ? items.first : items
|
28
|
+
end
|
29
|
+
|
30
|
+
# Push items to the end of the queue.
|
31
|
+
#
|
32
|
+
# items - A splat Array of items.
|
33
|
+
#
|
34
|
+
# Returns the Integer size of the queue after the operation.
|
35
|
+
def push(*items, score: Time.now.to_f)
|
36
|
+
redis.multi do
|
37
|
+
redis.zadd(name, items.reduce([]) { |ary, i| ary.push(score, pack(i)) })
|
38
|
+
size
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
alias << push
|
43
|
+
|
44
|
+
# Returns the Integer size of the queue.
|
45
|
+
def size
|
46
|
+
redis.zcard(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Push items to the front of the queue.
|
50
|
+
#
|
51
|
+
# items - A splat Array of items.
|
52
|
+
#
|
53
|
+
# Returns the Integer size of the queue after the operation.
|
54
|
+
def unshift(*items)
|
55
|
+
_, score = redis.zrange(name, 0, 0, with_scores: true).pop
|
56
|
+
score ? push(*items, score: score - 1) : push(*items)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/turnpike/version.rb
CHANGED
data/test/turnpike_test.rb
CHANGED
@@ -1,52 +1,75 @@
|
|
1
|
-
|
1
|
+
$: << File.expand_path('../lib', File.dirname(__FILE__))
|
2
2
|
require 'minitest/autorun'
|
3
|
-
require
|
3
|
+
require 'minitest/benchmark' if ENV['BENCH']
|
4
|
+
require 'turnpike'
|
4
5
|
|
5
6
|
class TestTurnpike < MiniTest::Unit::TestCase
|
6
|
-
def
|
7
|
-
|
7
|
+
def test_call
|
8
|
+
assert_kind_of Turnpike::Queue, Turnpike.call
|
9
|
+
assert_kind_of Turnpike::UniqueQueue, Turnpike.call(unique: true)
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
12
|
+
def test_queue_name
|
13
|
+
queue = Turnpike::Base.new('foo')
|
14
|
+
assert_includes queue.name, 'foo'
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
queue = Turnpike
|
17
|
-
|
18
|
-
|
17
|
+
def test_abstract_class
|
18
|
+
queue = Turnpike::Base.new('foo')
|
19
|
+
%w(push pop unshift size).each do |mth|
|
20
|
+
assert_raises(NotImplementedError) { queue.send(mth) }
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
|
-
def
|
22
|
-
queue = Turnpike
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def bench_queue
|
25
|
+
queue = Turnpike.call
|
26
|
+
assert_performance_linear do |n|
|
27
|
+
queue.push(*(1..n).to_a)
|
28
|
+
1.upto(n) { queue.pop }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def bench_unique
|
33
|
+
queue = Turnpike.call(unique: true)
|
34
|
+
assert_performance_linear do |n|
|
35
|
+
queue.push(*(1..n).to_a)
|
36
|
+
1.upto(n) { queue.pop }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module QueueTests
|
42
|
+
def queue
|
43
|
+
@queue ||= klass.new('foo')
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup
|
47
|
+
Redis.current.flushall
|
28
48
|
end
|
29
49
|
|
30
50
|
def test_push
|
31
|
-
queue = Turnpike::Queue.new
|
32
51
|
queue.push(1)
|
33
52
|
assert_equal 1, queue.size
|
34
53
|
queue.push(2, 3)
|
35
|
-
assert_equal 3, queue.size
|
36
54
|
assert_equal [1, 2, 3], peek(queue)
|
37
55
|
end
|
38
56
|
|
57
|
+
def test_emptiness
|
58
|
+
assert queue.empty?
|
59
|
+
queue << 1
|
60
|
+
assert !queue.empty?
|
61
|
+
queue.clear
|
62
|
+
assert queue.empty?
|
63
|
+
end
|
64
|
+
|
39
65
|
def test_unshift
|
40
|
-
queue
|
41
|
-
queue.unshift(1)
|
42
|
-
assert_equal 1, queue.size
|
66
|
+
queue.push(1)
|
43
67
|
queue.unshift(2, 3)
|
44
68
|
assert_equal 3, queue.size
|
45
|
-
assert_equal
|
69
|
+
assert_equal 1, peek(queue).last
|
46
70
|
end
|
47
71
|
|
48
72
|
def test_pop
|
49
|
-
queue = Turnpike::Queue.new
|
50
73
|
queue.push(1, 2)
|
51
74
|
assert_equal 1, queue.pop
|
52
75
|
assert_equal 2, queue.pop
|
@@ -54,7 +77,6 @@ class TestTurnpike < MiniTest::Unit::TestCase
|
|
54
77
|
end
|
55
78
|
|
56
79
|
def test_pop_many
|
57
|
-
queue = Turnpike::Queue.new
|
58
80
|
queue.push(1, 2, 3)
|
59
81
|
assert_equal [1, 2], queue.pop(2)
|
60
82
|
assert_equal [3], queue.pop(2)
|
@@ -62,7 +84,6 @@ class TestTurnpike < MiniTest::Unit::TestCase
|
|
62
84
|
end
|
63
85
|
|
64
86
|
def test_order
|
65
|
-
queue = Turnpike::Queue.new
|
66
87
|
queue.push(1, 2)
|
67
88
|
queue.unshift(3)
|
68
89
|
assert_equal 3, queue.pop
|
@@ -70,12 +91,33 @@ class TestTurnpike < MiniTest::Unit::TestCase
|
|
70
91
|
assert_equal 2, queue.pop
|
71
92
|
end
|
72
93
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
assert_equal 1,
|
79
|
-
assert_equal 2,
|
94
|
+
def test_multiplicity
|
95
|
+
q1 = klass.new('foo')
|
96
|
+
q2 = klass.new('bar')
|
97
|
+
q1.push(1)
|
98
|
+
q2.push(2, 3)
|
99
|
+
assert_equal 1, q1.size
|
100
|
+
assert_equal 2, q2.size
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class TestQueue < MiniTest::Unit::TestCase
|
105
|
+
include QueueTests
|
106
|
+
|
107
|
+
def klass; Turnpike::Queue; end
|
108
|
+
|
109
|
+
def peek(queue)
|
110
|
+
queue.redis.lrange(queue.name, 0, -1).map { |i| queue.unpack(i) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
class TestUniqueQueue < MiniTest::Unit::TestCase
|
116
|
+
include QueueTests
|
117
|
+
|
118
|
+
def klass; Turnpike::UniqueQueue; end
|
119
|
+
|
120
|
+
def peek(queue)
|
121
|
+
queue.redis.zrange(queue.name, 0, -1).map { |i| queue.unpack(i) }
|
80
122
|
end
|
81
123
|
end
|
data/turnpike.gemspec
CHANGED
@@ -8,15 +8,16 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ['Hakan Ensari']
|
9
9
|
s.email = ['hakan.ensari@papercavalier.com']
|
10
10
|
s.homepage = 'http://github.com/hakanensari/turnpike'
|
11
|
-
s.
|
12
|
-
s.
|
11
|
+
s.description = %q{A Redis-backed FIFO queue}
|
12
|
+
s.summary = %q{A Redis-backed FIFO queue}
|
13
13
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
16
|
s.require_paths = ['lib']
|
18
17
|
|
19
18
|
s.add_runtime_dependency 'redis', '~> 3.0'
|
20
19
|
s.add_runtime_dependency 'msgpack', '~> 0.5.4'
|
21
20
|
s.add_development_dependency 'rake'
|
21
|
+
|
22
|
+
s.required_ruby_version = '>= 2.0'
|
22
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turnpike
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hakan Ensari
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description:
|
55
|
+
description: A Redis-backed FIFO queue
|
56
56
|
email:
|
57
57
|
- hakan.ensari@papercavalier.com
|
58
58
|
executables: []
|
@@ -66,7 +66,9 @@ files:
|
|
66
66
|
- README.md
|
67
67
|
- Rakefile
|
68
68
|
- lib/turnpike.rb
|
69
|
+
- lib/turnpike/base.rb
|
69
70
|
- lib/turnpike/queue.rb
|
71
|
+
- lib/turnpike/unique_queue.rb
|
70
72
|
- lib/turnpike/version.rb
|
71
73
|
- test/turnpike_test.rb
|
72
74
|
- turnpike.gemspec
|
@@ -81,7 +83,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
83
|
requirements:
|
82
84
|
- - '>='
|
83
85
|
- !ruby/object:Gem::Version
|
84
|
-
version: '0'
|
86
|
+
version: '2.0'
|
85
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
88
|
requirements:
|
87
89
|
- - '>='
|
@@ -92,7 +94,6 @@ rubyforge_project:
|
|
92
94
|
rubygems_version: 2.0.0
|
93
95
|
signing_key:
|
94
96
|
specification_version: 4
|
95
|
-
summary: A Redis-backed queue
|
97
|
+
summary: A Redis-backed FIFO queue
|
96
98
|
test_files:
|
97
99
|
- test/turnpike_test.rb
|
98
|
-
has_rdoc:
|