class Noid::Minter

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

Attributes

counters[W]
seq[R]
template[R]

Public Class Methods

new(options = {}) click to toggle source
# File lib/noid/minter.rb, line 40
def initialize(options = {})
  @template = Template.new(options[:template])

  @counters = options[:counters]
  @max_counters = options[:max_counters]

  # callback when an identifier is minted
  @after_mint = options[:after_mint]

  # used for random minters
  @rand = options[:rand] if options[:rand].is_a? Random
  @rand ||= Marshal.load(options[:rand]) if options[:rand]
  @rand ||= Random.new(options[:seed] || Random.new_seed)

  # used for sequential minters
  @seq = options[:seq] || 0
end

Public Instance Methods

counters() click to toggle source

Counters to use for quasi-random NOID sequences

# 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
dump() click to toggle source
# 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() click to toggle source

Mint a new identifier

# 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() click to toggle source
# File lib/noid/minter.rb, line 92
def next_in_sequence
  if random?
    next_random
  else
    next_sequence
  end
end
next_random() click to toggle source
# 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() click to toggle source
# File lib/noid/minter.rb, line 109
def next_sequence
  seq.tap { @seq += 1 }
end
random?() click to toggle source
# File lib/noid/minter.rb, line 149
def random?
  template.generator == 'r'
end
random_bucket() click to toggle source
# File lib/noid/minter.rb, line 113
def random_bucket
  @rand.rand(counters.size)
end
remaining() click to toggle source

Returns the number of identifiers remaining in the minter @return [Fixnum]

# File lib/noid/minter.rb, line 87
def remaining
  return Float::INFINITY if unbounded?
  template.max - seq
end
seed(seed_number, sequence = 0) click to toggle source

Reseed the RNG

# 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?() click to toggle source
# File lib/noid/minter.rb, line 153
def unbounded?
  template.generator == 'z'
end
valid?(id) click to toggle source

Is the identifier valid under the template string and checksum? @param [String] id @return bool

# File lib/noid/minter.rb, line 80
def valid?(id)
  template.valid?(id)
end