hash_queue 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -22,7 +22,7 @@ For those living on the edge:
22
22
 
23
23
  ..with Bundler 1.1 or newer:
24
24
 
25
- gem 'mimi', github: 'mikekreeki/hash_queue'
25
+ gem 'hash_queue', github: 'mikekreeki/hash_queue'
26
26
 
27
27
  ## Usage
28
28
 
@@ -187,7 +187,30 @@ hash_queue[:foo].pop # => #<Object:0x000001008d4658>
187
187
  hash_queue[:foo].pop(size: 2) # => [#<Object:0x000001008ae228>, #<Object:0x000001008ae200>]
188
188
  ```
189
189
 
190
- You can use same options as mentioned above.
190
+ You can use same options as mentioned above plus you can pass in a block into play. Block passed to `HashQueue::Queue#pop` yields items that otherwise would be directly returned, e.g. popped from the queue, and method actually pops values only if return value of a block evaluates to true.
191
+
192
+ ```ruby
193
+ food = HashQueue::Queue.new
194
+ food.push :carrot
195
+
196
+ food.pop do |snack|
197
+ current_user.favors? snack
198
+ end
199
+ ```
200
+
201
+ ##### peek
202
+
203
+ Use `#peek` to look what is in the head of the queue but without removing it from there.
204
+
205
+ ```ruby
206
+ queue = HashQueue::Queue.new
207
+ queue.push :foo
208
+
209
+ queue.peek # => :foo
210
+ queue.size # => 1
211
+ ```
212
+
213
+ You can peek on more items in the head of the queue specifying `:size` option. Be aware, using `#peek` your code might be subject to race condition. If you want to pop only when some condition is met, use `#pop` with a block mentioned above.
191
214
 
192
215
  ### Locking capabilities
193
216
 
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/testtask'
8
8
  Rake::TestTask.new do |t|
9
9
  t.libs << 'spec'
10
10
  t.pattern = 'spec/**/*_spec.rb'
11
- t.verbose = true
11
+ t.verbose = false
12
12
  end
13
13
 
14
14
  desc "Run tests"
data/hash_queue.gemspec CHANGED
@@ -9,8 +9,7 @@ Gem::Specification.new do |gem|
9
9
  gem.homepage = "http://github.com/mikekreeki/hash_queue"
10
10
 
11
11
  gem.add_development_dependency('minitest')
12
- gem.add_development_dependency('turn')
13
- gem.add_development_dependency('term-ansicolor')
12
+ gem.add_development_dependency('minitest-reporters')
14
13
 
15
14
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
15
  gem.files = `git ls-files`.split("\n")
@@ -90,7 +90,7 @@ module HashQueue
90
90
  class QueueProxy
91
91
 
92
92
  [ :queue, :enqueue, :push, :<<, :queue_many, :enqueue_many, :push_many,
93
- :pop, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
93
+ :pop, :peek, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
94
94
  :locked?, :unlock_all, :count_locks, :locks_count].each do |m|
95
95
  define_method m do |*args|
96
96
  subject.send m, *args
@@ -1,4 +1,5 @@
1
1
  require 'hash_queue/queue/lockable'
2
+ require 'ostruct'
2
3
 
3
4
  module HashQueue
4
5
  class Queue
@@ -15,7 +16,7 @@ module HashQueue
15
16
  @mutex.synchronize do
16
17
  @queue.concat objs
17
18
 
18
- wake_waiting unless @waiting.empty?
19
+ wake_waiting
19
20
  end
20
21
  end
21
22
  alias_method :enqueue, :queue
@@ -28,17 +29,33 @@ module HashQueue
28
29
  def pop(options = {}, results = [])
29
30
  @mutex.synchronize do
30
31
  loop do
31
- if options[:blocking] and _empty?
32
- @waiting.push Thread.current
32
+ if options[:blocking] and not can_pop?(options[:size] || 1)
33
+ @waiting.push OpenStruct.new(thread: Thread.current, pop_size: options[:size] || 1)
33
34
  @mutex.sleep
34
35
  else
35
- return _pop(options,results)
36
+ if block_given?
37
+ should_pop = yield _peek(options)
38
+
39
+ if should_pop
40
+ return _pop(options,results)
41
+ else
42
+ return
43
+ end
44
+ else
45
+ return _pop(options,results)
46
+ end
36
47
  end
37
48
  end
38
49
  end
39
50
  end
40
51
  alias_method :shift, :pop
41
52
 
53
+ def peek(options = {})
54
+ @mutex.synchronize do
55
+ _peek(options)
56
+ end
57
+ end
58
+
42
59
  def size
43
60
  @mutex.synchronize do
44
61
  @queue.size
@@ -64,7 +81,7 @@ module HashQueue
64
81
  def _pop(options,results)
65
82
  size = options.fetch(:size, 1)
66
83
 
67
- if _locked? and _count_locks >= size
84
+ unless can_pop?(size)
68
85
  if options.key? :size
69
86
  return []
70
87
  else
@@ -85,14 +102,34 @@ module HashQueue
85
102
  end
86
103
  end
87
104
 
105
+ def _peek(options)
106
+ if options.has_key? :size
107
+ @queue[0..options[:size]]
108
+ else
109
+ @queue[0]
110
+ end
111
+ end
112
+
88
113
  def _empty?
89
114
  @queue.empty?
90
115
  end
91
116
 
92
117
  def wake_waiting
93
- @waiting.shift.wakeup
94
- rescue ThreadError
95
- retry
118
+ unless @waiting.empty?
119
+ while @waiting[0] and can_pop? @waiting[0].pop_size
120
+ begin
121
+ @waiting.shift.thread.wakeup
122
+ rescue ThreadError
123
+ retry
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def can_pop?(size)
130
+ size = @queue.size if size > @queue.size
131
+
132
+ (not _empty?) and (size > _count_locks)
96
133
  end
97
134
 
98
135
  end
@@ -10,6 +10,8 @@ module HashQueue
10
10
  def unlock(n = 1)
11
11
  @mutex.synchronize do
12
12
  @locks.shift(n)
13
+
14
+ wake_waiting
13
15
  end
14
16
  end
15
17
 
@@ -22,6 +24,8 @@ module HashQueue
22
24
  def unlock_all
23
25
  @mutex.synchronize do
24
26
  @locks.clear
27
+
28
+ wake_waiting
25
29
  end
26
30
  end
27
31
 
@@ -1,3 +1,3 @@
1
1
  module HashQueue
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -62,6 +62,35 @@ describe HashQueue::Queue do
62
62
  @queue.clear
63
63
  @queue.pop(size: 1).must_equal []
64
64
  end
65
+
66
+ it 'should yeild items in the block passed to #pop' do
67
+ @queue.pop { |item| item.must_equal 1 }
68
+ @queue.pop(size: 1) { |items| items.must_equal [2] }
69
+ end
70
+
71
+ it 'should pop when block passed to #pop evaluates to truthy value' do
72
+ item = @queue.pop do
73
+ true
74
+ end
75
+
76
+ item.must_equal 1
77
+ end
78
+
79
+ it 'shouldnt pop when block passed to #pop evaluates to falsy value' do
80
+ item = @queue.pop {}
81
+
82
+ item.must_be_nil
83
+ end
84
+
85
+ it 'should be able to peek in the queue' do
86
+ @queue.peek.must_equal 1
87
+ @queue.size.must_equal 2
88
+ end
89
+
90
+ it 'should be able to peek on batch of items' do
91
+ @queue.peek(size: 2).must_equal [1,2]
92
+ @queue.size.must_equal 2
93
+ end
65
94
  end
66
95
 
67
96
  describe 'when we queue weird stuff' do
@@ -99,6 +128,43 @@ describe HashQueue::Queue do
99
128
  @queue.pop(blocking: true, size: 1).wont_be_empty
100
129
  end
101
130
 
131
+ it 'should return appropriate number of items when locked and size is specified' do
132
+ @queue.lock 1
133
+
134
+ Thread.new {
135
+ sleep 0.5
136
+ @queue.queue :foo
137
+ @queue.queue :bar
138
+ }
139
+
140
+ Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 2).must_equal [:foo] }
141
+ end
142
+
143
+ it 'should wait until queue is unlocked' do
144
+ @queue.push :foo, :bar, :xyz
145
+ @queue.lock 3
146
+
147
+ Thread.new {
148
+ sleep 0.5
149
+ @queue.unlock
150
+ sleep 0.5
151
+ @queue.unlock_all
152
+ }
153
+
154
+ Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 3).must_equal [:foo] }
155
+ Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 3).must_equal [:bar, :xyz] }
156
+ end
157
+
158
+ it 'should wake multiple threads if necessary' do
159
+ threads = []
160
+ threads << Thread.new { sleep 0.1; @queue.pop(blocking: true).must_equal :foo }
161
+ threads << Thread.new { sleep 0.2; @queue.pop(blocking: true).must_equal :bar }
162
+
163
+ sleep 0.1
164
+ @queue.push :foo, :bar
165
+ Timeout::timeout(0.7) { threads.map(&:join) }
166
+ end
167
+
102
168
  it 'should handle nil in queue' do
103
169
  Thread.new {
104
170
  sleep 0.5
data/spec/spec_helper.rb CHANGED
@@ -2,7 +2,9 @@ require 'bundler/setup'
2
2
  require 'timeout'
3
3
 
4
4
  require 'minitest/autorun'
5
- require 'turn/autorun'
5
+ require "minitest/reporters"
6
6
 
7
7
  require 'hash_queue'
8
8
 
9
+
10
+ MiniTest::Reporters.use! MiniTest::Reporters::ProgressReporter.new
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.1
4
+ version: 0.1.2
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-10 00:00:00.000000000 Z
12
+ date: 2012-12-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -28,23 +28,7 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
30
  - !ruby/object:Gem::Dependency
31
- name: turn
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :development
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
46
- - !ruby/object:Gem::Dependency
47
- name: term-ansicolor
31
+ name: minitest-reporters
48
32
  requirement: !ruby/object:Gem::Requirement
49
33
  none: false
50
34
  requirements: