monotime 0.3.0 → 0.4.0
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 +4 -4
- data/.travis.yml +1 -0
- data/README.md +52 -0
- data/lib/monotime/version.rb +1 -1
- data/lib/monotime.rb +131 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54cec038f8a6e79cbfaf1364c22a2733b9c155d15562ac6c4181568e254ce3f2
|
4
|
+
data.tar.gz: f2fb3122d163d62cbfe6c148a547af7917efad19a30032695a34949bdf5758e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 708d7018fc1d90987b00074545ff351ab31ba60a37e3808092f85d48f0dce660380895c8fd0fa3e9f1038deb958a2274e730e2119d73f582377c72ef9aebe6a4
|
7
|
+
data.tar.gz: 0a5cd0b6f7ffb5a50f6d31efd21d4887e9130dec54551bb1e04642c7ada69532b12c4f4bf432b97ada9b4ba5be6d9e0f1ced6c307c6488df7dbb87199fec7853
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
[](https://badge.fury.io/rb/monotime)
|
2
|
+
[](https://travis-ci.org/Freaky/monotime)
|
3
|
+
|
1
4
|
# Monotime
|
2
5
|
|
3
6
|
A sensible interface to Ruby's monotonic clock, inspired by Rust.
|
@@ -62,6 +65,8 @@ And how to do basic maths on itself:
|
|
62
65
|
```ruby
|
63
66
|
(Duration.from_millis(42) + Duration.from_secs(1)).to_s # => "1.042s"
|
64
67
|
(Duration.from_millis(42) - Duration.from_secs(1)).to_s # => "-958ms"
|
68
|
+
(Duration.from_secs(42) * 2).to_s # => "84s"
|
69
|
+
(Duration.from_secs(42) / 2).to_s # => "21s"
|
65
70
|
```
|
66
71
|
|
67
72
|
`Instant` does some simple maths too:
|
@@ -77,6 +82,53 @@ And how to do basic maths on itself:
|
|
77
82
|
`Duration` and `Instant` are also `Comparable` with other instances of their
|
78
83
|
type, and support `#hash` for use in, er, hashes.
|
79
84
|
|
85
|
+
## Sleeping
|
86
|
+
|
87
|
+
`Duration` can be used to sleep a thread, assuming it's positive (time travel
|
88
|
+
is not yet implemented):
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# Equivalent
|
92
|
+
sleep(Duration.from_secs(1).to_secs) # => 1
|
93
|
+
|
94
|
+
Duration.from_secs(1).sleep # => 1
|
95
|
+
```
|
96
|
+
|
97
|
+
So can `Instant`, taking a `Duration` and sleeping until the given `Duration`
|
98
|
+
past the time the `Instant` was created, if any. This may be useful if you wish
|
99
|
+
to maintain an approximate interval while performing work in between:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
poke_duration = Duration.from_secs(60)
|
103
|
+
loop do
|
104
|
+
start = Instant.now
|
105
|
+
poke_my_api(api_to_poke, what_to_poke_it_with)
|
106
|
+
start.sleep(poke_duration) # sleeps 60 seconds minus how long poke_my_api took
|
107
|
+
# alternative: start.sleep_secs(60)
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
`Instant#sleep` returns a `Duration` which was slept, or a negative `Duration` if
|
112
|
+
the desired sleep period has passed.
|
113
|
+
|
114
|
+
## Duration duck typing
|
115
|
+
|
116
|
+
Operations taking a `Duration` can also accept any type which implements
|
117
|
+
`#to_nanos`, returning an (Integer) number of nanoseconds the value represents.
|
118
|
+
|
119
|
+
For example, to treat built-in numeric types as second durations, you could do:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
class Numeric
|
123
|
+
def to_nanos
|
124
|
+
Integer(self * 1_000_000_000)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
(Duration.from_secs(1) + 41).to_s # => "42s"
|
129
|
+
(Instant.now - 42).to_s # => "42.000010545s"
|
130
|
+
```
|
131
|
+
|
80
132
|
## Development
|
81
133
|
|
82
134
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/monotime/version.rb
CHANGED
data/lib/monotime.rb
CHANGED
@@ -45,14 +45,54 @@ module Monotime
|
|
45
45
|
duration_since(self.class.now)
|
46
46
|
end
|
47
47
|
|
48
|
-
#
|
48
|
+
# Sleep for the given `Duration` past this +Instant+, if any.
|
49
|
+
#
|
50
|
+
# @example Sleeps for a second
|
51
|
+
# start = Instant.now
|
52
|
+
# sleep 0.5 # do stuff for half a second
|
53
|
+
# start.sleep(Duration.from_secs(1)).to_s # => "490.088706ms" (slept)
|
54
|
+
# start.sleep(Duration.from_secs(1)).to_s # => "-12.963502ms" (did not sleep)
|
55
|
+
#
|
56
|
+
# @param duration [Duration, #to_nanos]
|
57
|
+
# @return [Duration] the slept duration, if +#positive?+, else the overshot time
|
58
|
+
def sleep(duration)
|
59
|
+
remaining = duration - elapsed
|
60
|
+
|
61
|
+
return remaining unless remaining.positive?
|
62
|
+
remaining.tap(&:sleep)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Sleep for the given number of seconds past this +Instant+, if any.
|
66
|
+
#
|
67
|
+
# Equivalent to +#sleep(Duration.from_secs(secs))+
|
68
|
+
#
|
69
|
+
# @param secs [Numeric] number of seconds to sleep past this +Instant+
|
70
|
+
# @return [Duration] the slept duration, if +#positive?+, else the overshot time
|
71
|
+
# @see #sleep
|
72
|
+
def sleep_secs(secs)
|
73
|
+
sleep(Duration.from_secs(secs))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sleep for the given number of milliseconds past this +Instant+, if any.
|
77
|
+
#
|
78
|
+
# Equivalent to +#sleep(Duration.from_millis(millis))+
|
79
|
+
#
|
80
|
+
# @param millis [Numeric] number of milliseconds to sleep past this +Instant+
|
81
|
+
# @return [Duration] the slept duration, if +#positive?+, else the overshot time
|
82
|
+
# @see #sleep
|
83
|
+
def sleep_millis(secs)
|
84
|
+
sleep(Duration.from_millis(secs))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sugar for +#elapsed.to_s+.
|
49
88
|
#
|
50
89
|
# @see Duration#to_s
|
51
90
|
def to_s(*args)
|
52
91
|
elapsed.to_s(*args)
|
53
92
|
end
|
54
93
|
|
55
|
-
# Add a +Duration+ to this +Instant+, returning
|
94
|
+
# Add a +Duration+ or +#to_nanos+-coercible object to this +Instant+, returning
|
95
|
+
# a new +Instant+.
|
56
96
|
#
|
57
97
|
# @param other [Duration, #to_nanos]
|
58
98
|
# @return [Instant]
|
@@ -63,7 +103,8 @@ module Monotime
|
|
63
103
|
end
|
64
104
|
|
65
105
|
# Subtract another +Instant+ to generate a +Duration+ between the two,
|
66
|
-
# or a +Duration
|
106
|
+
# or a +Duration+ or +#to_nanos+-coercible object, to generate an +Instant+
|
107
|
+
# offset by it.
|
67
108
|
#
|
68
109
|
# @param other [Instant, Duration, #to_nanos]
|
69
110
|
# @return [Duration, Instant]
|
@@ -77,17 +118,28 @@ module Monotime
|
|
77
118
|
end
|
78
119
|
end
|
79
120
|
|
80
|
-
#
|
121
|
+
# Determine if the given +Instant+ is before, equal to or after this one.
|
122
|
+
# +nil+ if not passed an +Instant+.
|
123
|
+
#
|
124
|
+
# @return [-1, 0, 1, nil]
|
81
125
|
def <=>(other)
|
82
126
|
@ns <=> other.ns if other.is_a?(Instant)
|
83
127
|
end
|
84
128
|
|
129
|
+
# Determine if +other+'s value equals that of this +Instant+.
|
130
|
+
# Use +eql?+ if type checks are desired for future compatibility.
|
131
|
+
#
|
132
|
+
# @return [Boolean]
|
133
|
+
# @see #eql?
|
85
134
|
def ==(other)
|
86
135
|
other.is_a?(Instant) && @ns == other.ns
|
87
136
|
end
|
88
137
|
|
89
138
|
alias eql? ==
|
90
139
|
|
140
|
+
# Generate a hash for this type and value.
|
141
|
+
#
|
142
|
+
# @return [Fixnum]
|
91
143
|
def hash
|
92
144
|
self.class.hash ^ @ns.hash
|
93
145
|
end
|
@@ -146,7 +198,8 @@ module Monotime
|
|
146
198
|
end
|
147
199
|
end
|
148
200
|
|
149
|
-
# Add another +Duration+ to this one,
|
201
|
+
# Add another +Duration+ or +#to_nanos+-coercible object to this one,
|
202
|
+
# returning a new +Duration+.
|
150
203
|
#
|
151
204
|
# @param [Duration, #to_nanos]
|
152
205
|
#
|
@@ -157,7 +210,8 @@ module Monotime
|
|
157
210
|
Duration.new(to_nanos + other.to_nanos)
|
158
211
|
end
|
159
212
|
|
160
|
-
# Subtract another +Duration+ from this one,
|
213
|
+
# Subtract another +Duration+ or +#to_nanos+-coercible object from this one,
|
214
|
+
# returning a new +Duration+.
|
161
215
|
#
|
162
216
|
# @param [Duration, #to_nanos]
|
163
217
|
# @return [Duration]
|
@@ -167,17 +221,51 @@ module Monotime
|
|
167
221
|
Duration.new(to_nanos - other.to_nanos)
|
168
222
|
end
|
169
223
|
|
170
|
-
#
|
224
|
+
# Divide this duration by a +Numeric+.
|
225
|
+
#
|
226
|
+
# @param [Numeric]
|
227
|
+
# @return [Duration]
|
228
|
+
def /(other)
|
229
|
+
Duration.new(to_nanos / other)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Multiply this duration by a +Numeric+.
|
233
|
+
#
|
234
|
+
# @param [Numeric]
|
235
|
+
# @return [Duration]
|
236
|
+
def *(other)
|
237
|
+
Duration.new(to_nanos * other)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Compare the *value* of this +Duration+ with another, or any +#to_nanos+-coercible
|
241
|
+
# object, or nil if not comparable.
|
242
|
+
#
|
243
|
+
# @param [Duration, #to_nanos, Object]
|
244
|
+
# @return [-1, 0, 1, nil]
|
171
245
|
def <=>(other)
|
172
|
-
to_nanos <=> other.to_nanos if other.
|
246
|
+
to_nanos <=> other.to_nanos if other.respond_to?(:to_nanos)
|
173
247
|
end
|
174
248
|
|
249
|
+
# Compare the equality of the *value* of this +Duration+ with another, or
|
250
|
+
# any +#to_nanos+-coercible object, or nil if not comparable.
|
251
|
+
#
|
252
|
+
# @param [Duration, #to_nanos, Object]
|
253
|
+
# @return [Boolean]
|
175
254
|
def ==(other)
|
176
|
-
other.
|
255
|
+
other.respond_to?(:to_nanos) && to_nanos == other.to_nanos
|
177
256
|
end
|
178
257
|
|
179
|
-
|
258
|
+
# Check equality of the value and type of this +Duration+ with another.
|
259
|
+
#
|
260
|
+
# @param [Duration, Object]
|
261
|
+
# @return [Boolean]
|
262
|
+
def eql?(other)
|
263
|
+
other.is_a?(Duration) && to_nanos == other.to_nanos
|
264
|
+
end
|
180
265
|
|
266
|
+
# Generate a hash for this type and value.
|
267
|
+
#
|
268
|
+
# @return [Fixnum]
|
181
269
|
def hash
|
182
270
|
self.class.hash ^ to_nanos.hash
|
183
271
|
end
|
@@ -210,6 +298,38 @@ module Monotime
|
|
210
298
|
@ns
|
211
299
|
end
|
212
300
|
|
301
|
+
# Return true if this +Duration+ is positive.
|
302
|
+
#
|
303
|
+
# @return [Boolean]
|
304
|
+
def positive?
|
305
|
+
to_nanos.positive?
|
306
|
+
end
|
307
|
+
|
308
|
+
# Return true if this +Duration+ is negative.
|
309
|
+
#
|
310
|
+
# @return [Boolean]
|
311
|
+
def negative?
|
312
|
+
to_nanos.negative?
|
313
|
+
end
|
314
|
+
|
315
|
+
# Return true if this +Duration+ is zero.
|
316
|
+
#
|
317
|
+
# @return [Boolean]
|
318
|
+
def zero?
|
319
|
+
to_nanos.zero?
|
320
|
+
end
|
321
|
+
|
322
|
+
# Sleep for the duration of this +Duration+. Equivalent to
|
323
|
+
# +Kernel.sleep(duration.to_secs)+.
|
324
|
+
#
|
325
|
+
# @raise [NotImplementedError] negative +Duration+ sleeps are not yet supported.
|
326
|
+
# @return [Integer]
|
327
|
+
# @see Instant#sleep
|
328
|
+
def sleep
|
329
|
+
raise NotImplementedError, 'time travel module missing' if negative?
|
330
|
+
Kernel.sleep(to_secs)
|
331
|
+
end
|
332
|
+
|
213
333
|
DIVISORS = [
|
214
334
|
[1_000_000_000.0, 's'],
|
215
335
|
[1_000_000.0, 'ms'],
|
@@ -232,7 +352,7 @@ module Monotime
|
|
232
352
|
ns = to_nanos.abs
|
233
353
|
div, unit = DIVISORS.find { |d, _| ns >= d }
|
234
354
|
ns /= div if div.nonzero?
|
235
|
-
num = format("#{'-' if
|
355
|
+
num = format("#{'-' if negative?}%.#{precision}f", ns)
|
236
356
|
num.sub!(/\.?0*$/, '') if precision.nonzero?
|
237
357
|
num << unit
|
238
358
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: monotime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Hurst
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|