1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
module Timers
# An individual timer set to fire a given proc at a given time. A timer is
# always connected to a Timer::Group but it would ONLY be in @group.timers
# if it also has a @handle specified. Otherwise it is either PAUSED or has
# been FIRED and is not recurring. You can manually enter this state by
# calling #cancel and resume normal operation by calling #reset.
class Timer
include Comparable
attr_reader :interval, :offset, :recurring
def initialize(group, interval, recurring = false, offset = nil, &block)
@group = group
@interval = interval
@recurring = recurring
@block = block
@offset = offset
@handle = nil
# If a start offset was supplied, use that, otherwise use the current timers offset.
reset(@offset || @group.current_offset)
end
def paused?
@group.paused_timers.include? self
end
def pause
return if paused?
@group.timers.delete self
@group.paused_timers.add self
@handle.cancel! if @handle
@handle = nil
end
def resume
return unless paused?
@group.paused_timers.delete self
# This will add us back to the group:
reset
end
alias_method :continue, :resume
# Extend this timer
def delay(seconds)
@handle.cancel! if @handle
@offset += seconds
@handle = @group.events.schedule(@offset, self)
end
# Cancel this timer. Do not call while paused.
def cancel
return unless @handle
@handle.cancel! if @handle
@handle = nil
# This timer is no longer valid:
@group.timers.delete self if @group
end
# Reset this timer. Do not call while paused.
def reset(offset = @group.current_offset)
# This logic allows us to minimise the interaction with @group.timers.
# A timer with a handle is always registered with the group.
if @handle
@handle.cancel!
else
@group.timers << self
end
@offset = Float(offset) + @interval
@handle = @group.events.schedule(@offset, self)
end
# Fire the block.
def fire(offset = @group.current_offset)
if recurring == :strict
# ... make the next interval strictly the last offset + the interval:
reset(@offset)
elsif recurring
reset(offset)
else
@offset = offset
end
@block.call(offset)
cancel unless recurring
end
alias_method :call, :fire
# Number of seconds until next fire / since last fire
def fires_in
@offset - @group.current_offset if @offset
end
# Inspect a timer
def inspect
str = "#<Timers::Timer:#{object_id.to_s(16)} "
if @offset
if fires_in >= 0
str << "fires in #{fires_in} seconds"
else
str << "fired #{fires_in.abs} seconds ago"
end
str << ", recurs every #{interval}" if recurring
else
str << "dead"
end
str << ">"
end
end
end
|