hash_queue 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1
3
+ ## 0.1.1
4
+
5
+ + [FEATURE] Added queueing in batches (queueing more items in a single method call)
6
+ + [BUGFIX] Fixed bug when popping from HashQueue::Queue with blocking option didn't take nil or an empty array as a valid value [ISSUE#1]
7
+
8
+ ## 0.1.0
4
9
 
5
10
  + Initial release
data/README.md CHANGED
@@ -46,9 +46,19 @@ hash_queue[:my_queue].queue Stuff.new
46
46
  hash_queue.queue :my_queue, Stuff.new
47
47
  ```
48
48
 
49
- Keys (or namespaces if you prefer) can be anything you want. Usually those will be symbols or strings but don't need to be. Objects, classes, numbers or even `true` or `nil` will work. Same applies for queued items.
49
+ You can also queue multiple items in a single method call:
50
50
 
51
- `#queue` is aliased as `#enqueue` and `#push` for convenience on both hash_queue and individual queues.
51
+ ```ruby
52
+ hash_queue[:my_queue].queue_many :foo, :bar, :xyz
53
+
54
+ # Ruby itself provides some handy magic syntax for expanding arrays into arguments. The result is identical.
55
+ stuff = [:foo, :bar, :xyz]
56
+ hash_queue[:my_queue].queue_many *stuff
57
+ ```
58
+
59
+ Keys (or namespaces if you prefer) can be anything you want. Usually those will be symbols or strings but don't need to be. Objects, classes, numbers or even `true` or `nil` will work. Same applies for queued items (but beware if you don't pop with `:size` option, you logically won't be able to distinguish if `nil` you get back is `nil` you queued there or just null return value of popping from an empty queue)
60
+
61
+ `#queue` is aliased as `#enqueue`, `#push` and `#<<` for convenience. `#queue_many` is aliased as `#enqueue_many` and `#push_many` accordingly.
52
62
 
53
63
  ### Working with hash_queue as a whole
54
64
 
@@ -65,9 +75,11 @@ hash_queue.queue :favourite_rubies, 'rubinius'
65
75
  hash_queue.size # => 2
66
76
  ```
67
77
 
78
+ Also aliased as `#count` and `#length`.
79
+
68
80
  ##### empty?
69
81
 
70
- Returns `true` or `false` whether hash_queue is empty or not.
82
+ Returns `true` or `false` whether HashQueue instance is empty or not.
71
83
 
72
84
  ```ruby
73
85
  hash_queue = HashQueue.new
@@ -89,8 +89,9 @@ module HashQueue
89
89
 
90
90
  class QueueProxy
91
91
 
92
- [ :queue, :enqueue, :push, :<<, :pop, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
93
- :locked?, :unlock_all, :count_locks, :locks_count].each do |m|
92
+ [ :queue, :enqueue, :push, :<<, :queue_many, :enqueue_many, :push_many,
93
+ :pop, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
94
+ :locked?, :unlock_all, :count_locks, :locks_count].each do |m|
94
95
  define_method m do |*args|
95
96
  subject.send m, *args
96
97
  end
@@ -8,27 +8,33 @@ module HashQueue
8
8
  @mutex = Mutex.new
9
9
  @queue = []
10
10
  @locks = []
11
+ @waiting = []
11
12
  end
12
13
 
13
- def queue(obj)
14
+ def queue(*objs)
14
15
  @mutex.synchronize do
15
- @queue.push obj
16
+ @queue.concat objs
17
+
18
+ wake_waiting unless @waiting.empty?
16
19
  end
17
20
  end
18
21
  alias_method :enqueue, :queue
19
22
  alias_method :push, :queue
20
23
  alias_method :<<, :queue
24
+ alias_method :queue_many, :queue
25
+ alias_method :enqueue_many, :queue
26
+ alias_method :push_many, :queue
21
27
 
22
28
  def pop(options = {}, results = [])
23
- if options[:blocking]
24
- loop do
25
- result = _pop(options, results)
26
-
27
- return result unless result.nil? or result == []
28
- sleep 0.01
29
+ @mutex.synchronize do
30
+ loop do
31
+ if options[:blocking] and _empty?
32
+ @waiting.push Thread.current
33
+ @mutex.sleep
34
+ else
35
+ return _pop(options,results)
36
+ end
29
37
  end
30
- else
31
- _pop(options,results)
32
38
  end
33
39
  end
34
40
  alias_method :shift, :pop
@@ -56,34 +62,38 @@ module HashQueue
56
62
  private
57
63
 
58
64
  def _pop(options,results)
59
- @mutex.synchronize do
60
- size = options.fetch(:size, 1)
61
-
62
- if _locked? and _count_locks >= size
63
- if options.key? :size
64
- return []
65
- else
66
- return nil
67
- end
68
- end
69
-
70
- (size - _count_locks).times do
71
- break if _empty?
72
- results.push @queue.shift
73
- _lock if options[:lock]
74
- end
75
-
76
- if options.key? :size
77
- results
65
+ size = options.fetch(:size, 1)
66
+
67
+ if _locked? and _count_locks >= size
68
+ if options.key? :size
69
+ return []
78
70
  else
79
- results[0]
71
+ return nil
80
72
  end
81
- end
73
+ end
74
+
75
+ (size - _count_locks).times do
76
+ break if _empty?
77
+ results.push @queue.shift
78
+ _lock if options[:lock]
79
+ end
80
+
81
+ if options.key? :size
82
+ results
83
+ else
84
+ results[0]
85
+ end
82
86
  end
83
87
 
84
88
  def _empty?
85
89
  @queue.empty?
86
90
  end
91
+
92
+ def wake_waiting
93
+ @waiting.shift.wakeup
94
+ rescue ThreadError
95
+ retry
96
+ end
87
97
 
88
98
  end
89
99
  end
@@ -1,3 +1,3 @@
1
1
  module HashQueue
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- #require 'timeout'
3
2
 
4
3
  describe HashQueue do
5
4
  it 'should be able to create new HashQueue instance' do
@@ -159,6 +158,15 @@ describe HashQueue::Hash do
159
158
  @hash_queue.pop(blocking: true).wont_be_empty
160
159
  end
161
160
 
161
+ it 'should work as expected when an empty array is queued' do
162
+ Thread.new {
163
+ sleep 0.5
164
+ @hash_queue[:foo].queue []
165
+ }
166
+
167
+ Timeout::timeout(0.7) { @hash_queue.pop(blocking: true).must_equal [[]] }
168
+ end
169
+
162
170
  end
163
171
 
164
172
  end
@@ -12,6 +12,16 @@ describe HashQueue::Queue do
12
12
  @queue.size.must_equal 0
13
13
  end
14
14
 
15
+ it 'should be able to queue stuff' do
16
+ @queue.queue :foo
17
+ @queue.size.must_equal 1
18
+ end
19
+
20
+ it 'should be to queue stuff in batches' do
21
+ @queue.queue_many :foo, :bar, :xyz
22
+ @queue.size.must_equal 3
23
+ end
24
+
15
25
  end
16
26
 
17
27
  describe 'when queued stuff' do
@@ -89,6 +99,26 @@ describe HashQueue::Queue do
89
99
  @queue.pop(blocking: true, size: 1).wont_be_empty
90
100
  end
91
101
 
102
+ it 'should handle nil in queue' do
103
+ Thread.new {
104
+ sleep 0.5
105
+ @queue.queue_many nil, nil
106
+ }
107
+
108
+ Timeout::timeout(0.7) { @queue.pop(blocking: true).must_equal nil }
109
+ Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 1).must_equal [nil] }
110
+ end
111
+
112
+ it 'should handle empty array in queue' do
113
+ Thread.new {
114
+ sleep 0.5
115
+ @queue.queue_many [], []
116
+ }
117
+
118
+ Timeout::timeout(0.7) { @queue.pop(blocking: true).must_equal [] }
119
+ Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 1).must_equal [[]] }
120
+ end
121
+
92
122
  end
93
123
 
94
124
  describe 'locking' do
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'bundler/setup'
2
+ require 'timeout'
2
3
 
3
4
  require 'minitest/autorun'
4
5
  require 'turn/autorun'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-09 00:00:00.000000000 Z
12
+ date: 2012-09-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest