rspec-rewind 0.1.0 → 1.0.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/CHANGELOG.md +28 -1
- data/README.md +42 -124
- data/lib/rspec/rewind/api.rb +37 -0
- data/lib/rspec/rewind/attempt_runner.rb +7 -2
- data/lib/rspec/rewind/backoff.rb +26 -2
- data/lib/rspec/rewind/configuration.rb +176 -30
- data/lib/rspec/rewind/configuration_validation.rb +75 -0
- data/lib/rspec/rewind/core.rb +150 -0
- data/lib/rspec/rewind/event.rb +67 -16
- data/lib/rspec/rewind/example_state_resetter.rb +14 -42
- data/lib/rspec/rewind/failure_fingerprint.rb +19 -0
- data/lib/rspec/rewind/flaky_reporter.rb +18 -16
- data/lib/rspec/rewind/flaky_transition.rb +20 -3
- data/lib/rspec/rewind/matcher_validation.rb +17 -4
- data/lib/rspec/rewind/retry_budget.rb +126 -3
- data/lib/rspec/rewind/retry_count_resolver.rb +26 -3
- data/lib/rspec/rewind/retry_decision.rb +128 -20
- data/lib/rspec/rewind/retry_delay_resolver.rb +57 -6
- data/lib/rspec/rewind/retry_event_builder.rb +73 -6
- data/lib/rspec/rewind/retry_gate.rb +100 -6
- data/lib/rspec/rewind/retry_loop.rb +53 -6
- data/lib/rspec/rewind/retry_notifier.rb +55 -8
- data/lib/rspec/rewind/retry_policy.rb +137 -9
- data/lib/rspec/rewind/retry_summary.rb +61 -0
- data/lib/rspec/rewind/retry_transition.rb +189 -8
- data/lib/rspec/rewind/rspec_adapter.rb +47 -0
- data/lib/rspec/rewind/runner.rb +5 -4
- data/lib/rspec/rewind/runner_component_factory.rb +16 -0
- data/lib/rspec/rewind/runner_components.rb +11 -5
- data/lib/rspec/rewind/version.rb +1 -1
- data/lib/rspec/rewind.rb +2 -65
- data/sig/rspec/rewind.rbs +290 -24
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df7affb556767810fad03faf4d94602e90e3f38af615e04742064acf7c1a95a9
|
|
4
|
+
data.tar.gz: 5d176192d59025af23f2509be6e2161bca3195c05215f7f4008ecc3e69bb187a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ab4033f522b2bdd412113befc6a36470f2ea9847ac0b3c6a1e16c6aec8a4db6114b5fe712638be9ab6e0b52bb707b305c70214caf6a4e7c1a9fb4e7295a984c
|
|
7
|
+
data.tar.gz: 582d1716789ab6e374b682a9dc5b153e2815b57bddbcf6a018944c57acc65de1bfc4571c7beb55e623bcadc4051e9c908a37b5de8b4acb31b7748eb44b651f42
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added `rspec/rewind/core` for loading the API without auto-installing the RSpec hook.
|
|
13
|
+
- Added `RSPEC_REWIND_AUTO_INSTALL` and `RSPEC_REWIND_DISABLE` controls.
|
|
14
|
+
- Added retry lifecycle hooks: `before_retry`, `after_retry`, and `not_retried_callback`.
|
|
15
|
+
- Added retry summary and flaky-threshold controls: `display_retry_summary`, `fail_on_flaky`, and `max_flaky_examples`.
|
|
16
|
+
- Added retry limits and dry-run controls: `max_retries`, `max_elapsed_time`, `max_total_sleep`, and `dry_run`.
|
|
17
|
+
- Added process-aware retry budgets with `RSpec::Rewind::FileRetryBudget`.
|
|
18
|
+
- Added richer retry events with decision reasons, matcher details, failure fingerprints, timing, sleep, budget, and selected metadata fields.
|
|
19
|
+
- Added `not_retried` and `reset_failed` event reporting, plus `reset_failure_policy`.
|
|
20
|
+
- Added advanced policy controls for `retry_if_mode`, `retry_on_default`, and metadata append/override modes.
|
|
21
|
+
- Added injectable jitter RNG support for exponential backoff.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Retry event payloads are immutable.
|
|
26
|
+
- Runner configuration is snapshotted per example so later configuration changes do not affect in-flight retries.
|
|
27
|
+
- Flaky reporter path and reporter objects stay synchronized, and reporters are flushed and closed at suite end.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Clamp retry sleep before sleeping when `max_total_sleep` is configured.
|
|
32
|
+
- Prevent `not_retried_callback` errors from masking the original suite result.
|
|
33
|
+
- Keep reporter lifecycle failures from interrupting suite shutdown unless strict callbacks are enabled.
|
|
34
|
+
- Revalidate existing retry matchers when strict matcher validation is enabled.
|
|
35
|
+
- Validate exponential backoff jitter RNG output and keep jittered delays within the configured maximum.
|
|
36
|
+
|
|
10
37
|
## 0.1.0 (2026-02-07)
|
|
11
38
|
|
|
12
|
-
- Initial release.
|
|
39
|
+
- Initial release.
|
data/README.md
CHANGED
|
@@ -1,41 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
Deterministic retry orchestration for flaky RSpec examples.
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
<img src="https://img.shields.io/badge/ruby-%3E%3D%203.1-ruby.svg" alt="Ruby Version">
|
|
9
|
-
<img src="https://img.shields.io/badge/rspec--core-%3E%3D%203.12%2C%20%3C%204.0-brightgreen.svg" alt="RSpec Core Version">
|
|
10
|
-
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
|
|
11
|
-
<a href="https://badge.fury.io/rb/rspec-rewind"><img src="https://badge.fury.io/rb/rspec-rewind.svg" alt="Gem Version" height="18"></a>
|
|
12
|
-
<a href="https://github.com/ydah/rspec-rewind/actions/workflows/main.yml">
|
|
13
|
-
<img src="https://github.com/ydah/rspec-rewind/actions/workflows/main.yml/badge.svg" alt="CI Status">
|
|
14
|
-
</a>
|
|
15
|
-
</p>
|
|
16
|
-
|
|
17
|
-
<p align="center">
|
|
18
|
-
<a href="#installation">Installation</a> •
|
|
19
|
-
<a href="#quick-start">Quick Start</a> •
|
|
20
|
-
<a href="#per-example-controls">Per-Example Controls</a> •
|
|
21
|
-
<a href="#configuration">Configuration</a> •
|
|
22
|
-
<a href="#observability">Observability</a> •
|
|
23
|
-
<a href="#compatibility">Compatibility</a>
|
|
24
|
-
</p>
|
|
25
|
-
|
|
26
|
-
`rspec-rewind` is a modern retry gem for RSpec, inspired by [`rspec-retry`](https://github.com/NoRedInk/rspec-retry), with deterministic control and flaky-test observability for CI-heavy projects.
|
|
27
|
-
|
|
28
|
-
## Why rspec-rewind
|
|
29
|
-
|
|
30
|
-
- `retries` always means "extra attempts" (not total attempts).
|
|
31
|
-
- Retry filtering with `retry_on`, `skip_retry_on`, and `retry_if`.
|
|
32
|
-
- Configurable delay via fixed, linear, exponential, or custom backoff.
|
|
33
|
-
- Suite-level retry budget to prevent hidden retry inflation.
|
|
34
|
-
- Flaky detection hooks and optional JSONL reporting.
|
|
1
|
+
# rspec-rewind
|
|
35
2
|
|
|
36
|
-
|
|
3
|
+
Deterministic retry orchestration for flaky RSpec examples.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/rb/rspec-rewind)
|
|
6
|
+
[](https://github.com/ydah/rspec-rewind/actions/workflows/main.yml)
|
|
7
|
+
[](LICENSE.txt)
|
|
37
8
|
|
|
38
|
-
|
|
9
|
+
`rspec-rewind` retries RSpec examples with explicit retry counts, exception filters,
|
|
10
|
+
backoff strategies, retry budgets, and flaky-test reporting.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
39
13
|
|
|
40
14
|
```ruby
|
|
41
15
|
gem "rspec-rewind"
|
|
@@ -47,9 +21,10 @@ Then run:
|
|
|
47
21
|
bundle install
|
|
48
22
|
```
|
|
49
23
|
|
|
50
|
-
##
|
|
24
|
+
## Usage
|
|
51
25
|
|
|
52
|
-
`
|
|
26
|
+
Require `rspec/rewind` from your RSpec setup file. This installs the around hook
|
|
27
|
+
automatically.
|
|
53
28
|
|
|
54
29
|
```ruby
|
|
55
30
|
# spec/spec_helper.rb
|
|
@@ -57,15 +32,16 @@ require "rspec/rewind"
|
|
|
57
32
|
|
|
58
33
|
RSpec::Rewind.configure do |config|
|
|
59
34
|
config.default_retries = 1
|
|
60
|
-
config.backoff = RSpec::Rewind::Backoff.exponential(base: 0.1, factor: 2.0, max: 1.0, jitter: 0.2)
|
|
61
35
|
config.retry_on = [Net::ReadTimeout, Errno::ECONNRESET]
|
|
62
36
|
config.skip_retry_on = [NoMethodError]
|
|
37
|
+
config.backoff = RSpec::Rewind::Backoff.exponential(base: 0.1, factor: 2.0, max: 1.0)
|
|
63
38
|
config.retry_budget = 20
|
|
64
39
|
config.flaky_report_path = "tmp/rspec-rewind/flaky.jsonl"
|
|
65
40
|
end
|
|
66
41
|
```
|
|
67
42
|
|
|
68
|
-
|
|
43
|
+
Retry counts are extra attempts. `rewind: 2` can run the example up to three
|
|
44
|
+
times total.
|
|
69
45
|
|
|
70
46
|
```ruby
|
|
71
47
|
it "eventually becomes consistent", rewind: 2 do
|
|
@@ -76,7 +52,7 @@ end
|
|
|
76
52
|
## Per-Example Controls
|
|
77
53
|
|
|
78
54
|
```ruby
|
|
79
|
-
it "
|
|
55
|
+
it "retries transient API failures",
|
|
80
56
|
rewind: 3,
|
|
81
57
|
rewind_wait: 0.2,
|
|
82
58
|
rewind_retry_on: [Net::ReadTimeout, /502/],
|
|
@@ -86,37 +62,19 @@ it "uses metadata overrides",
|
|
|
86
62
|
end
|
|
87
63
|
```
|
|
88
64
|
|
|
89
|
-
|
|
|
65
|
+
| Key | Meaning |
|
|
90
66
|
| --- | --- |
|
|
91
|
-
| `rewind` |
|
|
92
|
-
| `rewind_wait` | Fixed
|
|
93
|
-
| `rewind_backoff` |
|
|
94
|
-
| `rewind_retry_on` |
|
|
95
|
-
| `rewind_skip_retry_on` |
|
|
96
|
-
| `rewind_if` | Predicate
|
|
67
|
+
| `rewind` | Extra retry count. `true` uses the configured default; `false` disables retries. |
|
|
68
|
+
| `rewind_wait` | Fixed delay before the next attempt. |
|
|
69
|
+
| `rewind_backoff` | Numeric or callable backoff strategy. |
|
|
70
|
+
| `rewind_retry_on` | Additional retry allow-list matchers. |
|
|
71
|
+
| `rewind_skip_retry_on` | Additional retry deny-list matchers. Checked before allow-list matchers. |
|
|
72
|
+
| `rewind_if` | Predicate that decides whether the failure should retry. |
|
|
97
73
|
|
|
98
|
-
|
|
74
|
+
`retry_on` and `skip_retry_on` matchers can be exception classes, regexps, or
|
|
75
|
+
callables.
|
|
99
76
|
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
RSpec::Rewind.configure do |config|
|
|
104
|
-
config.default_retries = 0
|
|
105
|
-
config.backoff = RSpec::Rewind::Backoff.fixed(0)
|
|
106
|
-
config.retry_on = []
|
|
107
|
-
config.skip_retry_on = []
|
|
108
|
-
config.retry_if = nil
|
|
109
|
-
config.retry_callback = nil
|
|
110
|
-
config.flaky_callback = nil
|
|
111
|
-
config.retry_budget = nil
|
|
112
|
-
config.flaky_report_path = nil
|
|
113
|
-
config.verbose = false
|
|
114
|
-
config.display_retry_failure_messages = false
|
|
115
|
-
config.clear_lets_on_failure = true
|
|
116
|
-
end
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Backoff helpers:
|
|
77
|
+
## Backoff
|
|
120
78
|
|
|
121
79
|
```ruby
|
|
122
80
|
RSpec::Rewind::Backoff.fixed(0.2)
|
|
@@ -124,39 +82,18 @@ RSpec::Rewind::Backoff.linear(step: 0.1, max: 1.0)
|
|
|
124
82
|
RSpec::Rewind::Backoff.exponential(base: 0.1, factor: 2.0, max: 2.0, jitter: 0.2)
|
|
125
83
|
```
|
|
126
84
|
|
|
127
|
-
Environment override:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
RSPEC_REWIND_RETRIES=2 bundle exec rspec
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
`RSPEC_REWIND_RETRIES` has highest priority over defaults and metadata.
|
|
134
|
-
|
|
135
|
-
## Retry Decision Order
|
|
136
|
-
|
|
137
|
-
1. Stop if no exception happened.
|
|
138
|
-
2. Stop if exception matches any `skip_retry_on`.
|
|
139
|
-
3. If `retry_on` is set, stop unless exception matches at least one matcher.
|
|
140
|
-
4. If `retry_if` exists, retry only when predicate returns truthy.
|
|
141
|
-
5. Stop if retry budget is exhausted.
|
|
142
|
-
|
|
143
85
|
## Observability
|
|
144
86
|
|
|
145
|
-
|
|
87
|
+
Use callbacks when you want to send retry events to logs or metrics.
|
|
146
88
|
|
|
147
89
|
```ruby
|
|
148
90
|
RSpec::Rewind.configure do |config|
|
|
149
|
-
config.retry_callback = ->(event)
|
|
150
|
-
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
config.flaky_callback = ->(event) do
|
|
154
|
-
puts "[flaky] #{event.description} (attempt #{event.attempt})"
|
|
155
|
-
end
|
|
91
|
+
config.retry_callback = ->(event) { warn "[retry] #{event.example_id} attempt=#{event.attempt}" }
|
|
92
|
+
config.flaky_callback = ->(event) { warn "[flaky] #{event.example_id}" }
|
|
156
93
|
end
|
|
157
94
|
```
|
|
158
95
|
|
|
159
|
-
|
|
96
|
+
Set `flaky_report_path` to write flaky examples as JSONL:
|
|
160
97
|
|
|
161
98
|
```ruby
|
|
162
99
|
RSpec::Rewind.configure do |config|
|
|
@@ -164,21 +101,14 @@ RSpec::Rewind.configure do |config|
|
|
|
164
101
|
end
|
|
165
102
|
```
|
|
166
103
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
- `attempt`
|
|
176
|
-
- `retries`
|
|
177
|
-
- `exception_class`
|
|
178
|
-
- `exception_message`
|
|
179
|
-
- `duration`
|
|
180
|
-
- `sleep_seconds`
|
|
181
|
-
- `timestamp`
|
|
104
|
+
## Environment
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
RSPEC_REWIND_RETRIES=2 bundle exec rspec
|
|
108
|
+
RSPEC_REWIND_DISABLE=1 bundle exec rspec
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`RSPEC_REWIND_RETRIES` takes priority over configured defaults and metadata.
|
|
182
112
|
|
|
183
113
|
## Compatibility
|
|
184
114
|
|
|
@@ -188,24 +118,12 @@ Each flaky JSONL row includes:
|
|
|
188
118
|
## Development
|
|
189
119
|
|
|
190
120
|
```bash
|
|
191
|
-
bundle
|
|
121
|
+
bundle install
|
|
122
|
+
bundle exec rake spec
|
|
123
|
+
bundle exec rake rubocop
|
|
192
124
|
bundle exec rake rbs
|
|
193
125
|
```
|
|
194
126
|
|
|
195
|
-
CI validates:
|
|
196
|
-
|
|
197
|
-
- Specs across Ruby 3.1, 3.2, 3.3, 3.4, 4.0, and head
|
|
198
|
-
- Minimum compatibility with RSpec 3.12 (`BUNDLE_GEMFILE=gemfiles/rspec_3_12.gemfile`)
|
|
199
|
-
- Type signatures (`rake rbs`)
|
|
200
|
-
- Coverage threshold (`COVERAGE=1 rspec`)
|
|
201
|
-
- Gem packaging (`rake build`)
|
|
202
|
-
- Dependency security audit (`bundler-audit`)
|
|
203
|
-
|
|
204
|
-
## Contributing
|
|
205
|
-
|
|
206
|
-
Bug reports and pull requests are welcome on GitHub:
|
|
207
|
-
https://github.com/ydah/rspec-rewind
|
|
208
|
-
|
|
209
127
|
## License
|
|
210
128
|
|
|
211
129
|
Released under the [MIT License](LICENSE.txt).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Rewind
|
|
5
|
+
PUBLIC_API = [
|
|
6
|
+
Backoff,
|
|
7
|
+
Configuration,
|
|
8
|
+
Event,
|
|
9
|
+
FileRetryBudget,
|
|
10
|
+
FlakyReporter,
|
|
11
|
+
RetryBudget,
|
|
12
|
+
RetryDecision,
|
|
13
|
+
RetryPolicy,
|
|
14
|
+
Runner
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
INTERNAL_API = [
|
|
18
|
+
AttemptRunner,
|
|
19
|
+
ExampleContext,
|
|
20
|
+
ExampleMethods,
|
|
21
|
+
ExampleStateResetter,
|
|
22
|
+
FailureFingerprint,
|
|
23
|
+
FlakyTransition,
|
|
24
|
+
RetryCountResolver,
|
|
25
|
+
RetryDelayResolver,
|
|
26
|
+
RetryEventBuilder,
|
|
27
|
+
RetryGate,
|
|
28
|
+
RetryLoop,
|
|
29
|
+
RetryNotifier,
|
|
30
|
+
RetryTransition,
|
|
31
|
+
RunnerComponentFactory,
|
|
32
|
+
RunnerComponents,
|
|
33
|
+
RunnerLogger,
|
|
34
|
+
RSpecAdapter
|
|
35
|
+
].freeze
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -11,6 +11,10 @@ module RSpec
|
|
|
11
11
|
SecurityError
|
|
12
12
|
].freeze
|
|
13
13
|
|
|
14
|
+
def initialize(clock: nil)
|
|
15
|
+
@clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
def run(run_target:, exception_source:)
|
|
15
19
|
started_at = monotonic_time
|
|
16
20
|
|
|
@@ -18,7 +22,8 @@ module RSpec
|
|
|
18
22
|
run_target.run
|
|
19
23
|
duration = monotonic_time - started_at
|
|
20
24
|
[current_exception(exception_source), duration, false]
|
|
21
|
-
|
|
25
|
+
# RSpec expectation failures inherit from Exception, so fatal exceptions are filtered explicitly.
|
|
26
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
22
27
|
raise if fatal_exception?(e)
|
|
23
28
|
|
|
24
29
|
duration = monotonic_time - started_at
|
|
@@ -29,7 +34,7 @@ module RSpec
|
|
|
29
34
|
private
|
|
30
35
|
|
|
31
36
|
def monotonic_time
|
|
32
|
-
|
|
37
|
+
@clock.call
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
def current_exception(exception_source)
|
data/lib/rspec/rewind/backoff.rb
CHANGED
|
@@ -20,11 +20,13 @@ module RSpec
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def exponential(base:, factor: 2.0, max: nil, jitter: 0.0)
|
|
23
|
+
def exponential(base:, factor: 2.0, max: nil, jitter: 0.0, rng: Kernel, min_factor: 0.0)
|
|
24
24
|
base_value = normalize_numeric(base, 'base')
|
|
25
25
|
factor_value = normalize_numeric(factor, 'factor')
|
|
26
|
+
min_factor_value = normalize_numeric(min_factor, 'min_factor')
|
|
26
27
|
jitter_value = normalize_numeric(jitter, 'jitter')
|
|
27
28
|
max_value = max.nil? ? nil : normalize_numeric(max, 'max')
|
|
29
|
+
raise ArgumentError, "factor must be >= #{min_factor_value}" if factor_value < min_factor_value
|
|
28
30
|
|
|
29
31
|
lambda do |retry_number:, **_|
|
|
30
32
|
exponent = [retry_number.to_i - 1, 0].max
|
|
@@ -36,7 +38,7 @@ module RSpec
|
|
|
36
38
|
spread = delay * jitter_value
|
|
37
39
|
min_delay = [delay - spread, 0.0].max
|
|
38
40
|
max_delay = delay + spread
|
|
39
|
-
(
|
|
41
|
+
clamp((random_value(rng) * (max_delay - min_delay)) + min_delay, max_value)
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -59,6 +61,28 @@ module RSpec
|
|
|
59
61
|
number
|
|
60
62
|
end
|
|
61
63
|
private_class_method :normalize_numeric
|
|
64
|
+
|
|
65
|
+
def random_value(rng)
|
|
66
|
+
raw =
|
|
67
|
+
if rng.respond_to?(:rand)
|
|
68
|
+
rng.rand
|
|
69
|
+
elsif rng.respond_to?(:call)
|
|
70
|
+
rng.call
|
|
71
|
+
else
|
|
72
|
+
raise ArgumentError, 'rng must respond to #rand or #call'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
value = begin
|
|
76
|
+
Float(raw)
|
|
77
|
+
rescue TypeError, ArgumentError
|
|
78
|
+
raise ArgumentError, 'rng must return a numeric value'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
raise ArgumentError, 'rng must return a value between 0 and 1' unless value.between?(0.0, 1.0)
|
|
82
|
+
|
|
83
|
+
value
|
|
84
|
+
end
|
|
85
|
+
private_class_method :random_value
|
|
62
86
|
end
|
|
63
87
|
end
|
|
64
88
|
end
|