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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 826cb79526ad3b850c35706cf2f83d31d964bea9
4
- data.tar.gz: 2660a866510fe81c2fa4803d3934d1c7cc391dfa
3
+ metadata.gz: 71c0caf5486b4a3dd29477c55ad64dd46a524413
4
+ data.tar.gz: 74a7c074fb111de0d96a35b5d8de8ac75d509270
5
5
  SHA512:
6
- metadata.gz: bbfc8eac02fb62742f91149a17efe40acbd3ee12c68403fe279ec68b7298c75734efed720ccdcb279326a3b0c5c3a9340e3b97e7c106a69b5f61b7fb931505e6
7
- data.tar.gz: 7a715795ff4ff9e7d9f4aead0e3d4e1bda67d5f86bb34361474fd70fc4dbf21bcb89e9261521eff8b4c11e9d6c750c5fdbeca625e9a9e50d0186edd89aa3d667
6
+ metadata.gz: 80372b97d3b71ec35006cd72c933d2466de7334cf983496ba64c942e19ed8f81e3f577efe6a3bf44e31a665de96f759c53cf2824c946f57ea3d0f1dd05ff55b8
7
+ data.tar.gz: 641c1458dc324d58e98369c04bf8e403e403be5acbac3747236cadcffe0edba72c176c8e68a8d0d57edf43ede2a30d2b1972c9333c7fa9e1c82d43456826f194
@@ -1,7 +1,5 @@
1
1
  rvm:
2
2
  - 1.9.3
3
3
  - 2.0.0
4
- - jruby-19mode
5
- - rbx-19mode
6
4
  services:
7
5
  - redis-server
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source 'http://rubygems.org'
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 wraps [Redis][redis], uses [Message Pack][msgpack], and speaks Ruby.
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
- queue = Turnpike['foo']
9
- queue << 1, 2, 3
10
- queue.pop # => 1
11
- queue << 4
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.4 or higher.
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
- [redis]: http://redis.io/
20
- [msgpack]: http://msgpack.org/
42
+ [red]: http://redis.io/
@@ -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
- # Create a queue.
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
@@ -1,32 +1,8 @@
1
- require 'msgpack'
2
- require 'redis'
1
+ require 'turnpike/base'
3
2
 
4
3
  module Turnpike
5
- # A queue.
6
- class Queue
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 << MessagePack.unpack(item)
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| MessagePack.pack(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| MessagePack.pack(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
@@ -1,3 +1,3 @@
1
1
  module Turnpike
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -1,52 +1,75 @@
1
- require 'bundler/setup'
1
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
2
2
  require 'minitest/autorun'
3
- require File.expand_path('../lib/turnpike', File.dirname(__FILE__))
3
+ require 'minitest/benchmark' if ENV['BENCH']
4
+ require 'turnpike'
4
5
 
5
6
  class TestTurnpike < MiniTest::Unit::TestCase
6
- def setup
7
- Redis.current.flushall
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 peek(queue)
11
- redis = queue.send(:redis)
12
- redis.lrange(queue.name, 0, -1).map { |i| MessagePack.unpack(i) }
12
+ def test_queue_name
13
+ queue = Turnpike::Base.new('foo')
14
+ assert_includes queue.name, 'foo'
13
15
  end
14
16
 
15
- def test_bracket
16
- queue = Turnpike['foo']
17
- assert_kind_of Turnpike::Queue, queue
18
- assert_equal 'turnpike:foo', queue.name
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 test_emptiness
22
- queue = Turnpike::Queue.new
23
- assert queue.empty?
24
- queue << 1
25
- assert !queue.empty?
26
- queue.clear
27
- assert queue.empty?
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 = Turnpike::Queue.new
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 [3, 2, 1], peek(queue)
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 test_multiple_queues
74
- queue1 = Turnpike::Queue.new 'foo'
75
- queue2 = Turnpike::Queue.new 'bar'
76
- queue1.push(1)
77
- queue2.push(2, 3)
78
- assert_equal 1, queue1.size
79
- assert_equal 2, queue2.size
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
@@ -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.summary = %q{A Redis-backed queue}
12
- s.description = %q{Turnpike is a Redis-backed queue.}
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.6.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-08 00:00:00.000000000 Z
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: Turnpike is a Redis-backed queue.
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: