Skip to content

Instantly share code, notes, and snippets.

@cheald
Last active October 26, 2015 19:44
Show Gist options
  • Save cheald/45fc72d78857a0824544 to your computer and use it in GitHub Desktop.
Save cheald/45fc72d78857a0824544 to your computer and use it in GitHub Desktop.
module Enumerable
class MaximumRuntimeExceeded < StandardError; end
class ManuallyInterrupted < StandardError; end
def with_timeout(timeout)
return to_enum(:with_timeout, timeout) unless block_given?
max_time = Time.now + timeout
each do |elem|
raise MaximumRuntimeExceeded if Time.now > max_time
raise ManuallyInterrupted if Enumerable.interrupt_enumerables?
yield elem
end
end
def self.interrupt_enumerables?
@interrupt_enumerables
end
def self.interrupt_enumerables!
@interrupt_enumerables = true
end
def self.allow_enumerables!
@interrupt_enumerables = false
end
end
describe "Enumerable" do
it "imposes a maximum runtime" do
expect { [1, 2, 3, 4].each.with_timeout(0.1) {|v| sleep 1 } }.to raise_error(Enumerable::MaximumRuntimeExceeded)
end
it "can interrupt running loops" do
expect { [1, 2].each.with_timeout(10) {|v| Enumerable.interrupt_enumerables! } }.to raise_error(Enumerable::ManuallyInterrupted)
Enumerable.allow_enumerables!
end
it "doesn't change enumerable functionality" do
expect( [1, 2, 3, 4].map.with_timeout(10) {|v| v * 2 }.to_a ).to eq([2, 4, 6, 8])
end
it "returns an enumerable if given without a block" do
expect( [1, 2, 3, 4].map.with_timeout(10) ).to be_a Enumerable
end
it "is chainable with other enumerables" do
expect([1, 2, 3, 4].map.with_timeout(10).with_index {|v, i| v + i }).to eq([1, 3, 5, 7])
end
it "works with loops" do
expect {
loop.with_timeout(0.1) do
sleep 1
end
}.to raise_error Enumerable::MaximumRuntimeExceeded
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment