monotime 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54cec038f8a6e79cbfaf1364c22a2733b9c155d15562ac6c4181568e254ce3f2
4
- data.tar.gz: f2fb3122d163d62cbfe6c148a547af7917efad19a30032695a34949bdf5758e4
3
+ metadata.gz: bbb84fdd4db4d629ca5d4cc847c47d9003f4553f010b575364f7ad562c16bb4d
4
+ data.tar.gz: 40729463ba3970e77110c2ae92620f4d6ca08b6d9997776272a3d0a05a03b595
5
5
  SHA512:
6
- metadata.gz: 708d7018fc1d90987b00074545ff351ab31ba60a37e3808092f85d48f0dce660380895c8fd0fa3e9f1038deb958a2274e730e2119d73f582377c72ef9aebe6a4
7
- data.tar.gz: 0a5cd0b6f7ffb5a50f6d31efd21d4887e9130dec54551bb1e04642c7ada69532b12c4f4bf432b97ada9b4ba5be6d9e0f1ced6c307c6488df7dbb87199fec7853
6
+ metadata.gz: 2916513d7d5dada28d18855d7644055cf82ddd6d2ad66ebd27c7a73d9ae2c88cde94887e7769d007372768c7acbcac32d8210308f9bc2ba2256b150126f29d9b
7
+ data.tar.gz: cff3a4bd7be443460f7dfc65432da3f3d30fef62433e5caa73c932497d490fdc5641b34659f18d8790bb0797449d93b9019f46678c0489b3aa9756c871222bd1
data/.rubocop.yml CHANGED
@@ -1,2 +1,11 @@
1
1
  Metrics/LineLength:
2
2
  Max: 96
3
+
4
+ Style/AsciiComments:
5
+ Enabled: false
6
+
7
+ Style/AccessModifierDeclarations:
8
+ Enabled: false
9
+
10
+ Style/FormatStringToken:
11
+ Enabled: false
data/README.md CHANGED
@@ -108,6 +108,14 @@ loop do
108
108
  end
109
109
  ```
110
110
 
111
+ Or you can declare a future `Instant` and ask to sleep until it passes:
112
+
113
+ ```ruby
114
+ next_minute = Instant.now + Duration.from_secs(60)
115
+ do_stuff
116
+ next_minute.sleep # => sleeps any remaining seconds
117
+ ```
118
+
111
119
  `Instant#sleep` returns a `Duration` which was slept, or a negative `Duration` if
112
120
  the desired sleep period has passed.
113
121
 
data/lib/monotime.rb CHANGED
@@ -8,7 +8,8 @@ module Monotime
8
8
  class Instant
9
9
  # A measurement, in nanoseconds. Should be considered opaque and
10
10
  # non-portable outside the process that created it.
11
- protected def ns() @ns end
11
+ attr_reader :ns
12
+ protected :ns
12
13
 
13
14
  include Comparable
14
15
 
@@ -17,6 +18,7 @@ module Monotime
17
18
  # Users should generally *not* pass anything to this function.
18
19
  #
19
20
  # @param nanos [Integer]
21
+ # @see #now
20
22
  def initialize(nanos = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond))
21
23
  @ns = Integer(nanos)
22
24
  end
@@ -45,7 +47,9 @@ module Monotime
45
47
  duration_since(self.class.now)
46
48
  end
47
49
 
48
- # Sleep for the given `Duration` past this +Instant+, if any.
50
+ # Sleep until this +Instant+, plus an optional +Duration+, returning a +Duration+
51
+ # that's either positive if any time was slept, or negative if sleeping would
52
+ # require time travel.
49
53
  #
50
54
  # @example Sleeps for a second
51
55
  # start = Instant.now
@@ -53,13 +57,17 @@ module Monotime
53
57
  # start.sleep(Duration.from_secs(1)).to_s # => "490.088706ms" (slept)
54
58
  # start.sleep(Duration.from_secs(1)).to_s # => "-12.963502ms" (did not sleep)
55
59
  #
56
- # @param duration [Duration, #to_nanos]
60
+ # @example Also sleeps for a second.
61
+ # one_second_in_the_future = Instant.now + Duration.from_secs(1)
62
+ # one_second_in_the_future.sleep.to_s # => "985.592712ms" (slept)
63
+ # one_second_in_the_future.sleep.to_s # => "-4.71217ms" (did not sleep)
64
+ #
65
+ # @param duration [nil, Duration, #to_nanos]
57
66
  # @return [Duration] the slept duration, if +#positive?+, else the overshot time
58
- def sleep(duration)
59
- remaining = duration - elapsed
67
+ def sleep(duration = nil)
68
+ remaining = duration ? duration - elapsed : -elapsed
60
69
 
61
- return remaining unless remaining.positive?
62
- remaining.tap(&:sleep)
70
+ remaining.tap { |rem| rem.sleep if rem.positive? }
63
71
  end
64
72
 
65
73
  # Sleep for the given number of seconds past this +Instant+, if any.
@@ -80,8 +88,8 @@ module Monotime
80
88
  # @param millis [Numeric] number of milliseconds to sleep past this +Instant+
