-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Urn represents a pseudo random Uniform Resource Name (URN) generator. Invoking the method `next` returns a unique URN with a fixed prefix and a random alphanumeric suffix.
- Loading branch information
1 parent
2670363
commit 4218f51
Showing
8 changed files
with
115 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,115 @@ | ||
# frozen_string_literal: true | ||
|
||
# Urn represents a Uniform Resource Name (URN) generator. | ||
# It generates a URN with a fixed prefix and a random alphanumeric suffix. | ||
# Urn represents a pseudo random Uniform Resource Name (URN) generator. | ||
# Invoking the method `next` returns a unique URN with a fixed prefix | ||
# and a random alphanumeric suffix. | ||
# | ||
# Urn.configure do |c| | ||
# c.max_suffix = 11 | ||
# c.seeds = { teacher: ENV['TEACHER_URN_SEED'] } | ||
# c.urns = ->(route) { Application.where(application_route: route).pluck(:urn) } | ||
# end | ||
# | ||
# Example: | ||
# | ||
# Urn.generate('teacher') # => "IRPTE12345" | ||
# Urn.generate('teacher') # => "IRPTE12345" | ||
# Urn.generate('salaried_trainee') # => "IRPST12345" | ||
# Urn.next('teacher') # => "IRPTE12345" | ||
# Urn.next('teacher') # => "IRPTE12345" | ||
# Urn.next('salaried_trainee') # => "IRPST12345" | ||
# | ||
class Urn | ||
attr_reader :value | ||
attr_writer :suffix | ||
class NoUrnAvailableError < StandardError; end | ||
|
||
def self.generate(applicant_type) | ||
code = applicant_type_code(applicant_type) | ||
PREFIX + code + Array.new(LENGTH) { CHARSET.sample }.join | ||
class Config | ||
def initialize | ||
@default_prefix = "IRP" | ||
@default_max_suffix = 99_999 | ||
@default_codes = { | ||
teacher: "TE", | ||
salaried_trainee: "ST", | ||
}.with_indifferent_access | ||
@default_urns = ->(_) { [] } | ||
end | ||
|
||
attr_writer :prefix, :codes, :max_suffix, :seeds, :urns, :padding_size | ||
|
||
def prefix | ||
@prefix || @default_prefix | ||
end | ||
|
||
def codes | ||
(@codes || @default_codes).with_indifferent_access | ||
end | ||
|
||
def max_suffix | ||
@max_suffix || @default_max_suffix | ||
end | ||
|
||
def padding_size | ||
@padding_size || max_suffix.to_s.size | ||
end | ||
|
||
def seeds | ||
(@seeds || {}).with_indifferent_access | ||
end | ||
|
||
def urns | ||
@urns || @default_urns | ||
end | ||
end | ||
|
||
CHARSET = %w[0 1 2 3 4 5 6 7 8 9].freeze | ||
PREFIX = "IRP" | ||
LENGTH = 5 | ||
private_constant :CHARSET, :PREFIX, :LENGTH | ||
class << self | ||
def configure | ||
yield(config) | ||
end | ||
|
||
def config | ||
return @config if @config.present? | ||
|
||
@config = Config.new | ||
end | ||
|
||
def next(route) | ||
routes[route].next | ||
rescue KeyError | ||
raise(ArgumentError, "Invalid route: #{route}, must be one of #{config.codes.keys}") | ||
end | ||
|
||
private | ||
|
||
def routes | ||
@routes ||= Hash.new do |hash, route| | ||
hash[route] = urn_enumerator( | ||
config.codes.fetch(route), | ||
config.seeds.fetch(route, Random.new_seed), | ||
config.urns.call(route), | ||
) | ||
end | ||
end | ||
|
||
def urns(code, seed) | ||
Array | ||
.new(config.max_suffix) { formatter(code, _1) } | ||
.drop(1) | ||
.shuffle!(random: Random.new(seed)) | ||
end | ||
|
||
def formatter(code, suffix) | ||
[config.prefix, code, sprintf("%0#{config.padding_size}d", suffix)].join | ||
end | ||
|
||
def available_urns(code, seed, used_urns) | ||
urns(code, seed) - used_urns | ||
end | ||
|
||
def urn_enumerator(code, seed, used_urns) | ||
list = available_urns(code, seed, used_urns) | ||
error_msg = "you have exhausted urn for code #{code} you need to increase the size of the suffix" | ||
|
||
Enumerator.new do |yielder| | ||
list.each { yielder << _1 } | ||
|
||
def self.applicant_type_code(applicant_type) | ||
case applicant_type | ||
when "teacher" | ||
"TE" | ||
when "salaried_trainee" | ||
"ST" | ||
else | ||
raise(ArgumentError, "Invalid applicant type: #{applicant_type}") | ||
raise(NoUrnAvailableError, error_msg) | ||
end | ||
end | ||
end | ||
private_methods :applicant_type_code | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
require Rails.root.join("app/models/urn") | ||
|
||
Urn.configure do |c| | ||
c.prefix = "IRP" | ||
c.max_suffix = 99_999 | ||
c.urns = ->(route) { Application.where(application_route: route).pluck(:urn) } | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters