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 129 130 131 132 133 134 135 136 137 138 139
|
# frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2014-2025, by Samuel Williams.
# Copyright, 2014-2017, by Tony Arcieri.
# Copyright, 2014, by Utenmiki.
# Copyright, 2014, by Lin Jen-Shin.
# Copyright, 2017, by Vít Ondruch.
# Copyright, 2025, by Patrik Wenger.
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 = nil
@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 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.
# @param offset [Numeric] the duration to add to the timer.
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
result = @block.call(offset, self)
cancel unless recurring
result
end
alias 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
buffer = to_s[0..-2]
if @offset
delta_offset = @offset - @group.current_offset
if delta_offset > 0
buffer << " fires in #{delta_offset} seconds"
else
buffer << " fired #{delta_offset.abs} seconds ago"
end
buffer << ", recurs every #{interval}" if recurring
end
buffer << ">"
return buffer
end
end
end
|