81
89
  # @return [Duration] the slept duration, if +#positive?+, else the overshot time
82
90
  # @see #sleep
83
- def sleep_millis(secs)
84
- sleep(Duration.from_millis(secs))
91
+ def sleep_millis(millis)
92
+ sleep(Duration.from_millis(millis))
85
93
  end
86
94
 
87
95
  # Sugar for +#elapsed.to_s+.
@@ -94,6 +102,9 @@ module Monotime
94
102
  # Add a +Duration+ or +#to_nanos+-coercible object to this +Instant+, returning
95
103
  # a new +Instant+.
96
104
  #
105
+ # @example
106
+ # (Instant.now + Duration.from_secs(1)).to_s # => "-999.983976ms"
107
+ #
97
108
  # @param other [Duration, #to_nanos]
98
109
  # @return [Instant]
99
110
  def +(other)
@@ -106,6 +117,10 @@ module Monotime
106
117
  # or a +Duration+ or +#to_nanos+-coercible object, to generate an +Instant+
107
118
  # offset by it.
108
119
  #
120
+ # @example
121
+ # (Instant.now - Duration.from_secs(1)).to_s # => "1.000016597s"
122
+ # (Instant.now - Instant.now).to_s # => "-3.87μs"
123
+ #
109
124
  # @param other [Instant, Duration, #to_nanos]
110
125
  # @return [Duration, Instant]
111
126
  def -(other)
@@ -152,7 +167,10 @@ module Monotime
152
167
  # Create a new +Duration+ of a specified number of nanoseconds, zero by
153
168
  # default.
154
169
  #
170
+ # Users are strongly advised to use +#from_nanos+ instead.
171
+ #
155
172
  # @param nanos [Integer]
173
+ # @see #from_nanos
156
174
  def initialize(nanos = 0)
157
175
  @ns = Integer(nanos)
158
176
  end
@@ -163,7 +181,7 @@ module Monotime
163
181
  # @param secs [Numeric]
164
182
  # @return [Duration]
165
183
  def from_secs(secs)
166
- new(Integer(Float(secs) * 1_000_000_000))
184
+ new(Integer(secs * 1_000_000_000))
167
185
  end
168
186
 
169
187
  # Generate a new +Duration+ measuring the given number of milliseconds.
@@ -171,7 +189,7 @@ module Monotime
171
189
  # @param millis [Numeric]
172
190
  # @return [Duration]
173
191
  def from_millis(millis)
174
- new(Integer(Float(millis) * 1_000_000))
192
+ new(Integer(millis * 1_000_000))
175
193
  end
176
194
 
177
195
  # Generate a new +Duration+ measuring the given number of microseconds.
@@ -179,7 +197,7 @@ module Monotime
179
197
  # @param micros [Numeric]
180
198
  # @return [Duration]
181
199
  def from_micros(micros)
182
- new(Integer(Float(micros) * 1_000))
200
+ new(Integer(micros * 1_000))
183
201
  end
184
202
 
185
203
  # Generate a new +Duration+ measuring the given number of nanoseconds.
@@ -192,6 +210,9 @@ module Monotime
192
210
 
193
211
  # Return a +Duration+ measuring the elapsed time of the yielded block.
194
212
  #
213
+ # @example
214
+ # Duration.measure { sleep(0.5) }.to_s # => "512.226109ms"
215
+ #
195
216
  # @return [Duration]
196
217
  def measure
197
218
  Instant.now.tap { yield }.elapsed
@@ -201,8 +222,10 @@ module Monotime
201
222
  # Add another +Duration+ or +#to_nanos+-coercible object to this one,
202
223
  # returning a new +Duration+.
203
224
  #
204
- # @param [Duration, #to_nanos]
225
+ # @example
226
+ # (Duration.from_secs(10) + Duration.from_secs(5)).to_s # => "15s"
205
227
  #
228
+ # @param [Duration, #to_nanos]
206
229
  # @return [Duration]
207
230
  def +(other)
208
231
  raise TypeError, 'Not one of: [Duration, #to_nanos]' unless other.respond_to?(:to_nanos)
@@ -213,6 +236,9 @@ module Monotime
213
236
  # Subtract another +Duration+ or +#to_nanos+-coercible object from this one,
214
237
  # returning a new +Duration+.
215
238
  #
239
+ # @example
240
+ # (Duration.from_secs(10) - Duration.from_secs(5)).to_s # => "5s"
241
+ #
216
242
  # @param [Duration, #to_nanos]
217
243
  # @return [Duration]
218
244
  def -(other)
@@ -223,6 +249,9 @@ module Monotime
223
249
 
224
250
  # Divide this duration by a +Numeric+.
225
251
  #
252
+ # @example
253
+ # (Duration.from_secs(10) / 2).to_s # => "5s"
254
+ #
226
255
  # @param [Numeric]
227
256
  # @return [Duration]
228
257
  def /(other)
@@ -231,12 +260,38 @@ module Monotime
231
260
 
232
261
  # Multiply this duration by a +Numeric+.
233
262
  #
263
+ # @example
264
+ # (Duration.from_secs(10) * 2).to_s # => "20s"
265
+ #
234
266
  # @param [Numeric]
235
267
  # @return [Duration]
236
268
  def *(other)
237
269
  Duration.new(to_nanos * other)
238
270
  end
239
271
 
272
+ # Unary minus: make a positive +Duration+ negative, and vice versa.
273
+ #
274
+ # @example
275
+ # -Duration.from_secs(-1).to_s # => "1s"
276
+ # -Duration.from_secs(1).to_s # => "-1s"
277
+ #
278
+ # @return [Duration]
279
+ def -@
280
+ Duration.new(-to_nanos)
281
+ end
282
+
283
+ # Return a +Duration+ that's absolute (positive).
284
+ #
285
+ # @example
286
+ # Duration.from_secs(-1).abs.to_s # => "1s"
287
+ # Duration.from_secs(1).abs.to_s # => "1s"
288
+ #
289
+ # @return [Duration]
290
+ def abs
291
+ return self if positive? || zero?
292
+ Duration.new(to_nanos.abs)
293
+ end
294
+
240
295
  # Compare the *value* of this +Duration+ with another, or any +#to_nanos+-coercible
241
296
  # object, or nil if not comparable.
242
297
  #
@@ -322,6 +377,10 @@ module Monotime
322
377
  # Sleep for the duration of this +Duration+. Equivalent to
323
378
  # +Kernel.sleep(duration.to_secs)+.
324
379
  #
380
+ # @example
381
+ # Duration.from_secs(1).sleep # => 1
382
+ # Duration.from_secs(-1).sleep # => raises NotImplementedError
383
+ #
325
384
  # @raise [NotImplementedError] negative +Duration+ sleeps are not yet supported.
326
385
  # @return [Integer]
327
386
  # @see Instant#sleep
@@ -345,16 +404,66 @@ module Monotime
345
404
  # The exact format is subject to change, users with specific requirements
346
405
  # are encouraged to use their own formatting methods.
347
406
  #
407
+ # @example
408
+ # Duration.from_nanos(100).to_s # => "100ns"
409
+ # Duration.from_micros(100).to_s # => "100μs"
410
+ # Duration.from_millis(100).to_s # => "100ms"
411
+ # Duration.from_secs(100).to_s # => "100s"
412
+ # Duration.from_nanos(1234567).to_s # => "1.234567ms"
413
+ # Duration.from_nanos(1234567).to_s(2) # => "1.23ms"
414
+ #
348
415
  # @param precision [Integer] the maximum number of decimal places
349
416
  # @return [String]
350
417
  def to_s(precision = 9)
351
418
  precision = Integer(precision).abs
352
- ns = to_nanos.abs
353
- div, unit = DIVISORS.find { |d, _| ns >= d }
354
- ns /= div if div.nonzero?
355
- num = format("#{'-' if negative?}%.#{precision}f", ns)
356
- num.sub!(/\.?0*$/, '') if precision.nonzero?
357
- num << unit
419
+ div, unit = DIVISORS.find { |d, _| to_nanos.abs >= d }
420
+
421
+ if div.zero?
422
+ format('%d%s', to_nanos, unit)
423
+ else
424
+ format("%#.#{precision}f", to_nanos / div).sub(/\.?0*\z/, '') << unit
425
+ end
358
426
  end
427
+
428
+ # def to_s_bigdecimal(precision = 9)
429
+ # require 'bigdecimal'
430
+ # precision = Integer(precision).abs
431
+ # div, unit = DIVISORS.find { |d, _| to_nanos.abs >= d }
432
+
433
+ # if div.zero?
434
+ # format('%d%s', to_nanos, unit)
435
+ # else
436
+ # num = (BigDecimal(to_nanos) / div.to_i)
437
+ # .round(precision, :banker).to_s('F').sub(/\.?0*$/, '')
438
+ # format('%s%s', num, unit)
439
+ # end
440
+ # end
441
+
442
+ # def to_s_divmod(precision = 9)
443
+ # precision = Integer(precision).abs
444
+
445
+ # ns = to_nanos.abs
446
+ # div, unit = DIVISORS.find { |d, _| ns >= d }
447
+
448
+ # return format('%dns', to_nanos) if div.zero?
449
+
450
+ # whole, frac = to_nanos.divmod(div.to_i)
451
+
452
+ # if precision.zero? || frac.zero?
453
+ # whole += 1 if frac > div / 2
454
+ # return format('%d%s', whole, unit)
455
+ # end
456
+
457
+ # # XXX: still need to round: Duration.from_nanos(99999999999).to_s_divmod 7 # => 99.1s
458
+ # p frac
459
+ # frac = ((frac / div.to_f) * (10 ** precision)).round
460
+ # if frac.to_s =~ /\A10*\z/ # erm...
461
+ # whole += 1
462
+ # frac = 0
463
+ # end
464
+ # num = format('%d.%d', whole, frac)
465
+ # num.sub!(/\.?0*\z/, '') if precision.nonzero?
466
+ # num << unit
467
+ # end
359
468
  end
360
469
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Monotime
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  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.0
4
+ version: 0.5.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-08 00:00:00.000000000 Z
11
+ date: 2018-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler