hash_queue 0.1.0 → 0.1.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/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