timers 4.3.1 → 4.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f9fdee45ad2b102af90e2d529a1384a5f6b975d6ec3112dfafebac2d8574b0f
4
- data.tar.gz: 5bec94f4fb24de10acaf4fba0e93514aa2912444f19188e6ecd1c6b6e3d25340
3
+ metadata.gz: 6aaa0d481ea83db607add1809f3f5d96b085bddf5bda6fa17165c18316c4c527
4
+ data.tar.gz: 8995d8268bd1c49b047e0fe0d1bf252d8c943962d05eee4026601b952cacc339
5
5
  SHA512:
6
- metadata.gz: 712db02d6c449fca924459a952df2c9ae21058b7c492ce81d4b86f4989c69aa9ef90cc98ae421ab2f13934da6488d7d91a5718c2f1070714476147302c3d9b6e
7
- data.tar.gz: c4a358d3bdc7e108afd016e42fdb478b8e717e3c400ae6cf0694e880b91112c9b118e7609d1c31143247d5d87db2577851b1e6002f2731e524a40d7e96c1cc28
6
+ metadata.gz: 3d5a69bc7f7e32e02365476d9a25e97dbe72c572497874356603554ecc28f5ee88821af1a2bfd4239a232f0d7a9ef0048c45a1909eefa7faae3619c51c3ec87a
7
+ data.tar.gz: 6521553ef5271c9191aa0dc4f23a65c3eb776293273784e46597bda0a92ae0d97aa915e4f9c25869b8923f516fe23ed2bf389abf34e1cb76ac6f5484d6d927da
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,4 @@
1
+ @BC�S���rr�H�rW
2
+ ���۳�Jc�ݠb�1ɥY���(���.W����Li����"^�'���&��.����y�2Sk\Ї7�?.q(IK ��(��?�:�s���A�(L������f8)��(0�[�
3
+ RʞY��s����`�L[�D*t��R} MG�?�����l�Jƒ���ߩTu���mx��G��
4
+ �W8��� u��4ټn_�D|N���[����m�1��l�U�a�Y�N.�?' 0fTtH��_r邧��i�KW�^��س�ڲN�R�o�2��~e$'��=��%���@�� H��5ŭ#�
data/lib/timers/events.rb CHANGED
@@ -1,143 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2014-2022, by Samuel Williams.
5
+ # Copyright, 2014-2016, by Tony Arcieri.
6
+ # Copyright, 2014, by Lavir the Whiolet.
7
+ # Copyright, 2015, by Utenmiki.
8
+ # Copyright, 2015, by Donovan Keme.
9
+ # Copyright, 2021, by Wander Hillen.
22
10
 
23
11
  require_relative "timer"
12
+ require_relative "priority_heap"
24
13
 
25
14
  module Timers
26
- # Maintains an ordered list of events, which can be cancelled.
15
+ # Maintains a PriorityHeap of events ordered on time, which can be cancelled.
27
16
  class Events
28
17
  # Represents a cancellable handle for a specific timer event.
29
18
  class Handle
19
+ include Comparable
20
+
30
21
  def initialize(time, callback)
31
22
  @time = time
32
23
  @callback = callback
33
24
  end
34
-
25
+
35
26
  # The absolute time that the handle should be fired at.
36
27
  attr_reader :time
37
-
28
+
38
29
  # Cancel this timer, O(1).
39
30
  def cancel!
40
31
  # The simplest way to keep track of cancelled status is to nullify the
41
32
  # callback. This should also be optimal for garbage collection.
42
33
  @callback = nil
43
34
  end
44
-
35
+
45
36
  # Has this timer been cancelled? Cancelled timer's don't fire.
46
37
  def cancelled?
47
38
  @callback.nil?
48
39
  end
49
-
50
- def > other
51
- @time > other.to_f
52
- end
53
-
54
- def >= other
55
- @time >= other.to_f
56
- end
57
-
58
- def to_f
59
- @time
40
+
41
+ def <=> other
42
+ @time <=> other.time
60
43
  end
61
-
44
+
62
45
  # Fire the callback if not cancelled with the given time parameter.
63
46
  def fire(time)
64
47
  @callback.call(time) if @callback
65
48
  end
66
49
  end
67
-
50
+
68
51
  def initialize
69
52
  # A sequence of handles, maintained in sorted order, future to present.
70
53
  # @sequence.last is the next event to be fired.
71
- @sequence = []
54
+ @sequence = PriorityHeap.new
72
55
  @queue = []
73
56
  end
74
-
57
+
75
58
  # Add an event at the given time.
76
59
  def schedule(time, callback)
60
+ flush!
61
+
77
62
  handle = Handle.new(time.to_f, callback)
78
63
 
79
64
  @queue << handle
80
65
 
81
66
  return handle
82
67
  end
83
-
68
+
84
69
  # Returns the first non-cancelled handle.
85
70
  def first
86
71
  merge!
87
72
 
88
- while (handle = @sequence.last)
73
+ while (handle = @sequence.peek)
89
74
  return handle unless handle.cancelled?
90
75
  @sequence.pop
91
76
  end
92
77
  end
93
-
78
+
94
79
  # Returns the number of pending (possibly cancelled) events.
95
80
  def size
96
81
  @sequence.size + @queue.size
97
82
  end
98
-
83
+
99
84
  # Fire all handles for which Handle#time is less than the given time.
100
85
  def fire(time)
101
86
  merge!
102
87
 
103
- while handle = @sequence.last and handle.time <= time
88
+ while handle = @sequence.peek and handle.time <= time
104
89
  @sequence.pop
105
90
  handle.fire(time)
106
91
  end
107
92
  end
108
-
93
+
109
94
  private
110
-
95
+
96
+ # Move all non-cancelled timers from the pending queue to the priority heap
111
97
  def merge!
112
98
  while handle = @queue.pop
113
99
  next if handle.cancelled?
114
100
 
115
- index = bisect_right(@sequence, handle)
116
-
117
- if current_handle = @sequence[index] and current_handle.cancelled?
118
- # puts "Replacing handle at index: #{index} due to cancellation in array containing #{@sequence.size} item(s)."
119
- @sequence[index] = handle
120
- else
121
- # puts "Inserting handle at index: #{index} in array containing #{@sequence.size} item(s)."
122
- @sequence.insert(index, handle)
123
- end
101
+ @sequence.push(handle)
124
102
  end
125
103
  end
126
-
127
- # Return the right-most index where to insert item e, in a list a, assuming
128
- # a is sorted in descending order.
129
- def bisect_right(a, e, l = 0, u = a.length)
130
- while l < u
131
- m = l + (u - l).div(2)
132
-
133
- if a[m] >= e
134
- l = m + 1
135
- else
136
- u = m
137
- end
104
+
105
+ def flush!
106
+ while @queue.last&.cancelled?
107
+ @queue.pop
138
108
  end
139
-
140
- l
141
109
  end
142
110
  end
143
111
  end
data/lib/timers/group.rb CHANGED
@@ -1,24 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2014-2022, by Samuel Williams.
5
+ # Copyright, 2014-2016, by Tony Arcieri.
6
+ # Copyright, 2015, by Donovan Keme.
7
+ # Copyright, 2015, by Tommy Ong Gia Phu.
22
8
 
23
9
  require "set"
24
10
  require "forwardable"
@@ -31,62 +17,62 @@ module Timers
31
17
  # A collection of timers which may fire at different times
32
18
  class Group
33
19
  include Enumerable
34
-
20
+
35
21
  extend Forwardable
36
22
  def_delegators :@timers, :each, :empty?
37
-
23
+
38
24
  def initialize
39
25
  @events = Events.new
40
-
26
+
41
27
  @timers = Set.new
42
28
  @paused_timers = Set.new
43
-
29
+
44
30
  @interval = Interval.new
45
31
  @interval.start
46
32
  end
47
-
33
+
48
34
  # Scheduled events:
49
35
  attr_reader :events
50
-
36
+
51
37
  # Active timers:
52
38
  attr_reader :timers
53
-
39
+
54
40
  # Paused timers:
55
41
  attr_reader :paused_timers
56
-
42
+
57
43
  # Call the given block after the given interval. The first argument will be
58
44
  # the time at which the group was asked to fire timers for.
59
45
  def after(interval, &block)
60
46
  Timer.new(self, interval, false, &block)
61
47
  end
62
-
48
+
63
49
  # Call the given block immediately, and then after the given interval. The first
64
50
  # argument will be the time at which the group was asked to fire timers for.
65
51
  def now_and_after(interval, &block)
66
52
  yield
67
53
  after(interval, &block)
68
54
  end
69
-
55
+
70
56
  # Call the given block periodically at the given interval. The first
71
57
  # argument will be the time at which the group was asked to fire timers for.
72
58
  def every(interval, recur = true, &block)
73
59
  Timer.new(self, interval, recur, &block)
74
60
  end
75
-
61
+
76
62
  # Call the given block immediately, and then periodically at the given interval. The first
77
63
  # argument will be the time at which the group was asked to fire timers for.
78
64
  def now_and_every(interval, recur = true, &block)
79
65
  yield
80
66
  every(interval, recur, &block)
81
67
  end
82
-
68
+
83
69
  # Wait for the next timer and fire it. Can take a block, which should behave
84
70
  # like sleep(n), except that n may be nil (sleep forever) or a negative
85
71
  # number (fire immediately after return).
86
72
  def wait
87
73
  if block_given?
88
74
  yield wait_interval
89
-
75
+
90
76
  while (interval = wait_interval) && interval > 0
91
77
  yield interval
92
78
  end
@@ -99,7 +85,7 @@ module Timers
99
85
 
100
86
  fire
101
87
  end
102
-
88
+
103
89
  # Interval to wait until when the next timer will fire.
104
90
  # - nil: no timers
105
91
  # - -ve: timers expired already
@@ -109,36 +95,36 @@ module Timers
109
95
  handle = @events.first
110
96
  handle.time - Float(offset) if handle
111
97
  end
112
-
98
+
113
99
  # Fire all timers that are ready.
114
100
  def fire(offset = current_offset)
115
101
  @events.fire(offset)
116
102
  end
117
-
103
+
118
104
  # Pause all timers.
119
105
  def pause
120
106
  @timers.dup.each(&:pause)
121
107
  end
122
-
108
+
123
109
  # Resume all timers.
124
110
  def resume
125
111
  @paused_timers.dup.each(&:resume)
126
112
  end
127
-
113
+
128
114
  alias continue resume
129
-
115
+
130
116
  # Delay all timers.
131
117
  def delay(seconds)
132
118
  @timers.each do |timer|
133
119
  timer.delay(seconds)
134
120
  end
135
121
  end
136
-
122
+
137
123
  # Cancel all timers.
138
124
  def cancel
139
125
  @timers.dup.each(&:cancel)
140
126
  end
141
-
127
+
142
128
  # The group's current time.
143
129
  def current_offset
144
130
  @interval.to_f
@@ -1,24 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2018-2022, by Samuel Williams.
22
5
 
23
6
  module Timers
24
7
  # A collection of timers which may fire at different times
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2021, by Wander Hillen.
5
+ # Copyright, 2021-2022, by Samuel Williams.
6
+
7
+ module Timers
8
+ # A priority queue implementation using a standard binary minheap. It uses straight comparison
9
+ # of its contents to determine priority. This works because a Handle from Timers::Events implements
10
+ # the '<' operation by comparing the expiry time.
11
+ # See <https://en.wikipedia.org/wiki/Binary_heap> for explanations of the main methods.
12
+ class PriorityHeap
13
+ def initialize
14
+ # The heap is represented with an array containing a binary tree. See
15
+ # https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
16
+ # is built up.
17
+ @contents = []
18
+ end
19
+
20
+ # Returns the earliest timer or nil if the heap is empty.
21
+ def peek
22
+ @contents[0]
23
+ end
24
+
25
+ # Returns the number of elements in the heap
26
+ def size
27
+ @contents.size
28
+ end
29
+
30
+ # Returns the earliest timer if the heap is non-empty and removes it from the heap.
31
+ # Returns nil if the heap is empty. (and doesn't change the heap in that case)
32
+ def pop
33
+ # If the heap is empty:
34
+ if @contents.empty?
35
+ return nil
36
+ end
37
+
38
+ # If we have only one item, no swapping is required:
39
+ if @contents.size == 1
40
+ return @contents.pop
41
+ end
42
+
43
+ # Take the root of the tree:
44
+ value = @contents[0]
45
+
46
+ # Remove the last item in the tree:
47
+ last = @contents.pop
48
+
49
+ # Overwrite the root of the tree with the item:
50
+ @contents[0] = last
51
+
52
+ # Bubble it down into place:
53
+ bubble_down(0)
54
+
55
+ # validate!
56
+
57
+ return value
58
+ end
59
+
60
+ # Inserts a new timer into the heap, then rearranges elements until the heap invariant is true again.
61
+ def push(element)
62
+ # Insert the item at the end of the heap:
63
+ @contents.push(element)
64
+
65
+ # Bubble it up into position:
66
+ bubble_up(@contents.size - 1)
67
+
68
+ # validate!
69
+
70
+ return self
71
+ end
72
+
73
+ # Empties out the heap, discarding all elements
74
+ def clear!
75
+ @contents = []
76
+ end
77
+
78
+ # Validate the heap invariant. Every element except the root must not be smaller than
79
+ # its parent element. Note that it MAY be equal.
80
+ def valid?
81
+ # notice we skip index 0 on purpose, because it has no parent
82
+ (1..(@contents.size - 1)).all? { |e| @contents[e] >= @contents[(e - 1) / 2] }
83
+ end
84
+
85
+ private
86
+
87
+ def swap(i, j)
88
+ @contents[i], @contents[j] = @contents[j], @contents[i]
89
+ end
90
+
91
+ def bubble_up(index)
92
+ parent_index = (index - 1) / 2 # watch out, integer division!
93
+
94
+ while index > 0 && @contents[index] < @contents[parent_index]
95
+ # if the node has a smaller value than its parent, swap these nodes
96
+ # to uphold the minheap invariant and update the index of the 'current'
97
+ # node. If the node is already at index 0, we can also stop because that
98
+ # is the root of the heap.
99
+ # swap(index, parent_index)
100
+ @contents[index], @contents[parent_index] = @contents[parent_index], @contents[index]
101
+
102
+ index = parent_index
103
+ parent_index = (index - 1) / 2 # watch out, integer division!
104
+ end
105
+ end
106
+
107
+ def bubble_down(index)
108
+ swap_value = 0
109
+ swap_index = nil
110
+
111
+ while true
112
+ left_index = (2 * index) + 1
113
+ left_value = @contents[left_index]
114
+
115
+ if left_value.nil?
116
+ # This node has no children so it can't bubble down any further.
117
+ # We're done here!
118
+ return
119
+ end
120
+
121
+ # Determine which of the child nodes has the smallest value:
122
+ right_index = left_index + 1
123
+ right_value = @contents[right_index]
124
+
125
+ if right_value.nil? or right_value > left_value
126
+ swap_value = left_value
127
+ swap_index = left_index
128
+ else
129
+ swap_value = right_value
130
+ swap_index = right_index
131
+ end
132
+
133
+ if @contents[index] < swap_value
134
+ # No need to swap, the minheap invariant is already satisfied:
135
+ return
136
+ else
137
+ # At least one of the child node has a smaller value than the current node, swap current node with that child and update current node for if it might need to bubble down even further:
138
+ # swap(index, swap_index)
139
+ @contents[index], @contents[swap_index] = @contents[swap_index], @contents[index]
140
+
141
+ index = swap_index
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
data/lib/timers/timer.rb CHANGED
@@ -1,24 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2014-2022, by Samuel Williams.
5
+ # Copyright, 2014-2017, by Tony Arcieri.
6
+ # Copyright, 2014, by Utenmiki.
7
+ # Copyright, 2014, by Lin Jen-Shin.
8
+ # Copyright, 2017, by Vít Ondruch.
22
9
 
23
10
  module Timers
24
11
  # An individual timer set to fire a given proc at a given time. A timer is
@@ -29,66 +16,66 @@ module Timers
29
16
  class Timer
30
17
  include Comparable
31
18
  attr_reader :interval, :offset, :recurring
32
-
19
+
33
20
  def initialize(group, interval, recurring = false, offset = nil, &block)
34
21
  @group = group
35
-
22
+
36
23
  @interval = interval
37
24
  @recurring = recurring
38
25
  @block = block
39
26
  @offset = offset
40
-
27
+
41
28
  @handle = nil
42
-
29
+
43
30
  # If a start offset was supplied, use that, otherwise use the current timers offset.
44
31
  reset(@offset || @group.current_offset)
45
32
  end
46
-
33
+
47
34
  def paused?
48
35
  @group.paused_timers.include? self
49
36
  end
50
-
37
+
51
38
  def pause
52
39
  return if paused?
53
-
40
+
54
41
  @group.timers.delete self
55
42
  @group.paused_timers.add self
56
-
43
+
57
44
  @handle.cancel! if @handle
58
45
  @handle = nil
59
46
  end
60
-
47
+
61
48
  def resume
62
49
  return unless paused?
63
-
50
+
64
51
  @group.paused_timers.delete self
65
-
52
+
66
53
  # This will add us back to the group:
67
54
  reset
68
55
  end
69
-
56
+
70
57
  alias continue resume
71
-
58
+
72
59
  # Extend this timer
73
60
  def delay(seconds)
74
61
  @handle.cancel! if @handle
75
-
62
+
76
63
  @offset += seconds
77
-
64
+
78
65
  @handle = @group.events.schedule(@offset, self)
79
66
  end
80
-
67
+
81
68
  # Cancel this timer. Do not call while paused.
82
69
  def cancel
83
70
  return unless @handle
84
-
71
+
85
72
  @handle.cancel! if @handle
86
73
  @handle = nil
87
-
74
+
88
75
  # This timer is no longer valid:
89
76
  @group.timers.delete self if @group
90
77
  end
91
-
78
+
92
79
  # Reset this timer. Do not call while paused.
93
80
  # @param offset [Numeric] the duration to add to the timer.
94
81
  def reset(offset = @group.current_offset)
@@ -99,12 +86,12 @@ module Timers
99
86
  else
100
87
  @group.timers << self
101
88
  end
102
-
89
+
103
90
  @offset = Float(offset) + @interval
104
-
91
+
105
92
  @handle = @group.events.schedule(@offset, self)
106
93
  end
107
-
94
+
108
95
  # Fire the block.
109
96
  def fire(offset = @group.current_offset)
110
97
  if recurring == :strict
@@ -115,36 +102,38 @@ module Timers
115
102
  else
116
103
  @offset = offset
117
104
  end
118
-
105
+
119
106
  @block.call(offset, self)
120
-
107
+
121
108
  cancel unless recurring
122
109
  end
123
-
110
+
124
111
  alias call fire
125
-
112
+
126
113
  # Number of seconds until next fire / since last fire
127
114
  def fires_in
128
115
  @offset - @group.current_offset if @offset
129
116
  end
130
-
117
+
131
118
  # Inspect a timer
132
119
  def inspect
133
- str = "#{to_s[0..-2]} ".dup
134
-
120
+ buffer = "#{to_s[0..-2]} ".dup
121
+
135
122
  if @offset
136
- str << if fires_in >= 0
137
- "fires in #{fires_in} seconds"
138
- else
139
- "fired #{fires_in.abs} seconds ago"
140
- end
141
-
142
- str << ", recurs every #{interval}" if recurring
123
+ if fires_in >= 0
124
+ buffer << "fires in #{fires_in} seconds"
125
+ else
126
+ buffer << "fired #{fires_in.abs} seconds ago"
127
+ end
128
+
129
+ buffer << ", recurs every #{interval}" if recurring
143
130
  else
144
- str << "dead"
131
+ buffer << "dead"
145
132
  end
146
-
147
- str << ">"
133
+
134
+ buffer << ">"
135
+
136
+ return buffer
148
137
  end
149
138
  end
150
139
  end
@@ -1,25 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2016, by Tony Arcieri.
5
+ # Copyright, 2014-2022, by Samuel Williams.
6
+ # Copyright, 2015, by Donovan Keme.
22
7
 
23
8
  module Timers
24
- VERSION = "4.3.1"
9
+ VERSION = "4.3.4"
25
10
  end
data/lib/timers/wait.rb CHANGED
@@ -1,24 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2014-2022, by Samuel Williams.
5
+ # Copyright, 2014-2016, by Tony Arcieri.
6
+ # Copyright, 2015, by Utenmiki.
7
+ # Copyright, 2015, by Donovan Keme.
22
8
 
23
9
  require_relative "interval"
24
10
 
@@ -28,7 +14,7 @@ module Timers
28
14
  def self.for(duration, &block)
29
15
  if duration
30
16
  timeout = new(duration)
31
-
17
+
32
18
  timeout.while_time_remaining(&block)
33
19
  else
34
20
  loop do
@@ -36,31 +22,31 @@ module Timers
36
22
  end
37
23
  end
38
24
  end
39
-
25
+
40
26
  def initialize(duration)
41
27
  @duration = duration
42
28
  @remaining = true
43
29
  end
44
-
30
+
45
31
  attr_reader :duration
46
32
  attr_reader :remaining
47
-
33
+
48
34
  # Yields while time remains for work to be done:
49
35
  def while_time_remaining
50
36
  @interval = Interval.new
51
37
  @interval.start
52
-
38
+
53
39
  yield @remaining while time_remaining?
54
40
  ensure
55
41
  @interval.stop
56
42
  @interval = nil
57
43
  end
58
-
44
+
59
45
  private
60
-
46
+
61
47
  def time_remaining?
62
48
  @remaining = (@duration - @interval.to_f)
63
-
49
+
64
50
  @remaining > 0
65
51
  end
66
52
  end
data/lib/timers.rb CHANGED
@@ -1,24 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
3
+ # Released under the MIT License.
4
+ # Copyright, 2012-2016, by Tony Arcieri.
5
+ # Copyright, 2012, by Ryan LeCompte.
6
+ # Copyright, 2012, by Nicholas Evans.
7
+ # Copyright, 2012, by Dimitrij Denissenko.
8
+ # Copyright, 2013, by Chuck Remes.
9
+ # Copyright, 2013, by Ron Evans.
10
+ # Copyright, 2013, by Sean Gregory.
11
+ # Copyright, 2013, by Utenmiki.
12
+ # Copyright, 2013, by Jeremy Hinegardner.
13
+ # Copyright, 2014, by Larry Lv.
14
+ # Copyright, 2014, by Bruno Enten.
15
+ # Copyright, 2014-2022, by Samuel Williams.
16
+ # Copyright, 2014, by Mike Bourgeous.
22
17
 
23
18
  require_relative "timers/version"
24
19
 
data/license.md ADDED
@@ -0,0 +1,48 @@
1
+ # MIT License
2
+
3
+ Copyright, 2012-2017, by Tony Arcieri.
4
+ Copyright, 2012, by Ryan LeCompte.
5
+ Copyright, 2012, by Jesse Cooke.
6
+ Copyright, 2012, by Nicholas Evans.
7
+ Copyright, 2012, by Dimitrij Denissenko.
8
+ Copyright, 2013, by Chuck Remes.
9
+ Copyright, 2013, by Ron Evans.
10
+ Copyright, 2013, by Sean Gregory.
11
+ Copyright, 2013-2015, by Utenmiki.
12
+ Copyright, 2013, by Jeremy Hinegardner.
13
+ Copyright, 2014, by Larry Lv.
14
+ Copyright, 2014, by Bruno Enten.
15
+ Copyright, 2014-2022, by Samuel Williams.
16
+ Copyright, 2014, by Mike Bourgeous.
17
+ Copyright, 2014, by Klaus Trainer.
18
+ Copyright, 2014, by Lin Jen-Shin.
19
+ Copyright, 2014, by Lavir the Whiolet.
20
+ Copyright, 2015-2016, by Donovan Keme.
21
+ Copyright, 2015, by Tommy Ong Gia Phu.
22
+ Copyright, 2015, by Will Jessop.
23
+ Copyright, 2016, by Ryunosuke SATO.
24
+ Copyright, 2016, by Atul Bhosale.
25
+ Copyright, 2016, by Feram.
26
+ Copyright, 2017, by Vít Ondruch.
27
+ Copyright, 2017-2020, by Olle Jonsson.
28
+ Copyright, 2020, by Tim Smith.
29
+ Copyright, 2021, by Wander Hillen.
30
+ Copyright, 2022, by Yoshiki Takagi.
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy
33
+ of this software and associated documentation files (the "Software"), to deal
34
+ in the Software without restriction, including without limitation the rights
35
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36
+ copies of the Software, and to permit persons to whom the Software is
37
+ furnished to do so, subject to the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be included in all
40
+ copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
48
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,110 @@
1
+ # Timers
2
+
3
+ Collections of one-shot and periodic timers, intended for use with event loops such as [async](https://github.com/socketry/async).
4
+
5
+ [![Development Status](https://github.com/socketry/timers/workflows/Test/badge.svg)](https://github.com/socketry/timers/actions?workflow=Test)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ``` ruby
12
+ gem 'timers'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install timers
22
+
23
+ ## Usage
24
+
25
+ Create a new timer group with `Timers::Group.new`:
26
+
27
+ ``` ruby
28
+ require 'timers'
29
+
30
+ timers = Timers::Group.new
31
+ ```
32
+
33
+ Schedule a proc to run after 5 seconds with `Timers::Group#after`:
34
+
35
+ ``` ruby
36
+ five_second_timer = timers.after(5) { puts "Take five" }
37
+ ```
38
+
39
+ The `five_second_timer` variable is now bound to a Timers::Timer object. To
40
+ cancel a timer, use `Timers::Timer#cancel`
41
+
42
+ Once you've scheduled a timer, you can wait until the next timer fires with `Timers::Group#wait`:
43
+
44
+ ``` ruby
45
+ # Waits 5 seconds
46
+ timers.wait
47
+
48
+ # The script will now print "Take five"
49
+ ```
50
+
51
+ You can schedule a block to run periodically with `Timers::Group#every`:
52
+
53
+ ``` ruby
54
+ every_five_seconds = timers.every(5) { puts "Another 5 seconds" }
55
+
56
+ loop { timers.wait }
57
+ ```
58
+
59
+ You can also schedule a block to run immediately and periodically with `Timers::Group#now_and_every`:
60
+
61
+ ``` ruby
62
+ now_and_every_five_seconds = timers.now_and_every(5) { puts "Now and in another 5 seconds" }
63
+
64
+ loop { timers.wait }
65
+ ```
66
+
67
+ If you'd like another method to do the waiting for you, e.g. `Kernel.select`,
68
+ you can use `Timers::Group#wait_interval` to obtain the amount of time to wait. When
69
+ a timeout is encountered, you can fire all pending timers with `Timers::Group#fire`:
70
+
71
+ ``` ruby
72
+ loop do
73
+ interval = timers.wait_interval
74
+ ready_readers, ready_writers = select readers, writers, nil, interval
75
+
76
+ if ready_readers || ready_writers
77
+ # Handle IO
78
+ ...
79
+ else
80
+ # Timeout!
81
+ timers.fire
82
+ end
83
+ end
84
+ ```
85
+
86
+ You can also pause and continue individual timers, or all timers:
87
+
88
+ ``` ruby
89
+ paused_timer = timers.every(5) { puts "I was paused" }
90
+
91
+ paused_timer.pause
92
+ 10.times { timers.wait } # will not fire paused timer
93
+
94
+ paused_timer.resume
95
+ 10.times { timers.wait } # will fire timer
96
+
97
+ timers.pause
98
+ 10.times { timers.wait } # will not fire any timers
99
+
100
+ timers.resume
101
+ 10.times { timers.wait } # will fire all timers
102
+ ```
103
+
104
+ ## Contributing
105
+
106
+ 1. Fork it
107
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
108
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
109
+ 4. Push to the branch (`git push origin my-new-feature`)
110
+ 5. Create new Pull Request
data.tar.gz.sig ADDED
Binary file
metadata CHANGED
@@ -1,15 +1,73 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timers
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.1
4
+ version: 4.3.4
5
5
  platform: ruby
6
6
  authors:
7
- - Samuel Williams
8
7
  - Tony Arcieri
8
+ - Samuel Williams
9
+ - "//de"
10
+ - Wander Hillen
11
+ - Jeremy Hinegardner
12
+ - skinnyjames
13
+ - Chuck Remes
14
+ - utenmiki
15
+ - Olle Jonsson
16
+ - deadprogrammer
17
+ - takiy33
18
+ - Larry Lv
19
+ - Lin Jen-Shin
20
+ - Ryunosuke SATO
21
+ - Tommy Ong Gia Phu
22
+ - Atul Bhosale
23
+ - Bruno Enten
24
+ - Dimitrij Denissenko
25
+ - Donovan Keme
26
+ - Feram
27
+ - Jesse Cooke
28
+ - Klaus Trainer
29
+ - Lavir the Whiolet
30
+ - Mike Bourgeous
31
+ - Ryan LeCompte
32
+ - Tim Smith
33
+ - Vít Ondruch
34
+ - Will Jessop
35
+ - Yoshiki Takagi
36
+ - nicholas a. evans
37
+ - tommyogp
9
38
  autorequire:
10
39
  bindir: bin
11
- cert_chain: []
12
- date: 2020-09-03 00:00:00.000000000 Z
40
+ cert_chain:
41
+ - |
42
+ -----BEGIN CERTIFICATE-----
43
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
44
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
45
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
46
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
47
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
48
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
49
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
50
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
51
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
52
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
53
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
54
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
55
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
56
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
57
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
58
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
59
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
60
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
61
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
62
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
63
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
64
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
65
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
66
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
67
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
68
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
69
+ -----END CERTIFICATE-----
70
+ date: 2022-09-02 00:00:00.000000000 Z
13
71
  dependencies:
14
72
  - !ruby/object:Gem::Dependency
15
73
  name: bundler
@@ -40,19 +98,47 @@ dependencies:
40
98
  - !ruby/object:Gem::Version
41
99
  version: '0'
42
100
  - !ruby/object:Gem::Dependency
43
- name: rspec
101
+ name: bake-test
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '0.1'
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '0.1'
114
+ - !ruby/object:Gem::Dependency
115
+ name: bake-test-external
44
116
  requirement: !ruby/object:Gem::Requirement
45
117
  requirements:
46
118
  - - "~>"
47
119
  - !ruby/object:Gem::Version
48
- version: '3.0'
120
+ version: '0.2'
49
121
  type: :development
50
122
  prerelease: false
51
123
  version_requirements: !ruby/object:Gem::Requirement
52
124
  requirements:
53
125
  - - "~>"
54
126
  - !ruby/object:Gem::Version
55
- version: '3.0'
127
+ version: '0.2'
128
+ - !ruby/object:Gem::Dependency
129
+ name: sus
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '0.13'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '0.13'
56
142
  description:
57
143
  email:
58
144
  executables: []
@@ -63,9 +149,12 @@ files:
63
149
  - lib/timers/events.rb
64
150
  - lib/timers/group.rb
65
151
  - lib/timers/interval.rb
152
+ - lib/timers/priority_heap.rb
66
153
  - lib/timers/timer.rb
67
154
  - lib/timers/version.rb
68
155
  - lib/timers/wait.rb
156
+ - license.md
157
+ - readme.md
69
158
  homepage: https://github.com/socketry/timers
70
159
  licenses:
71
160
  - MIT
@@ -78,14 +167,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
167
  requirements:
79
168
  - - ">="
80
169
  - !ruby/object:Gem::Version
81
- version: '2.5'
170
+ version: '0'
82
171
  required_rubygems_version: !ruby/object:Gem::Requirement
83
172
  requirements:
84
173
  - - ">="
85
174
  - !ruby/object:Gem::Version
86
175
  version: '0'
87
176
  requirements: []
88
- rubygems_version: 3.1.2
177
+ rubygems_version: 3.3.7
89
178
  signing_key:
90
179
  specification_version: 4
91
180
  summary: Pure Ruby one-shot and periodic timers.
metadata.gz.sig ADDED
Binary file