File: timer.rb

package info (click to toggle)
ruby-timers 4.4.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 216 kB
  • sloc: ruby: 973; makefile: 6
file content (139 lines) | stat: -rw-r--r-- 3,245 bytes parent folder | download
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