monotime 0.7.0 → 0.8.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/.github/workflows/ci.yml +17 -0
- data/.rubocop.yml +24 -2
- data/CHANGELOG.md +119 -45
- data/README.md +40 -30
- data/lib/monotime/duration.rb +60 -12
- data/lib/monotime/include.rb +5 -0
- data/lib/monotime/instant.rb +98 -4
- data/lib/monotime/version.rb +2 -2
- data/monotime.gemspec +2 -1
- metadata +25 -11
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 388a55af121b75e9a9ac06db8674e5a8a4b06ade0431a5cdd961f82426c87ade
|
4
|
+
data.tar.gz: 7d62a9b673252f01591769d8659c5a36b1026088724a2afc9694b41f633ca053
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14c7ede1fc2daa4fef854d3be58392f57849ff07c4f815c3e995f32073e14893fa7ad95f08e074199457517d262fc8c2942316b8039260b1e66e4045409bb6cf
|
7
|
+
data.tar.gz: b2c907ce1b4f3f92185ba4cb238c3809b465d40be7e34eb1c4a225779a4f6c156ae1d78742ddf24a467d59bfd3437c15be54d4f8bc91a9a9d06fa42ee5e5544a
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
strategy:
|
6
|
+
fail-fast: false
|
7
|
+
matrix:
|
8
|
+
os: [ubuntu-latest, macos-latest]
|
9
|
+
ruby: ['2.7', '3.0', '3.1', '3.2', head, jruby, jruby-head, truffleruby, truffleruby-head]
|
10
|
+
runs-on: ${{ matrix.os }}
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v4
|
13
|
+
- uses: ruby/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: ${{ matrix.ruby }}
|
16
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
17
|
+
- run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
+
NewCops: enable
|
2
3
|
Exclude:
|
3
4
|
- "bin/*"
|
4
5
|
- "test/*"
|
@@ -6,11 +7,14 @@ AllCops:
|
|
6
7
|
- "Rakefile"
|
7
8
|
- "Gemfile"
|
8
9
|
|
9
|
-
|
10
|
+
Layout/LineLength:
|
10
11
|
Max: 96
|
11
12
|
|
12
13
|
Metrics/ClassLength:
|
13
|
-
Max:
|
14
|
+
Max: 140
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Max: 20
|
14
18
|
|
15
19
|
Style/AsciiComments:
|
16
20
|
Enabled: false
|
@@ -20,3 +24,21 @@ Style/AccessModifierDeclarations:
|
|
20
24
|
|
21
25
|
Style/FormatStringToken:
|
22
26
|
Enabled: false
|
27
|
+
|
28
|
+
Style/HashEachMethods:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
Style/HashTransformKeys:
|
32
|
+
Enabled: true
|
33
|
+
|
34
|
+
Style/HashTransformValues:
|
35
|
+
Enabled: true
|
36
|
+
|
37
|
+
Style/ExplicitBlockArgument:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/MixinUsage:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/TrailingCommaInArrayLiteral:
|
44
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,90 +1,160 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.8.0] - 2023-09-17
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Default precision for `Duration#to_s` can be set using
|
8
|
+
`Duration.default_to_s_precision=`.
|
9
|
+
- Default sleep function can be set using `Duration.sleep_function=`
|
10
|
+
- `Duration::ZERO` and `Duration.zero` for an easy, memory-efficient
|
11
|
+
zero-duration singleton.
|
12
|
+
- `Instant.clock_id` and `Instant.clock_id=` to control the default clock
|
13
|
+
source.
|
14
|
+
- `Instant.clock_getres` to get the minimum supported `Duration` from the
|
15
|
+
selected clock source.
|
16
|
+
- `Instant.monotonic_function=` to completely replace the default monotonic
|
17
|
+
function.
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- The default clock source is now chosen from a selection of options instead of
|
22
|
+
defaulting to `CLOCK_MONOTONIC``. Where possible options are used which are
|
23
|
+
unaffected by NTP frequency skew and which do not count time in system suspend.
|
24
|
+
- CI matrix drops Ruby 2.5 and 2.6 and adds 3.1, 3.2, head branches of Ruby,
|
25
|
+
JRuby, and TruffleRuby, and also tests under macOS.
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- CI on TruffleRuby has been fixed by disabling SimpleCov.
|
30
|
+
- Several fragile tests depending on relatively narrow sleep times have been fixed.
|
31
|
+
|
32
|
+
### Thanks
|
33
|
+
|
34
|
+
- [@petergoldstein] for fixing CI on TruffleRuby and adding 3.1 and 3.2.
|
35
|
+
- [@fig] for fixing a README error.
|
36
|
+
|
37
|
+
## [0.7.1] - 2021-10-22
|
38
|
+
|
39
|
+
### Added
|
40
|
+
|
41
|
+
- `simplecov` introduced to test suite.
|
42
|
+
- `monotime/include.rb` to auto-include types globally.
|
43
|
+
|
44
|
+
### Changed
|
45
|
+
|
46
|
+
- All `Instant` and `Duration` instances are now frozen.
|
47
|
+
- Migrate from Travis CI to Github Actions
|
48
|
+
- Update development dependency on `rake`.
|
49
|
+
|
3
50
|
## [0.7.0] - 2019-04-24
|
51
|
+
|
4
52
|
### Added
|
5
|
-
|
6
|
-
|
53
|
+
|
54
|
+
- `Duration.with_measure`, which yields and returns an array containing its
|
55
|
+
evaluated return value and its `Duration`.
|
7
56
|
|
8
57
|
### Changed
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
58
|
+
|
59
|
+
- Break `Duration` and `Instant` into their own files.
|
60
|
+
- Rename `Monotime::VERSION` to `Monotime::MONOTIME_VERSION` to reduce
|
61
|
+
potential for collision if the module is included.
|
62
|
+
- Update to bundler 2.0.
|
63
|
+
- Rework README.md. Includes fix for [issue #1] (added a "See Also" section).
|
14
64
|
|
15
65
|
## [0.6.1] - 2018-10-26
|
66
|
+
|
16
67
|
### Fixed
|
17
|
-
|
18
|
-
|
68
|
+
|
69
|
+
- Build gem from a clean git checkout, not my local development directory.
|
70
|
+
No functional changes.
|
19
71
|
|
20
72
|
## [0.6.0] - 2018-10-26
|
73
|
+
|
21
74
|
### Added
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
75
|
+
|
76
|
+
- This `CHANGELOG.md` by request of [@celsworth].
|
77
|
+
- Aliases for `Duration.from_*` and `Duration#to_*` without the prefix. e.g.
|
78
|
+
`Duration.from_secs(42).to_secs == 42` can now be written as
|
79
|
+
`Duration.secs(42).secs == 42`.
|
80
|
+
- `Duration#nonzero?`.
|
81
|
+
- `Instant#in_past?` and `Instant#in_future?`.
|
28
82
|
|
29
83
|
## [0.5.0] - 2018-10-13
|
84
|
+
|
30
85
|
### Added
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
86
|
+
|
87
|
+
- `Duration#abs` to make a `Duration` positive.
|
88
|
+
- `Duration#-@` to invert the sign of a `Duration`.
|
89
|
+
- `Duration#positive?`
|
90
|
+
- `Duration#negative?`
|
91
|
+
- `Duration#zero?`
|
36
92
|
|
37
93
|
### Changed
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
94
|
+
|
95
|
+
- `Instant#sleep` with no argument now sleeps until the `Instant`.
|
96
|
+
- `Duration.from_*` no longer coerce their argument to `Float`.
|
97
|
+
- `Duration#==` checks value via `#to_nanos`, not type.
|
98
|
+
- `Duration#eql?` checks value and type.
|
99
|
+
- `Duration#<=>` compares value via `#to_nanos`.
|
43
100
|
|
44
101
|
## [0.4.0] - 2018-10-09
|
102
|
+
|
45
103
|
### Added
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
104
|
+
|
105
|
+
- `Instant#sleep` - sleep to a given `Duration` past an `Instant`.
|
106
|
+
- `Instant#sleep_secs` and `Instant#sleep_millis` convenience methods.
|
107
|
+
- `Duration#sleep` - sleep for the `Duration`.
|
108
|
+
- `Duration#*` - multiply a `Duration` by a number.
|
109
|
+
- `Duration#/` - divide a `Duration` by a number.
|
51
110
|
|
52
111
|
### Changed
|
53
|
-
|
112
|
+
|
113
|
+
More `#to_nanos` `Duration` duck-typing.
|
54
114
|
|
55
115
|
## [0.3.0] - 2018-10-04
|
116
|
+
|
56
117
|
### Added
|
57
|
-
|
118
|
+
|
119
|
+
- `#to_nanos` is now used to duck-type `Duration` everywhere.
|
58
120
|
|
59
121
|
### Changed
|
60
|
-
|
122
|
+
|
123
|
+
- Make `<=>` return nil on invalid types, rather than raising a `TypeError`.
|
61
124
|
|
62
125
|
### Removed
|
63
|
-
|
126
|
+
|
127
|
+
- Dependency on `dry-equalizer`.
|
64
128
|
|
65
129
|
## [0.2.0] - 2018-10-03
|
130
|
+
|
66
131
|
### Added
|
67
|
-
|
68
|
-
|
132
|
+
|
133
|
+
- `Instant#to_s` as an alias for `#elapsed.to_s`
|
134
|
+
- `Duration#to_nanos`, with some limited duck-typing.
|
69
135
|
|
70
136
|
### Changed
|
71
|
-
|
72
|
-
|
73
|
-
|
137
|
+
|
138
|
+
- Switch to microseconds internally.
|
139
|
+
- `Duration#to_{secs,millis,micros}` now return a `Float`.
|
140
|
+
- `Instant#ns` is now `protected`.
|
74
141
|
|
75
142
|
### Fixed
|
76
|
-
|
77
|
-
|
78
|
-
|
143
|
+
|
144
|
+
- `Duration#to_s` zero-stripping with precision=0.
|
145
|
+
- `Instant#-` argument ordering with other `Instant`.
|
146
|
+
- `Duration#to_micros` returns microseconds, not picoseconds.
|
79
147
|
|
80
148
|
### Removed
|
81
|
-
|
82
|
-
|
149
|
+
|
150
|
+
- `Instant` and `Duration` maths methods no longer support passing an `Integer`
|
151
|
+
number of nanoseconds.
|
83
152
|
|
84
153
|
## [0.1.0] - 2018-10-02
|
154
|
+
|
85
155
|
### Added
|
86
|
-
- Initial release
|
87
156
|
|
157
|
+
- Initial release
|
88
158
|
|
89
159
|
[0.1.0]: https://github.com/Freaky/monotime/commits/v0.1.0
|
90
160
|
[0.2.0]: https://github.com/Freaky/monotime/commits/v0.2.0
|
@@ -94,5 +164,9 @@
|
|
94
164
|
[0.6.0]: https://github.com/Freaky/monotime/commits/v0.6.0
|
95
165
|
[0.6.1]: https://github.com/Freaky/monotime/commits/v0.6.1
|
96
166
|
[0.7.0]: https://github.com/Freaky/monotime/commits/v0.7.0
|
167
|
+
[0.7.1]: https://github.com/Freaky/monotime/commits/v0.7.0
|
168
|
+
[0.8.0]: https://github.com/Freaky/monotime/commits/v0.7.0
|
97
169
|
[issue #1]: https://github.com/Freaky/monotime/issues/1
|
98
170
|
[@celsworth]: https://github.com/celsworth
|
171
|
+
[@petergoldstein]: https://github.com/petergoldstein
|
172
|
+
[@fig]: https://github.com/fig
|
data/README.md
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
[](https://badge.fury.io/rb/monotime)
|
2
|
-
[](https://travis-ci.org/Freaky/monotime)
|
3
|
-
[](http://inch-ci.org/github/Freaky/monotime)
|
4
|
-
[](https://www.rubydoc.info/gems/monotime)
|
5
1
|
|
6
2
|
# Monotime
|
7
3
|
|
8
4
|
A sensible interface to Ruby's monotonic clock, inspired by Rust.
|
9
5
|
|
6
|
+
[](https://badge.fury.io/rb/monotime)
|
7
|
+
[](https://github.com/Freaky/monotime/actions)
|
8
|
+
[](http://inch-ci.org/github/Freaky/monotime)
|
9
|
+
[](https://www.rubydoc.info/gems/monotime)
|
10
|
+
|
10
11
|
## Installation
|
11
12
|
|
12
13
|
Add this line to your application's Gemfile:
|
@@ -23,10 +24,17 @@ Or install it yourself as:
|
|
23
24
|
|
24
25
|
$ gem install monotime
|
25
26
|
|
26
|
-
`Monotime` is tested on Ruby 2.
|
27
|
+
`Monotime` is tested on Ruby 2.7+, TruffleRuby, and JRuby.
|
27
28
|
|
28
29
|
## Usage
|
29
30
|
|
31
|
+
```ruby
|
32
|
+
require 'monotime'
|
33
|
+
# or, to automatically include Monotime::* in the global scope,
|
34
|
+
# as used by these examples:
|
35
|
+
require 'monotime/include'
|
36
|
+
```
|
37
|
+
|
30
38
|
`Monotime` offers a `Duration` type for describing spans of time, and an
|
31
39
|
`Instant` type for describing points in time. Both operate at nanosecond
|
32
40
|
resolution to the limits of whatever your Ruby implementation supports.
|
@@ -36,8 +44,6 @@ start point, perform the action and then ask for the `Duration` that has elapsed
|
|
36
44
|
since:
|
37
45
|
|
38
46
|
```ruby
|
39
|
-
include Monotime
|
40
|
-
|
41
47
|
start = Instant.now
|
42
48
|
do_something
|
43
49
|
elapsed = start.elapsed
|
@@ -47,9 +53,7 @@ Or use a convenience method:
|
|
47
53
|
|
48
54
|
```ruby
|
49
55
|
elapsed = Duration.measure { do_something }
|
50
|
-
|
51
56
|
# or
|
52
|
-
|
53
57
|
return_value, elapsed = Duration.with_measure { compute_something }
|
54
58
|
```
|
55
59
|
|
@@ -98,34 +102,37 @@ is not yet implemented):
|
|
98
102
|
```ruby
|
99
103
|
# Equivalent
|
100
104
|
sleep(Duration.secs(1).secs) # => 1
|
101
|
-
|
102
|
-
Duration.secs(1).sleep # => 1
|
105
|
+
Duration.secs(1).sleep # => 1
|
103
106
|
```
|
104
107
|
|
105
108
|
So can `Instant`, taking a `Duration` and sleeping until the given `Duration`
|
106
|
-
past the time the `Instant` was created, if any.
|
107
|
-
|
109
|
+
past the time the `Instant` was created, if any. This can be useful for
|
110
|
+
maintaining a precise cadence between tasks:
|
108
111
|
|
109
112
|
```ruby
|
110
|
-
|
113
|
+
interval = Duration.secs(60)
|
114
|
+
start = Instant.now
|
111
115
|
loop do
|
112
|
-
|
113
|
-
|
114
|
-
start
|
115
|
-
# alternative: start.sleep_secs(60)
|
116
|
+
do_stuff
|
117
|
+
start.sleep(interval)
|
118
|
+
start += interval
|
116
119
|
end
|
117
120
|
```
|
118
121
|
|
119
|
-
Or you can declare
|
122
|
+
Or you can declare an `Instant` in the future and sleep to that point:
|
120
123
|
|
121
124
|
```ruby
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
+
interval = Duration.secs(60)
|
126
|
+
deadline = Instant.now + interval
|
127
|
+
loop do
|
128
|
+
do_stuff
|
129
|
+
deadline.sleep
|
130
|
+
deadline += interval
|
131
|
+
end
|
125
132
|
```
|
126
133
|
|
127
134
|
`Instant#sleep` returns a `Duration` which was slept, or a negative `Duration`
|
128
|
-
|
135
|
+
indicating that the desired sleep point was in the past.
|
129
136
|
|
130
137
|
## Duration duck typing
|
131
138
|
|
@@ -153,7 +160,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
153
160
|
|
154
161
|
## Contributing
|
155
162
|
|
156
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/Freaky/monotime
|
163
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/Freaky/monotime>.
|
157
164
|
|
158
165
|
## License
|
159
166
|
|
@@ -163,16 +170,19 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
163
170
|
|
164
171
|
### Core Ruby
|
165
172
|
|
166
|
-
For a zero-dependency alternative, see
|
173
|
+
For a zero-dependency alternative upon which `monotime` is based, see
|
167
174
|
[`Process.clock_gettime`](https://ruby-doc.org/core-2.6.3/Process.html#method-c-clock_gettime).
|
168
|
-
|
169
|
-
|
175
|
+
|
176
|
+
`Process::CLOCK_MONOTONIC` is a safe default, but other options may offer better
|
177
|
+
behaviour in face of NTP frequency skew or suspend/resume and should be evaluated
|
178
|
+
carefully.
|
170
179
|
|
171
180
|
### Other Gems
|
172
181
|
|
173
182
|
[hitimes](https://rubygems.org/gems/hitimes) is a popular and mature alternative
|
174
183
|
which also includes a variety of features for gathering statistics about
|
175
|
-
measurements
|
184
|
+
measurements.
|
176
185
|
|
177
|
-
|
178
|
-
|
186
|
+
[concurrent-ruby](https://rubygems.org/gems/concurrent-ruby) includes
|
187
|
+
`Concurrent.monotonic_time`, which is at the time of writing a trivial proxy to
|
188
|
+
the aforementioned `Process::clock_gettime` with `Process::CLOCK_MONOTONIC`.
|
data/lib/monotime/duration.rb
CHANGED
@@ -8,15 +8,57 @@ module Monotime
|
|
8
8
|
# Create a new +Duration+ of a specified number of nanoseconds, zero by
|
9
9
|
# default.
|
10
10
|
#
|
11
|
-
# Users are strongly advised to use
|
11
|
+
# Users are strongly advised to use +Duration.from_nanos+ instead.
|
12
12
|
#
|
13
13
|
# @param nanos [Integer]
|
14
|
-
# @see
|
14
|
+
# @see from_nanos
|
15
15
|
def initialize(nanos = 0)
|
16
16
|
@ns = Integer(nanos)
|
17
|
+
freeze
|
17
18
|
end
|
18
19
|
|
20
|
+
# A static instance for zero durations
|
21
|
+
ZERO = allocate.tap { |d| d.__send__(:initialize, 0) }
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# The sleep function used by all +Monotime+ sleep functions.
|
25
|
+
#
|
26
|
+
# This function must accept a positive +Float+ number of seconds and return
|
27
|
+
# the +Float+ time slept.
|
28
|
+
#
|
29
|
+
# Defaults to +Kernel.method(:sleep)+
|
30
|
+
#
|
31
|
+
# @overload sleep_function=(function)
|
32
|
+
# @param function [#call]
|
33
|
+
attr_accessor :sleep_function
|
34
|
+
|
35
|
+
# Precision for +Duration#to_s+ if not otherwise specified
|
36
|
+
#
|
37
|
+
# Defaults to 9.
|
38
|
+
#
|
39
|
+
# @overload default_to_s_precision=(precision)
|
40
|
+
# @param precision [Numeric]
|
41
|
+
attr_accessor :default_to_s_precision
|
42
|
+
end
|
43
|
+
|
44
|
+
self.sleep_function = Kernel.method(:sleep)
|
45
|
+
self.default_to_s_precision = 9
|
46
|
+
|
19
47
|
class << self
|
48
|
+
# @!visibility private
|
49
|
+
def new(nanos = 0)
|
50
|
+
return ZERO if nanos.zero?
|
51
|
+
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return a zero +Duration+.
|
56
|
+
#
|
57
|
+
# @return [Duration]
|
58
|
+
def zero
|
59
|
+
ZERO
|
60
|
+
end
|
61
|
+
|
20
62
|
# Generate a new +Duration+ measuring the given number of seconds.
|
21
63
|
#
|
22
64
|
# @param secs [Numeric]
|
@@ -86,7 +128,7 @@ module Monotime
|
|
86
128
|
# @example
|
87
129
|
# (Duration.from_secs(10) + Duration.from_secs(5)).to_s # => "15s"
|
88
130
|
#
|
89
|
-
# @param [Duration, #to_nanos]
|
131
|
+
# @param other [Duration, #to_nanos]
|
90
132
|
# @return [Duration]
|
91
133
|
def +(other)
|
92
134
|
raise TypeError, 'Not one of: [Duration, #to_nanos]' unless other.respond_to?(:to_nanos)
|
@@ -100,7 +142,7 @@ module Monotime
|
|
100
142
|
# @example
|
101
143
|
# (Duration.from_secs(10) - Duration.from_secs(5)).to_s # => "5s"
|
102
144
|
#
|
103
|
-
# @param [Duration, #to_nanos]
|
145
|
+
# @param other [Duration, #to_nanos]
|
104
146
|
# @return [Duration]
|
105
147
|
def -(other)
|
106
148
|
raise TypeError, 'Not one of: [Duration, #to_nanos]' unless other.respond_to?(:to_nanos)
|
@@ -113,7 +155,7 @@ module Monotime
|
|
113
155
|
# @example
|
114
156
|
# (Duration.from_secs(10) / 2).to_s # => "5s"
|
115
157
|
#
|
116
|
-
# @param [Numeric]
|
158
|
+
# @param other [Numeric]
|
117
159
|
# @return [Duration]
|
118
160
|
def /(other)
|
119
161
|
Duration.new(to_nanos / other)
|
@@ -124,7 +166,7 @@ module Monotime
|
|
124
166
|
# @example
|
125
167
|
# (Duration.from_secs(10) * 2).to_s # => "20s"
|
126
168
|
#
|
127
|
-
# @param [Numeric]
|
169
|
+
# @param other [Numeric]
|
128
170
|
# @return [Duration]
|
129
171
|
def *(other)
|
130
172
|
Duration.new(to_nanos * other)
|
@@ -157,7 +199,7 @@ module Monotime
|
|
157
199
|
# Compare the *value* of this +Duration+ with another, or any +#to_nanos+-coercible
|
158
200
|
# object, or nil if not comparable.
|
159
201
|
#
|
160
|
-
# @param [Duration, #to_nanos, Object]
|
202
|
+
# @param other [Duration, #to_nanos, Object]
|
161
203
|
# @return [-1, 0, 1, nil]
|
162
204
|
def <=>(other)
|
163
205
|
to_nanos <=> other.to_nanos if other.respond_to?(:to_nanos)
|
@@ -166,7 +208,7 @@ module Monotime
|
|
166
208
|
# Compare the equality of the *value* of this +Duration+ with another, or
|
167
209
|
# any +#to_nanos+-coercible object, or nil if not comparable.
|
168
210
|
#
|
169
|
-
# @param [Duration, #to_nanos, Object]
|
211
|
+
# @param other [Duration, #to_nanos, Object]
|
170
212
|
# @return [Boolean]
|
171
213
|
def ==(other)
|
172
214
|
other.respond_to?(:to_nanos) && to_nanos == other.to_nanos
|
@@ -174,7 +216,7 @@ module Monotime
|
|
174
216
|
|
175
217
|
# Check equality of the value and type of this +Duration+ with another.
|
176
218
|
#
|
177
|
-
# @param [Duration, Object]
|
219
|
+
# @param other [Duration, Object]
|
178
220
|
# @return [Boolean]
|
179
221
|
def eql?(other)
|
180
222
|
other.is_a?(Duration) && to_nanos == other.to_nanos
|
@@ -184,7 +226,7 @@ module Monotime
|
|
184
226
|
#
|
185
227
|
# @return [Integer]
|
186
228
|
def hash
|
187
|
-
self.class
|
229
|
+
[self.class, to_nanos].hash
|
188
230
|
end
|
189
231
|
|
190
232
|
# Return this +Duration+ in seconds.
|
@@ -254,6 +296,8 @@ module Monotime
|
|
254
296
|
# Sleep for the duration of this +Duration+. Equivalent to
|
255
297
|
# +Kernel.sleep(duration.to_secs)+.
|
256
298
|
#
|
299
|
+
# The sleep function may be overridden globally using +Duration.sleep_function=+
|
300
|
+
#
|
257
301
|
# @example
|
258
302
|
# Duration.from_secs(1).sleep # => 1
|
259
303
|
# Duration.from_secs(-1).sleep # => raises NotImplementedError
|
@@ -261,10 +305,11 @@ module Monotime
|
|
261
305
|
# @raise [NotImplementedError] negative +Duration+ sleeps are not yet supported.
|
262
306
|
# @return [Integer]
|
263
307
|
# @see Instant#sleep
|
308
|
+
# @see sleep_function=
|
264
309
|
def sleep
|
265
310
|
raise NotImplementedError, 'time travel module missing' if negative?
|
266
311
|
|
267
|
-
|
312
|
+
self.class.sleep_function.call(to_secs)
|
268
313
|
end
|
269
314
|
|
270
315
|
DIVISORS = [
|
@@ -279,6 +324,8 @@ module Monotime
|
|
279
324
|
# Format this +Duration+ into a human-readable string, with a given number
|
280
325
|
# of decimal places.
|
281
326
|
#
|
327
|
+
# The default precision may be set globally using +Duration.default_to_s_precision=+
|
328
|
+
#
|
282
329
|
# The exact format is subject to change, users with specific requirements
|
283
330
|
# are encouraged to use their own formatting methods.
|
284
331
|
#
|
@@ -292,7 +339,8 @@ module Monotime
|
|
292
339
|
#
|
293
340
|
# @param precision [Integer] the maximum number of decimal places
|
294
341
|
# @return [String]
|
295
|
-
|
342
|
+
# @see default_to_s_precision=
|
343
|
+
def to_s(precision = self.class.default_to_s_precision)
|
296
344
|
precision = Integer(precision).abs
|
297
345
|
div, unit = DIVISORS.find { |d, _| to_nanos.abs >= d }
|
298
346
|
|
data/lib/monotime/instant.rb
CHANGED
@@ -11,14 +11,108 @@ module Monotime
|
|
11
11
|
|
12
12
|
include Comparable
|
13
13
|
|
14
|
+
class << self
|
15
|
+
attr_writer :clock_id
|
16
|
+
|
17
|
+
# The symbolic name of the automatically-selected +Process.clock_gettime+
|
18
|
+
# clock id, if available. This will *not* reflect a manually-set +clock_id+.
|
19
|
+
#
|
20
|
+
# @return [Symbol, nil]
|
21
|
+
attr_reader :clock_name
|
22
|
+
|
23
|
+
# The function used to create +Instant+ instances.
|
24
|
+
#
|
25
|
+
# This function must return a +Numeric+, monotonic count of nanoseconds
|
26
|
+
# since a fixed point in the past.
|
27
|
+
#
|
28
|
+
# Defaults to `lambda { Process.clock_gettime(clock_id, :nanosecond) }`.
|
29
|
+
#
|
30
|
+
# @overload monotonic_function=(function)
|
31
|
+
# @param function [#call]
|
32
|
+
attr_accessor :monotonic_function
|
33
|
+
|
34
|
+
# @overload clock_id
|
35
|
+
# The configured or detected +Process.clock_getime+ clock identifier.
|
36
|
+
#
|
37
|
+
# Raises +NotImplementedError+ if a suitable monotonic clock source cannot
|
38
|
+
# be found and one has not been specified manually.
|
39
|
+
#
|
40
|
+
# @return [Numeric]
|
41
|
+
#
|
42
|
+
# @overload clock_id=(id)
|
43
|
+
# The +Process.clock_gettime+ clock id used to create +Instant+ instances
|
44
|
+
# by the default monotonic function.
|
45
|
+
#
|
46
|
+
# Suggested options include but are not limited to:
|
47
|
+
#
|
48
|
+
# * +Process::CLOCK_MONOTONIC_RAW+
|
49
|
+
# * +Process::CLOCK_UPTIME_RAW+
|
50
|
+
# * +Process::CLOCK_UPTIME_PRECISE+
|
51
|
+
# * +Process::CLOCK_UPTIME_FAST+
|
52
|
+
# * +Process::CLOCK_UPTIME+
|
53
|
+
# * +Process::CLOCK_MONOTONIC_PRECISE+
|
54
|
+
# * +Process::CLOCK_MONOTONIC_FAST+
|
55
|
+
# * +Process::CLOCK_MONOTONIC+
|
56
|
+
#
|
57
|
+
# These are platform-dependant and may vary in resolution, performance,
|
58
|
+
# and behaviour from NTP frequency skew and system suspend/resume.
|
59
|
+
#
|
60
|
+
# It is possible to set non-monotonic clock sources here. You probably
|
61
|
+
# shouldn't.
|
62
|
+
#
|
63
|
+
# Defaults to auto-detect.
|
64
|
+
#
|
65
|
+
# @param id [Numeric]
|
66
|
+
def clock_id
|
67
|
+
@clock_id ||= detect_clock_id
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the claimed resolution of the given clock id or the configured
|
71
|
+
# +clock_id+, as a +Duration+, or +nil+ if invalid.
|
72
|
+
#
|
73
|
+
# @param clock [Numeric] Optional clock id instead of default.
|
74
|
+
def clock_getres(clock = @clock_id)
|
75
|
+
Duration.from_nanos(Process.clock_getres(clock, :nanosecond))
|
76
|
+
rescue SystemCallError
|
77
|
+
# suppress errors
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def detect_clock_id
|
83
|
+
name, id, =
|
84
|
+
[
|
85
|
+
:CLOCK_MONOTONIC_RAW, # Linux, not affected by NTP frequency adjustments
|
86
|
+
:CLOCK_UPTIME_RAW, # macOS, not affected by NTP frequency adjustments
|
87
|
+
:CLOCK_UPTIME_PRECISE, # FreeBSD, increments while system is running
|
88
|
+
:CLOCK_UPTIME, # OpenBSD, increments while system is running
|
89
|
+
:CLOCK_MONOTONIC_PRECISE, # FreeBSD, precise monotonic clock
|
90
|
+
:CLOCK_MONOTONIC, # Standard cross-platform monotonic clock
|
91
|
+
]
|
92
|
+
.each_with_index # Used to force a stable sort in min_by
|
93
|
+
.filter { |name, _| Process.const_defined?(name) }
|
94
|
+
.map { |name, index| [name, Process.const_get(name), index] }
|
95
|
+
.filter_map { |clock| clock.insert(2, clock_getres(clock[1])) }
|
96
|
+
.min_by { |clock| clock[2..] } # find smallest resolution and index
|
97
|
+
.tap { |clock| raise NotImplementedError, 'No usable clock' unless clock }
|
98
|
+
|
99
|
+
@clock_name = name
|
100
|
+
id
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
self.monotonic_function = -> { Process.clock_gettime(clock_id, :nanosecond) }
|
105
|
+
clock_id # detect our clock_id early
|
106
|
+
|
14
107
|
# Create a new +Instant+ from an optional nanosecond measurement.
|
15
108
|
#
|
16
109
|
# Users should generally *not* pass anything to this function.
|
17
110
|
#
|
18
111
|
# @param nanos [Integer]
|
19
112
|
# @see #now
|
20
|
-
def initialize(nanos =
|
113
|
+
def initialize(nanos = self.class.monotonic_function.call)
|
21
114
|
@ns = Integer(nanos)
|
115
|
+
freeze
|
22
116
|
end
|
23
117
|
|
24
118
|
# An alias to +new+, and generally preferred over it.
|
@@ -111,8 +205,8 @@ module Monotime
|
|
111
205
|
# Sugar for +#elapsed.to_s+.
|
112
206
|
#
|
113
207
|
# @see Duration#to_s
|
114
|
-
def to_s(
|
115
|
-
elapsed.to_s(
|
208
|
+
def to_s(...)
|
209
|
+
elapsed.to_s(...)
|
116
210
|
end
|
117
211
|
|
118
212
|
# Add a +Duration+ or +#to_nanos+-coercible object to this +Instant+, returning
|
@@ -172,7 +266,7 @@ module Monotime
|
|
172
266
|
#
|
173
267
|
# @return [Integer]
|
174
268
|
def hash
|
175
|
-
self.class
|
269
|
+
[self.class, @ns].hash
|
176
270
|
end
|
177
271
|
end
|
178
272
|
end
|
data/lib/monotime/version.rb
CHANGED
data/monotime.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.require_paths = ["lib"]
|
33
33
|
|
34
34
|
spec.add_development_dependency "bundler", "~> 2"
|
35
|
-
spec.add_development_dependency "rake", "~>
|
35
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
36
36
|
spec.add_development_dependency "minitest", "~> 5.0"
|
37
|
+
spec.add_development_dependency "simplecov", "~> 0.21"
|
37
38
|
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.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Hurst
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,16 +52,30 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
-
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.21'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.21'
|
69
|
+
description:
|
56
70
|
email:
|
57
71
|
- tom@hur.st
|
58
72
|
executables: []
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
76
|
+
- ".github/workflows/ci.yml"
|
62
77
|
- ".gitignore"
|
63
78
|
- ".rubocop.yml"
|
64
|
-
- ".travis.yml"
|
65
79
|
- CHANGELOG.md
|
66
80
|
- Gemfile
|
67
81
|
- LICENSE.txt
|
@@ -71,6 +85,7 @@ files:
|
|
71
85
|
- bin/setup
|
72
86
|
- lib/monotime.rb
|
73
87
|
- lib/monotime/duration.rb
|
88
|
+
- lib/monotime/include.rb
|
74
89
|
- lib/monotime/instant.rb
|
75
90
|
- lib/monotime/version.rb
|
76
91
|
- monotime.gemspec
|
@@ -79,7 +94,7 @@ licenses:
|
|
79
94
|
- MIT
|
80
95
|
metadata:
|
81
96
|
allowed_push_host: https://rubygems.org
|
82
|
-
post_install_message:
|
97
|
+
post_install_message:
|
83
98
|
rdoc_options: []
|
84
99
|
require_paths:
|
85
100
|
- lib
|
@@ -94,9 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
109
|
- !ruby/object:Gem::Version
|
95
110
|
version: '0'
|
96
111
|
requirements: []
|
97
|
-
|
98
|
-
|
99
|
-
signing_key:
|
112
|
+
rubygems_version: 3.4.19
|
113
|
+
signing_key:
|
100
114
|
specification_version: 4
|
101
115
|
summary: A sensible interface to the monotonic clock
|
102
116
|
test_files: []
|