timers 4.3.1 → 4.3.4

Sign up to get free protection for your applications and to get access to all the features.
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