turnpike 0.6.0 → 0.7.0

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.
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: