consistent_random 1.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +117 -10
- data/VERSION +1 -1
- data/lib/consistent_random/active_job.rb +34 -0
- data/lib/consistent_random/rack_middleware.rb +9 -2
- data/lib/consistent_random/sidekiq_client_middleware.rb +20 -0
- data/lib/consistent_random/sidekiq_middleware.rb +21 -1
- data/lib/consistent_random.rb +91 -20
- metadata +4 -3
- data/lib/consistent_random/context.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53c979ca8b32310fc3357e231ed29a029964396e19f1bafd0ac44220be535a1a
|
4
|
+
data.tar.gz: 35e7431d888b473db4063a8cec225314aae0e445740388a97ee9296d5a559c03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbc32f5cbbf0ad48742e9ede2e14a187530a32b2e832a0b9af5b293c9716f3e9893795601853ccc4b32ad9cbb24b70decb88f9b94f2c17b7ba687bfca2cd1810
|
7
|
+
data.tar.gz: 80ebaa9bfd1ba8519e755369cf76184e89c97c88302979a7b4fe0d176fbb107a3734a9fa2aa820ad80d0f5f3b370f7755b721080be7ec79219df48a0e6c5d3a9
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 2.1.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added optional seed block to the Rack middleware to allow for custom seed values based on the request.
|
12
|
+
- Added helper method `ConsistentRandom::SidekiqMiddleware.install` to install both the client and server middlewares in one call.
|
13
|
+
|
14
|
+
## 2.0.0
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- `ConsistentRandom#rand` uses a faster hashing algorithm for generating random values. This will make the value consistent across different Ruby versions and platforms.
|
19
|
+
|
20
|
+
### Added
|
21
|
+
|
22
|
+
- Added `ConsistentRandom::SidekiqClientMiddleware` to allow persisting consistent random seeds to Sidekiq jobs so behavior is consistent between when jobs are enqueued and when they are executed.
|
23
|
+
- Added `ConsistentRandom::ActiveJob` for hooking into ActiveJob to persist consistent random seeds to jobs.
|
24
|
+
|
7
25
|
## 1.0.0
|
8
26
|
|
9
27
|
### Added
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Consistent Random
|
2
2
|
|
3
3
|
[![Continuous Integration](https://github.com/bdurand/consistent_random/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/consistent_random/actions/workflows/continuous_integration.yml)
|
4
4
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
@@ -6,13 +6,25 @@
|
|
6
6
|
|
7
7
|
## Introduction
|
8
8
|
|
9
|
-
This Ruby gem allows you to generate consistent random values tied to a specific name within a defined scope. It ensures that random behavior remains consistent within a particular context
|
9
|
+
This Ruby gem allows you to generate consistent random values tied to a specific name within a defined scope. It ensures that random behavior remains consistent within a particular context.
|
10
10
|
|
11
|
-
|
11
|
+
Consistent Random is designed to simplify feature rollouts and other scenarios where you need to generate random values, but need those values to remain consistent within defined contexts.
|
12
|
+
|
13
|
+
For example, consider rolling out a new feature to a subset of requests. You may want to do this to allow testing a new feature by only enabling it for 10% of requests. You want to randomize which requests get the new feature, but ensure that within each request, the feature is consistently enabled or disabled across all actions. This gem allows you to achieve that by tying random values to specific names and defining a scope. Within that scope, the same value will be consistently generated for each named variable.
|
14
|
+
|
15
|
+
## Table of Contents
|
16
|
+
- [Usage](#usage)
|
17
|
+
- [Middlewares](#middlewares)
|
18
|
+
- [Rack Middleware](#rack-middleware)
|
19
|
+
- [Sidekiq Middleware](#sidekiq-middleware)
|
20
|
+
- [ActiveJob](#activejob)
|
21
|
+
- [Installation](#installation)
|
22
|
+
- [Contributing](#contributing)
|
23
|
+
- [License](#license)
|
12
24
|
|
13
25
|
## Usage
|
14
26
|
|
15
|
-
To generate consistent random values, you need to define a scope.
|
27
|
+
To generate consistent random values, you need to define a scope. Scopes are defined with the `ConsistentRandom.scope` method. Within the scope block, calls to `ConsistentRandom` will return the same random values for the same name. Scopes are isolated to the block in which they're defined, meaning random values are consistent within each scoped block but independent across threads or separate invocations.
|
16
28
|
|
17
29
|
```ruby
|
18
30
|
ConsistentRandom.scope do
|
@@ -65,11 +77,11 @@ random = ConsistentRandom.new("foobar")
|
|
65
77
|
random.rand != random.rand # => true
|
66
78
|
```
|
67
79
|
|
68
|
-
|
80
|
+
## Middlewares
|
69
81
|
|
70
|
-
The gem provides built-in middlewares for Rack
|
82
|
+
The gem provides built-in middlewares for Rack, Sidekiq, and ActiveJob. These middlewares allow you to automatically scope web requests and propagate consistent random values from the original request to asynchronous jobs.
|
71
83
|
|
72
|
-
|
84
|
+
### Rack Middleware
|
73
85
|
|
74
86
|
In a Rack application:
|
75
87
|
|
@@ -87,14 +99,109 @@ Or in a Rails application:
|
|
87
99
|
config.middleware.use ConsistentRandom::RackMiddleware
|
88
100
|
```
|
89
101
|
|
90
|
-
|
102
|
+
You can also specify a seed value based on the request. This can be useful if you want to generate random values based on a specific request attribute, such as the current user.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Rack::Builder.app do
|
106
|
+
use ConsistentRandom::RackMiddleware, ->(env) { env["warden"].user.id }
|
107
|
+
run MyApp
|
108
|
+
end
|
109
|
+
```
|
91
110
|
|
92
|
-
|
111
|
+
If the seed block returns `nil`, then a random seed will be generated for the request.
|
112
|
+
|
113
|
+
### Sidekiq Middleware
|
114
|
+
|
115
|
+
Add the middlewares to your Sidekiq in an initializer:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
ConsistentRandom::SidekiqMiddleware.install
|
119
|
+
```
|
120
|
+
|
121
|
+
This will install both the client and server middleware. You can also install them manually if you need more control on the order of the middlewares. You should install the client middleware on both the server and client configurations.
|
93
122
|
|
94
123
|
```ruby
|
95
124
|
Sidekiq.configure_server do |config|
|
96
125
|
config.server_middleware do |chain|
|
97
|
-
chain.
|
126
|
+
chain.prepend ConsistentRandom::SidekiqMiddleware
|
127
|
+
end
|
128
|
+
|
129
|
+
config.client_middleware do |chain|
|
130
|
+
chain.add ConsistentRandom::SidekiqClientMiddleware
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
Sidekiq.configure_client do |config|
|
135
|
+
config.client_middleware do |chain|
|
136
|
+
chain.add ConsistentRandom::SidekiqClientMiddleware
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
Consistent random values will be propagated from the original request to any Sidekiq jobs so you will get consistent behavior on any ansynchronous jobs. You can disable this behavior on a job by setting the `conistent_random` sidekiq option to `false`:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class MyWorker
|
145
|
+
include Sidekiq::Job
|
146
|
+
|
147
|
+
sidekiq_options consistent_random: false
|
148
|
+
|
149
|
+
def perform
|
150
|
+
# Each job will use it's own random scope.
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
You can still specify a custom seed value in your worker if, for example, you want to ensure that values are consistent based on a user when the job is not enqueued from a Rack request.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class MyWorker
|
159
|
+
include Sidekiq::Job
|
160
|
+
|
161
|
+
def perform(user_id)
|
162
|
+
ConsistentRandom.scope(user_id) do
|
163
|
+
...
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
### ActiveJob
|
170
|
+
|
171
|
+
You can use consistent random values in your ActiveJob jobs by including the `ConsistentRandom::ActiveJob` module.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
class MyJob < ApplicationJob
|
175
|
+
include ConsistentRandom::ActiveJob
|
176
|
+
|
177
|
+
def perform
|
178
|
+
# Job will use consistent random values using the same scope from when it was enqueued.
|
179
|
+
end
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
Jobs will inherit the same consistent random values as the request that spawned the job. You can force a job to use it's own random scope by setting the `consistent_random` option to `false`:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class MyJob < ApplicationJob
|
187
|
+
include ConsistentRandom::ActiveJob
|
188
|
+
|
189
|
+
self.inherit_consistent_random_scope = false
|
190
|
+
|
191
|
+
def perform
|
192
|
+
# Job will use it's own random scope.
|
193
|
+
end
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
You can still specify a custom seed value in your worker if, for example, you want to ensure that values are consistent based on a user when the job is not enqueued from a Rack request.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
class MyJob < ApplicationJob
|
201
|
+
def perform(user_id)
|
202
|
+
ConsistentRandom.scope(user_id) do
|
203
|
+
...
|
204
|
+
end
|
98
205
|
end
|
99
206
|
end
|
100
207
|
```
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
2.1.0
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ConsistentRandom
|
4
|
+
module ActiveJob
|
5
|
+
def self.included(base)
|
6
|
+
attr_reader :consistent_random_seeds
|
7
|
+
|
8
|
+
base.around_perform :peform_with_consistent_random_scope
|
9
|
+
|
10
|
+
base.class_attribute :inherit_consistent_random_scope, instance_writer: false
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize
|
14
|
+
job_data = super
|
15
|
+
if inherit_consistent_random_scope != false
|
16
|
+
seed = ConsistentRandom.current_seed
|
17
|
+
job_data["consistent_random_seed"] = seed unless seed.nil?
|
18
|
+
end
|
19
|
+
job_data
|
20
|
+
end
|
21
|
+
|
22
|
+
def deserialize(job_data)
|
23
|
+
super
|
24
|
+
@consistent_random_seeds = job_data["consistent_random_seed"]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def peform_with_consistent_random_scope(&block)
|
30
|
+
seeds = consistent_random_seeds unless inherit_consistent_random_scope == false
|
31
|
+
ConsistentRandom.scope(seeds, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -4,12 +4,19 @@ class ConsistentRandom
|
|
4
4
|
# Rack middleware that wraps a request with consistent random scope
|
5
5
|
# so that you can generate consistent random values within a request.
|
6
6
|
class RackMiddleware
|
7
|
-
|
7
|
+
# @param app [Object] Rack application to wrap
|
8
|
+
# @param seed_block [Proc, #call, nil] block to generate seed for the request
|
9
|
+
# If provided, the block will be called with the request env and
|
10
|
+
# the return value will be used as the seed for the request. You can
|
11
|
+
# use this to generate a seed based on the request state..
|
12
|
+
def initialize(app, seed_block = nil)
|
8
13
|
@app = app
|
14
|
+
@seed_block = seed_block
|
9
15
|
end
|
10
16
|
|
11
17
|
def call(env)
|
12
|
-
|
18
|
+
seed = @seed_block.call(env) if @seed_block
|
19
|
+
ConsistentRandom.scope(seed) do
|
13
20
|
@app.call(env)
|
14
21
|
end
|
15
22
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ConsistentRandom
|
4
|
+
# Sidekiq client middleware that adds the current seeds to the job options. These
|
5
|
+
# seeds will be deserialized and used when the job is run on the server so that
|
6
|
+
# the client and server can share consistent random values.
|
7
|
+
class SidekiqClientMiddleware
|
8
|
+
if defined?(Sidekiq::ClientMiddleware)
|
9
|
+
include Sidekiq::ClientMiddleware
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(job_class_or_string, job, queue, redis_pool)
|
13
|
+
unless job["consistent_random"] == false
|
14
|
+
seed = ConsistentRandom.current_seed
|
15
|
+
job["consistent_random_seed"] = seed unless seed.nil?
|
16
|
+
end
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -8,8 +8,28 @@ class ConsistentRandom
|
|
8
8
|
include Sidekiq::ServerMiddleware
|
9
9
|
end
|
10
10
|
|
11
|
+
class << self
|
12
|
+
def install
|
13
|
+
Sidekiq.configure_server do |config|
|
14
|
+
config.server_middleware do |chain|
|
15
|
+
chain.prepend ConsistentRandom::SidekiqMiddleware
|
16
|
+
end
|
17
|
+
|
18
|
+
config.client_middleware do |chain|
|
19
|
+
chain.add ConsistentRandom::SidekiqClientMiddleware
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Sidekiq.configure_client do |config|
|
24
|
+
config.client_middleware do |chain|
|
25
|
+
chain.add ConsistentRandom::SidekiqClientMiddleware
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
def call(job_instance, job_payload, queue)
|
12
|
-
ConsistentRandom.scope do
|
32
|
+
ConsistentRandom.scope(job_payload["consistent_random_seed"]) do
|
13
33
|
yield
|
14
34
|
end
|
15
35
|
end
|
data/lib/consistent_random.rb
CHANGED
@@ -1,25 +1,56 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require_relative "consistent_random/sidekiq_middleware"
|
3
|
+
require "digest/sha1"
|
4
|
+
require "securerandom"
|
6
5
|
|
7
6
|
class ConsistentRandom
|
7
|
+
SEED_DIVISOR = (2**64 - 1).to_f
|
8
|
+
private_constant :SEED_DIVISOR
|
9
|
+
|
10
|
+
autoload :RackMiddleware, "consistent_random/rack_middleware"
|
11
|
+
autoload :SidekiqMiddleware, "consistent_random/sidekiq_middleware"
|
12
|
+
autoload :SidekiqClientMiddleware, "consistent_random/sidekiq_client_middleware"
|
13
|
+
autoload :ActiveJob, "consistent_random/active_job"
|
14
|
+
|
8
15
|
class << self
|
9
16
|
# Define a scope where consistent random values will be generated.
|
10
17
|
#
|
18
|
+
# @param seeds [String, Symbol, Integer, Array<String>, Array<Integer>, nil] optional value to
|
19
|
+
# use for generating random numbers. By default a random value will be generated. If the
|
20
|
+
# scope is nested in another scope block, then the seed from the parent scope will be used
|
21
|
+
# by default.
|
11
22
|
# @yield block of code to execute within the scope
|
12
23
|
# @return the result of the block
|
13
|
-
def scope
|
14
|
-
|
24
|
+
def scope(seed = nil)
|
25
|
+
existing_seed = Thread.current[:consistent_random_seed]
|
26
|
+
seed_value = case seed
|
27
|
+
when nil
|
28
|
+
existing_seed || SecureRandom.hex
|
29
|
+
when String, Symbol, Integer
|
30
|
+
seed.to_s
|
31
|
+
when Array
|
32
|
+
seed.map { |s| s.to_s }.join("\x1C")
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Invalid seed value: #{seed.inspect}"
|
35
|
+
end
|
36
|
+
|
15
37
|
begin
|
16
|
-
|
17
|
-
Thread.current[:consistent_random_context] = context
|
38
|
+
Thread.current[:consistent_random_seed] = seed_value
|
18
39
|
yield
|
19
40
|
ensure
|
20
|
-
Thread.current[:
|
41
|
+
Thread.current[:consistent_random_seed] = existing_seed
|
21
42
|
end
|
22
43
|
end
|
44
|
+
|
45
|
+
# Get the current seed used to generate random numbers. This will return nil if called
|
46
|
+
# outside of a scope.
|
47
|
+
#
|
48
|
+
# @return [String, nil] the seed value for the current scope
|
49
|
+
# or nil if called outside of a scope.
|
50
|
+
# @api private
|
51
|
+
def current_seed
|
52
|
+
Thread.current[:consistent_random_seed]
|
53
|
+
end
|
23
54
|
end
|
24
55
|
|
25
56
|
# @param name [Object] a name used to identifuy a consistent random value
|
@@ -27,7 +58,9 @@ class ConsistentRandom
|
|
27
58
|
@name = name
|
28
59
|
end
|
29
60
|
|
30
|
-
# Generate a random
|
61
|
+
# Generate a random number. The same number will be generated within a scope block.
|
62
|
+
# This method works the same as Kernel#rand. It will generate a consistent value even
|
63
|
+
# across Ruby versions and platforms.
|
31
64
|
#
|
32
65
|
# @param max [Integer, Range] the maximum value of the random float or a range indicating
|
33
66
|
# the minimum and maximum values.
|
@@ -35,35 +68,73 @@ class ConsistentRandom
|
|
35
68
|
# a number in that range. If max is an number, then it will be an integer between 0 and that
|
36
69
|
# value. Otherwise, it will be a float between 0 and 1.
|
37
70
|
def rand(max = nil)
|
38
|
-
|
71
|
+
value = seed / SEED_DIVISOR
|
72
|
+
case max
|
73
|
+
when nil
|
74
|
+
value
|
75
|
+
when Numeric
|
76
|
+
(value * max.to_i).to_i
|
77
|
+
when Range
|
78
|
+
cap_to_range(value, max)
|
79
|
+
end
|
39
80
|
end
|
40
81
|
|
41
|
-
# Generate a random
|
82
|
+
# Generate a random array of bytes. The same number will be generated within a scope block.
|
83
|
+
# This method works the same as Random#bytes.
|
42
84
|
#
|
43
85
|
# @param size [Integer] the number of bytes to generate.
|
44
86
|
# @return [String] a string of random bytes.
|
45
87
|
def bytes(size)
|
46
|
-
|
88
|
+
bytes = []
|
89
|
+
((size + 19) / 20).times { |i| bytes << seed_hash("#{@name}#{i}").to_s }
|
90
|
+
bytes.join[0, size]
|
47
91
|
end
|
48
92
|
|
49
|
-
# Generate a
|
50
|
-
#
|
93
|
+
# Generate a seed that can be used to generate random numbers. This seed will be
|
94
|
+
# return a consistent value when called within a scope.
|
51
95
|
#
|
52
|
-
# @return [
|
53
|
-
def
|
54
|
-
|
96
|
+
# @return [Integer] a 64 bit integer for seeding random values
|
97
|
+
def seed
|
98
|
+
hash = seed_hash(@name)
|
99
|
+
hash.byteslice(0, 8).unpack1("Q>")
|
55
100
|
end
|
56
101
|
|
57
102
|
# @return [Boolean] true if the other object is a ConsistentRandom that returns
|
58
103
|
# the same random number generator. If called outside of a scope, then it will
|
59
104
|
# always return false.
|
60
105
|
def ==(other)
|
61
|
-
other.is_a?(self.class) && other.
|
106
|
+
other.is_a?(self.class) && other.seed = seed
|
107
|
+
end
|
108
|
+
|
109
|
+
# Generate a random number generator for the given name. The generator will always
|
110
|
+
# have the same seed within a scope.
|
111
|
+
#
|
112
|
+
# This value is dependent on the Ruby Random class and may not generate consistent values
|
113
|
+
# across Ruby versions and platforms.
|
114
|
+
#
|
115
|
+
# @return [Random] a random number generator
|
116
|
+
def random
|
117
|
+
Random.new(seed)
|
62
118
|
end
|
63
119
|
|
64
120
|
private
|
65
121
|
|
66
|
-
def
|
67
|
-
|
122
|
+
def seed_hash(name)
|
123
|
+
random_seed = self.class.current_seed || SecureRandom.hex
|
124
|
+
Digest::SHA1.digest("#{random_seed}\x1C#{name}")
|
125
|
+
end
|
126
|
+
|
127
|
+
def cap_to_range(value, range)
|
128
|
+
min = range.begin
|
129
|
+
max = range.end
|
130
|
+
if min.nil? || max.nil?
|
131
|
+
raise ArgumentError, "Cannot generate random value for infinite range"
|
132
|
+
end
|
133
|
+
|
134
|
+
int_range = min.is_a?(Integer) && max.is_a?(Integer)
|
135
|
+
max += 1 if int_range && range.include?(max)
|
136
|
+
|
137
|
+
val = (value * (max - min)) + min
|
138
|
+
int_range ? val.to_i : val
|
68
139
|
end
|
69
140
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consistent_random
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -37,8 +37,9 @@ files:
|
|
37
37
|
- VERSION
|
38
38
|
- consistent_random.gemspec
|
39
39
|
- lib/consistent_random.rb
|
40
|
-
- lib/consistent_random/
|
40
|
+
- lib/consistent_random/active_job.rb
|
41
41
|
- lib/consistent_random/rack_middleware.rb
|
42
|
+
- lib/consistent_random/sidekiq_client_middleware.rb
|
42
43
|
- lib/consistent_random/sidekiq_middleware.rb
|
43
44
|
homepage: https://github.com/bdurand/consistent_random
|
44
45
|
licenses:
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ConsistentRandom
|
4
|
-
class Context
|
5
|
-
# @api private
|
6
|
-
attr_reader :seeds
|
7
|
-
|
8
|
-
# @param existing_context [Context, nil] Existing context to copy generators from
|
9
|
-
def initialize(existing_context = nil)
|
10
|
-
@seeds = (existing_context ? existing_context.seeds.dup : {})
|
11
|
-
end
|
12
|
-
|
13
|
-
# Return a random number generator for the given name and seed
|
14
|
-
#
|
15
|
-
# @param name [String] Name of the generator
|
16
|
-
# @return [Random] Random number generator
|
17
|
-
def seed(name)
|
18
|
-
@seeds[name] ||= Random.new_seed
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|