philiprehberger-queue_stack
Thread-safe Queue and Stack with capacity limits and blocking operations
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-queue_stack"
Or install directly:
gem install philiprehberger-queue_stack
Usage
require "philiprehberger/queue_stack"
q = Philiprehberger::QueueStack::Queue.new(capacity: 100)
q.enqueue('task')
item = q.dequeue # => 'task'
Queue (FIFO)
q = Philiprehberger::QueueStack::Queue.new(capacity: 10)
q.enqueue('first')
q.enqueue('second')
q.enqueue('third')
q.peek # => 'first'
q.peek_at(1) # => 'second'
q.peek_at(-1) # => 'third'
q.peek_at(99) # => nil (out of range)
q.dequeue # => 'first'
q.size # => 2
Stack (LIFO)
s = Philiprehberger::QueueStack::Stack.new(capacity: 10)
s.push('first')
s.push('second')
s.pop # => 'second'
s.peek # => 'first'
s.size # => 1
Blocking with Timeout
q = Philiprehberger::QueueStack::Queue.new
item = q.try_dequeue(timeout: 5) # waits up to 5 seconds
s = Philiprehberger::QueueStack::Stack.new
item = s.try_pop(timeout: 5) # waits up to 5 seconds
Conditional Removal
Remove the front/top item only when a predicate holds. Non-blocking; returns nil if the collection is empty or the block returns false (the item stays put).
q = Philiprehberger::QueueStack::Queue.new
q.enqueue({ priority: 10 })
q.enqueue({ priority: 2 })
# Only take high-priority work
q.dequeue_if { |job| job[:priority] >= 5 } # => { priority: 10 }
q.dequeue_if { |job| job[:priority] >= 5 } # => nil (head priority 2 left intact)
s = Philiprehberger::QueueStack::Stack.new
s.push(:ready)
s.pop_if { |item| item == :ready } # => :ready
s.pop_if { |item| item == :ready } # => nil (empty)
Drain
q = Philiprehberger::QueueStack::Queue.new
q.enqueue('a')
q.enqueue('b')
q.enqueue('c')
q.drain # => ['a', 'b', 'c'] (queue is now empty)
s = Philiprehberger::QueueStack::Stack.new
s.push('a')
s.push('b')
s.push('c')
s.drain # => ['c', 'b', 'a'] (stack is now empty)
Iteration
q = Philiprehberger::QueueStack::Queue.new
q.enqueue('a')
q.enqueue('b')
q.each { |item| puts item } # prints 'a', 'b'
q.to_a # => ['a', 'b'] (queue unchanged)
Non-Blocking Insertion
q = Philiprehberger::QueueStack::Queue.new(capacity: 1)
q.enqueue('a')
q.try_enqueue('b') # => false (full, no wait)
q.try_enqueue('b', timeout: 0.5) # => false after waiting up to 0.5s
s = Philiprehberger::QueueStack::Stack.new(capacity: 1)
s.push('a')
s.try_push('b') # => false (full, no wait)
Clear
q = Philiprehberger::QueueStack::Queue.new
q.enqueue('a'); q.enqueue('b')
q.clear
q.empty? # => true
Close / Shutdown
q = Philiprehberger::QueueStack::Queue.new
q.enqueue('a')
q.close
q.closed? # => true
q.dequeue # => 'a'
q.dequeue # => nil (closed and empty)
q.enqueue('b') # raises Philiprehberger::QueueStack::ClosedError
Capacity Limits
q = Philiprehberger::QueueStack::Queue.new(capacity: 3)
q.full? # => false
3.times { |i| q.enqueue(i) }
q.full? # => true
# enqueue blocks until space is available
API
Queue
| Method | Description |
|---|---|
.new(capacity:) |
Create a queue with optional capacity limit |
#enqueue(item) |
Add item to back (blocks if full) |
#try_enqueue(item, timeout: nil) |
Non-blocking enqueue, returns true/false (waits up to timeout if given) |
#dequeue |
Remove and return front item (blocks if empty) |
| `#dequeue_if { \ | item\ |
#try_dequeue(timeout:) |
Dequeue with timeout, returns nil on timeout |
#clear |
Remove all items without returning them |
#peek |
View front item without removing |
#peek_at(index) |
View item at the given position (supports negative indices, returns nil if out of range) |
#drain |
Remove and return all items as array (FIFO order) |
#each |
Iterate items without removing (returns Enumerator if no block) |
#to_a |
Snapshot as array (FIFO order) |
#close |
Mark as closed (new enqueues raise ClosedError) |
#closed? |
Whether the queue has been closed |
#size |
Number of items |
#empty? |
Whether the queue is empty |
#full? |
Whether the queue is at capacity |
Stack
| Method | Description |
|---|---|
.new(capacity:) |
Create a stack with optional capacity limit |
#push(item) |
Push item on top (blocks if full) |
#try_push(item, timeout: nil) |
Non-blocking push, returns true/false (waits up to timeout if given) |
#pop |
Remove and return top item (blocks if empty) |
| `#pop_if { \ | item\ |
#try_pop(timeout:) |
Pop with timeout, returns nil on timeout |
#clear |
Remove all items without returning them |
#peek |
View top item without removing |
#drain |
Remove and return all items as array (LIFO order) |
#each |
Iterate items without removing (returns Enumerator if no block) |
#to_a |
Snapshot as array (LIFO order) |
#close |
Mark as closed (new pushes raise ClosedError) |
#closed? |
Whether the stack has been closed |
#size |
Number of items |
#empty? |
Whether the stack is empty |
#full? |
Whether the stack is at capacity |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: