monotime 0.4.0 → 0.5.0

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: 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