Class: Noid::Minter
- Inherits:
-
Object
- Object
- Noid::Minter
- Defined in:
- lib/noid/minter.rb
Overview
Minters come in two varieties: stateful and stateless. A stateless minter – typically used with random rather than sequential templates, since minting in a sequence requires state to know the current position in the sequence – mints random identifiers and will mint duplicates eventually, depending upon the size of the identifier space in the provided template.
A stateful minter is a minter that has been initialized with parameters reflecting its current state. (A common way to store state between mintings is to call the minter ‘#dump` method which serializes the necessary parts of minter state to a hash, which may be persisted on disk or in other back-ends.) The parameters that are included are:
* template, a string setting the identifier pattern
* counters, a hash of "buckets" each with a current and max value
* seq, an integer reflecting how far into the sequence the minter is
* rand, a random number generator
Minters using random templates use a number of containers, each with a similar number of identifiers to split the identifier space into manageable chunks (or “buckets”) and to increase the appearance of randomness in the identifiers.
As an example, let’s assume a random identifier template that has 100 possible values. It might have 10 buckets, each with 10 identifiers that look similar because they have similar numeric values. Every call to ‘#mint` will use the random number generator stored in the minter’s state to select a bucket at random. Stateless minters will select a bucket at random as well.
The difference between stateless and stateful minters in this context is that stateful random minters are replayable as long as you have persisted the minter’s state, which includes a random number generator part of which is its original seed, which may be used over again in the future to replay the sequence of identifiers in this minter
Instance Attribute Summary collapse
-
#counters ⇒ Object
Counters to use for quasi-random NOID sequences.
-
#seq ⇒ Object
readonly
Returns the value of attribute seq.
-
#template ⇒ Object
readonly
Returns the value of attribute template.
Instance Method Summary collapse
- #dump ⇒ Object
-
#initialize(options = {}) ⇒ Minter
constructor
A new instance of Minter.
-
#mint ⇒ Object
Mint a new identifier.
- #next_in_sequence ⇒ Object
- #next_random ⇒ Object
- #next_sequence ⇒ Object
- #random? ⇒ Boolean
- #random_bucket ⇒ Object
-
#remaining ⇒ Fixnum
Returns the number of identifiers remaining in the minter.
-
#seed(seed_number, sequence = 0) ⇒ Object
Reseed the RNG.
- #unbounded? ⇒ Boolean
-
#valid?(id) ⇒ Boolean
Is the identifier valid under the template string and checksum?.
Constructor Details
#initialize(options = {}) ⇒ Minter
Returns a new instance of Minter.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/noid/minter.rb', line 40 def initialize( = {}) @template = Template.new([:template]) @counters = [:counters] @max_counters = [:max_counters] # callback when an identifier is minted @after_mint = [:after_mint] # used for random minters @rand = [:rand] if [:rand].is_a? Random @rand ||= Marshal.load([:rand]) if [:rand] @rand ||= Random.new([:seed] || Random.new_seed) # used for sequential minters @seq = [:seq] || 0 end |
Instance Attribute Details
#counters ⇒ Object
Counters to use for quasi-random NOID sequences
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/noid/minter.rb', line 119 def counters return @counters if @counters return [] unless random? percounter = template.max / (@max_counters || Noid::MAX_COUNTERS) + 1 t = 0 @counters = [] while t < template.max counter = {} counter[:value] = t counter[:max] = [t + percounter, template.max].min t += percounter @counters << counter end @counters end |
#seq ⇒ Object (readonly)
Returns the value of attribute seq.
37 38 39 |
# File 'lib/noid/minter.rb', line 37 def seq @seq end |
#template ⇒ Object (readonly)
Returns the value of attribute template.
37 38 39 |
# File 'lib/noid/minter.rb', line 37 def template @template end |
Instance Method Details
#dump ⇒ Object
140 141 142 143 144 145 146 147 |
# File 'lib/noid/minter.rb', line 140 def dump { template: template.template, counters: Marshal.load(Marshal.dump(counters)), seq: seq, rand: Marshal.dump(@rand) # we would Marshal.load this too, but serializers don't persist the internal state correctly } end |
#mint ⇒ Object
Mint a new identifier
60 61 62 63 64 65 66 |
# File 'lib/noid/minter.rb', line 60 def mint n = next_in_sequence id = template.mint(n) next_sequence if random? @after_mint.call(self, id) if @after_mint id end |
#next_in_sequence ⇒ Object
92 93 94 95 96 97 98 |
# File 'lib/noid/minter.rb', line 92 def next_in_sequence if random? next_random else next_sequence end end |
#next_random ⇒ Object
100 101 102 103 104 105 106 107 |
# File 'lib/noid/minter.rb', line 100 def next_random raise 'Exhausted noid sequence pool' if counters.size == 0 i = random_bucket n = counters[i][:value] counters[i][:value] += 1 counters.delete_at(i) if counters[i][:value] == counters[i][:max] n end |
#next_sequence ⇒ Object
109 110 111 |
# File 'lib/noid/minter.rb', line 109 def next_sequence seq.tap { @seq += 1 } end |
#random? ⇒ Boolean
149 150 151 |
# File 'lib/noid/minter.rb', line 149 def random? template.generator == 'r' end |
#random_bucket ⇒ Object
113 114 115 |
# File 'lib/noid/minter.rb', line 113 def random_bucket @rand.rand(counters.size) end |
#remaining ⇒ Fixnum
Returns the number of identifiers remaining in the minter
87 88 89 90 |
# File 'lib/noid/minter.rb', line 87 def remaining return Float::INFINITY if unbounded? template.max - seq end |
#seed(seed_number, sequence = 0) ⇒ Object
Reseed the RNG
70 71 72 73 74 |
# File 'lib/noid/minter.rb', line 70 def seed(seed_number, sequence = 0) @rand = Random.new(seed_number) sequence.times { next_random } @rand end |
#unbounded? ⇒ Boolean
153 154 155 |
# File 'lib/noid/minter.rb', line 153 def unbounded? template.generator == 'z' end |
#valid?(id) ⇒ Boolean
Is the identifier valid under the template string and checksum?
80 81 82 |
# File 'lib/noid/minter.rb', line 80 def valid?(id) template.valid?(id) end |