noid 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MDE0ZDYxMWVjODZhOGEwODM0NDRiZThjZmZhMWU3YTVjZjZjYTEwNw==
5
- data.tar.gz: !binary |-
6
- ZWY4NGM2YjcwMGViNmQxMTk1MmU1OTYzNGFiMDQ1ODk1YTI1ZjE3Mw==
2
+ SHA1:
3
+ metadata.gz: 50e4fa485ed73bb6f4d313b5fda35a6ecfeb08e7
4
+ data.tar.gz: 763d9e2e9f279a0afa7461b9f2ddee88d00e1318
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MmIxY2RiNWUzNjFmZmE1MmY4YjE1YjgxZTFkZTIwMDZhNWYwZTI1ZWM5NWE5
10
- MDcyZDFhMmE5ZWQxZDVhOWIwYzRmNjdjOGViZjA4NDQ4ZjcwOTViOWU3ZDcz
11
- NDE5ZjhhNWIwMDk3Y2IzZGUzZDI1Zjk5ZDE3NzBlYTE1MDVjNGE=
12
- data.tar.gz: !binary |-
13
- MDdlODMyZDMxYWExNzM5MTY4ZGY5ZjkzNzJhMDc0MDUwODg0OTA2OGEwNDJh
14
- MzRmNDdjNTk0NWE4NzkzZTYwMDJlNTBmZGY0MjgwZmMwZWNlNjgwMWMzZTYw
15
- NjYyNWM3OGMxYzMxNTVmOGZkNTU1Y2RkMDQ5MTc2Y2I4YzczY2M=
6
+ metadata.gz: b5d19a31918d22133f8c282e3deb6baab51e27cbe7fb65797a503bffff474b34964bef5d9e81d657f591653d807e0be33b5ab2bab644ac633a9904561fecf66c
7
+ data.tar.gz: 48e622908ddedc36c1296ecdbb9818694f7f3a680f5d610fc9115254d630bcb6489519bf00903ad1f6d751254fa5816ab4c989a93014b73439b1d99c8dae73d9
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
+ sudo: false
3
+ cache: bundler
2
4
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - jruby-19mode
5
+ - 2.1
6
+ - 2.2
6
7
  script: bundle exec rake
data/Gemfile CHANGED
@@ -1,7 +1,10 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in test.gemspec
4
4
  gemspec
5
5
 
6
- gem 'simplecov'
7
- gem 'simplecov-rcov'
6
+ group :development, :test do
7
+ gem 'simplecov'
8
+ gem 'simplecov-rcov'
9
+ gem 'byebug', require: false
10
+ end
data/Rakefile CHANGED
@@ -15,10 +15,10 @@ require 'rspec'
15
15
  require 'rspec/core/rake_task'
16
16
 
17
17
  desc 'Default: run specs.'
18
- task :default => :spec
18
+ task default: :spec
19
19
 
20
20
  RSpec::Core::RakeTask.new do |t|
21
- if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.8/
21
+ if ENV['COVERAGE'] && RUBY_VERSION =~ /^1.8/
22
22
  t.rcov = true
23
23
  t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
24
24
  end
@@ -32,14 +32,13 @@ begin
32
32
  doc_destination = File.join(project_root, 'doc')
33
33
 
34
34
  YARD::Rake::YardocTask.new(:doc) do |yt|
35
- yt.files = Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
36
- [ File.join(project_root, 'README.md') ]
35
+ yt.files = Dir.glob(File.join(project_root, 'lib', '**', '*.rb')) +
36
+ [File.join(project_root, 'README.md')]
37
37
  yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
38
38
  end
39
39
  rescue LoadError
40
- desc "Generate YARD Documentation"
40
+ desc 'Generate YARD Documentation'
41
41
  task :doc do
42
- abort "Please install the YARD gem to generate rdoc."
42
+ abort 'Please install the YARD gem to generate rdoc.'
43
43
  end
44
44
  end
45
-
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.1
1
+ 0.7.2
@@ -1,8 +1,11 @@
1
- require "noid/version"
2
- require "noid/minter"
3
- require "noid/template"
1
+ require 'noid/version'
2
+ require 'noid/minter'
3
+ require 'noid/template'
4
4
 
5
5
  module Noid
6
- XDIGIT = ['0','1','2','3','4','5','6','7','8','9','b','c','d','f','g','h','j','k','m','n','p','q','r','s','t','v','w','x','z']
7
- MAX_COUNTERS = 293
6
+ XDIGIT = %w(0 1 2 3 4 5 6 7 8 9 b c d f g h j k m n p q r s t v w x z)
7
+ MAX_COUNTERS = 293
8
+
9
+ class TemplateError < StandardError
10
+ end
8
11
  end
@@ -1,74 +1,43 @@
1
1
  module Noid
2
2
  class Minter
3
- attr_reader :seed, :seq
3
+ attr_reader :seed, :seq, :template
4
4
  attr_writer :counters
5
5
 
6
- def initialize options = {}
7
-
6
+ def initialize(options = {})
8
7
  if options[:state]
9
- seed(options[:seed])
8
+ # Only set the sequence ivar if this is a stateful minter
10
9
  @seq = options[:seq]
11
- @template_string = options[:template]
12
- @counters = options[:counters]
13
10
  else
14
- seed(options[:seed], options[:seq])
15
- @template_string = options[:template]
16
11
  @max_counters = options[:max_counters]
17
- @counters = options[:counters]
18
-
19
12
  @after_mint = options[:after_mint]
20
13
  end
21
- @template ||= Noid::Template.new(@template_string)
22
- end
14
+ @counters = options[:counters]
15
+ @template = Template.new(options[:template])
16
+ seed(options[:seed], options[:seq])
17
+ end
23
18
 
24
19
  ##
25
20
  # Mint a new identifier
26
21
  def mint
27
22
  n = next_in_sequence
28
23
  id = template.mint(n)
29
- if @after_mint
30
- @after_mint.call(self, id)
31
- end
24
+ @after_mint.call(self, id) if @after_mint
32
25
  id
33
26
  end
34
27
 
35
- ##
36
- # Noid identifier template
37
- #
38
- # @return Noid::Template
39
- def template
40
- @template
41
- end
42
-
43
28
  ##
44
29
  # Is the identifier valid under the template string and checksum?
45
30
  # @param [String] id
46
31
  # @return bool
47
- def valid? id
48
- prefix = @template.prefix.empty? ? '' : id[0..@template.prefix.length-1]
49
- ch = @template.prefix.empty? ? id.split('') : id[@template.prefix.length..-1].split('')
50
- check = ch.pop if @template.checkdigit?
51
- return false unless prefix == @template.prefix
52
-
53
- return false unless @template.characters.length == ch.length
54
- @template.characters.split('').each_with_index do |c, i|
55
- return false unless Noid::XDIGIT.include? ch[i]
56
- return false if c == 'd' and ch[i] =~ /[^\d]/
57
- end
58
-
59
- return false unless check.nil? or check == @template.checkdigit(id[0..-2])
60
-
61
- true
32
+ def valid?(id)
33
+ template.valid?(id)
62
34
  end
63
35
 
64
36
  ##
65
37
  # Seed the random number generator with a seed and sequence offset
66
- # @param [Integer] seed
67
- # @param [Integer] seq
68
38
  # @return [Random]
69
- def seed seed = nil, seq = 0
70
- @rand = ::Random.new(seed) if seed
71
- @rand ||= ::Random.new
39
+ def seed(seed_number = nil, seq = 0)
40
+ @rand = seed_number ? Random.new(seed_number) : Random.new
72
41
  @seed = @rand.seed
73
42
  @seq = seq || 0
74
43
 
@@ -80,27 +49,31 @@ module Noid
80
49
  def next_in_sequence
81
50
  n = @seq
82
51
  @seq += 1
83
- case template.generator
84
- when 'r'
85
- n = next_random
52
+ if template.generator == 'r'
53
+ next_random
54
+ else
55
+ n
86
56
  end
87
- n
88
57
  end
89
58
 
90
59
  def next_random
91
- raise Exception("Exhausted noid sequence pool") if counters.size == 0
92
- i = @rand.rand(counters.size)
60
+ raise 'Exhausted noid sequence pool' if counters.size == 0
61
+ i = random_bucket
93
62
  n = counters[i][:value]
94
63
  counters[i][:value] += 1
95
64
  counters.delete_at(i) if counters[i][:value] == counters[i][:max]
96
65
  n
97
66
  end
98
67
 
68
+ def random_bucket
69
+ @rand.rand(counters.size)
70
+ end
71
+
99
72
  ##
100
73
  # Counters to use for quasi-random NOID sequences
101
74
  def counters
102
75
  return @counters if @counters
103
- return [] unless template.generator == "r"
76
+ return [] unless template.generator == 'r'
104
77
 
105
78
  percounter = template.max / (@max_counters || Noid::MAX_COUNTERS) + 1
106
79
  t = 0
@@ -120,7 +93,7 @@ module Noid
120
93
  end
121
94
 
122
95
  def dump
123
- { :state => true, :seq => @seq, :seed => @seed, :template => template.template, :counters => Marshal.load(Marshal.dump(counters)) }
96
+ { state: true, seq: @seq, seed: @seed, template: template.template, counters: Marshal.load(Marshal.dump(counters)) }
124
97
  end
125
98
  end
126
99
  end
@@ -1,13 +1,16 @@
1
1
  module Noid
2
2
  class Template
3
- attr_reader :template
3
+ attr_reader :template, :prefix, :generator, :characters
4
4
 
5
- # @param [String] template A Template is a coded string of the form Prefix.Mask that governs how identifiers will be minted.
6
- def initialize template
7
- @template = template
5
+ VALID_PATTERN = /\A(.*)\.([rsz])([ed]+)(k?)\Z/
6
+
7
+ # @param [String] template A Template is a coded string of the form Prefix.Mask that governs how identifiers will be minted.
8
+ def initialize(template)
9
+ @template = template
10
+ parse!
8
11
  end
9
12
 
10
- def mint n
13
+ def mint(n)
11
14
  str = prefix
12
15
  str += n2xdig(n)
13
16
  str += checkdigit(str) if checkdigit?
@@ -15,103 +18,116 @@ module Noid
15
18
  str
16
19
  end
17
20
 
18
- def valid? str
19
- return false unless str[0..prefix.length] == prefix
20
-
21
- if generator == 'z'
22
- str[prefix.length..-1].length > 2
23
- else
24
- str[prefix.length..-1].length == characters.length
25
- end
26
-
27
- characters.split('').each_with_index do |c, i|
28
- case c
29
- when 'e'
30
- return false unless Noid::XDIGIT.include? str[prefix.length + i]
31
- when 'd'
32
- return false unless str[prefix.length + i] =~ /\d/
33
- end
34
- end
35
-
36
- return false unless checkdigit(str[0..-2]) == str.split('').last if checkdigit?
37
-
21
+ ##
22
+ # Is the string valid against this template string and checksum?
23
+ # @param [String] str
24
+ # @return bool
25
+ def valid?(str)
26
+ match = validation_regex.match(str)
27
+ return false if match.nil?
28
+ return checkdigit(match[1]) == match[3] if checkdigit?
38
29
  true
39
30
  end
40
31
 
41
32
  ##
42
- # identifier prefix string
43
- def prefix
44
- @prefix ||= @template.split('.').first
33
+ # calculate a checkdigit for the str
34
+ # @param [String] str
35
+ # @return [String] checkdigit
36
+ def checkdigit(str)
37
+ Noid::XDIGIT[str.split('').map { |x| Noid::XDIGIT.index(x).to_i }.each_with_index.map { |n, idx| n * (idx + 1) }.inject { |sum, n| sum + n } % Noid::XDIGIT.length]
45
38
  end
46
39
 
47
40
  ##
48
- # identifier mask string
49
- def mask
50
- @mask ||= @template.split('.').last
41
+ # minimum sequence value
42
+ def min
43
+ @min ||= 0
51
44
  end
52
45
 
53
46
  ##
54
- # generator type to use: r, s, z
55
- def generator
56
- @generator ||= mask[0..0]
47
+ # maximum sequence value for the template
48
+ def max
49
+ @max ||= if generator == 'z'
50
+ nil
51
+ else
52
+ size_list.inject(1) { |total, x| total * x }
53
+ end
57
54
  end
58
55
 
56
+ protected
57
+
59
58
  ##
60
- # sequence pattern: e (extended), d (digit)
61
- def characters
62
- @characters ||= begin
63
- if checkdigit?
64
- mask[1..-2]
65
- else
66
- mask[1..-1]
67
- end
68
- end
59
+ # A noid has the structure (prefix)(code)(checkdigit)
60
+ # the regexp has the following captures
61
+ # 1 - the prefix and the code
62
+ # 2 - the changing id characters (not the prefix and not the checkdigit)
63
+ # 3 - the checkdigit, if there is one. This field is missing if there is no checkdigit
64
+ def validation_regex
65
+ @validation_regex ||= begin
66
+ character_pattern = ''
67
+ # the first character in the mask after the type character is the most significant
68
+ # acc. to the Noid spec (p.9):
69
+ # https://wiki.ucop.edu/display/Curation/NOID?preview=/16744482/16973835/noid.pdf
70
+ character_pattern += character_to_pattern(character_list.first) + "*" if generator == 'z'
71
+ character_pattern += character_list.map { |c| character_to_pattern(c) }.join
72
+
73
+ %r{\A(#{Regexp.escape(prefix)}(#{character_pattern}))(#{character_to_pattern('k') if checkdigit?})\Z}
74
+ end
69
75
  end
70
76
 
71
77
  ##
72
- # should generated identifiers have a checkdigit?
73
- def checkdigit?
74
- mask.split('').last == 'k'
78
+ # parse template and put the results into instance variables
79
+ # raise an exception if there is a parse error
80
+ def parse!
81
+ match = VALID_PATTERN.match(template)
82
+ raise Noid::TemplateError, "Malformed noid template '#{template}'" unless match
83
+ @prefix = match[1]
84
+ @generator = match[2]
85
+ @characters = match[3]
86
+ @checkdigit = (match[4] == 'k')
75
87
  end
76
88
 
77
- ##
78
- # calculate a checkdigit for the str
79
- # @param [String] str
80
- # @return [String] checkdigit
81
- def checkdigit str
82
- Noid::XDIGIT[str.split('').map { |x| Noid::XDIGIT.index(x).to_i }.each_with_index.map { |n, idx| n*(idx+1) }.inject { |sum, n| sum += n } % Noid::XDIGIT.length ]
89
+ def xdigit_pattern
90
+ @xdigit_pattern ||= '[' + Noid::XDIGIT.join('') + ']'
83
91
  end
84
92
 
85
- ##
86
- # minimum sequence value
87
- def min
88
- @min ||= 0
93
+ def character_to_pattern(c)
94
+ case c
95
+ when 'e', 'k'
96
+ xdigit_pattern
97
+ when 'd'
98
+ '\d'
99
+ else
100
+ ''
101
+ end
89
102
  end
90
103
 
91
104
  ##
92
- # maximum sequence value for the template
93
- def max
94
- @max ||= begin
95
- case generator
96
- when 'z'
97
- nil
98
- else
99
- characters.split('').map { |x| character_space(x) }.compact.inject(1) { |total, x| total *= x }
100
- end
101
- end
105
+ # Return a list giving the number of possible characters at each position
106
+ def size_list
107
+ @size_list ||= character_list.map { |c| character_space(c) }
102
108
  end
103
109
 
110
+ def character_list
111
+ characters.split('')
112
+ end
113
+
114
+ def mask
115
+ generator + characters
116
+ end
117
+
118
+ def checkdigit?
119
+ @checkdigit
120
+ end
104
121
 
105
- protected
106
122
  ##
107
123
  # total size of a given template character value
108
124
  # @param [String] c
109
- def character_space c
125
+ def character_space(c)
110
126
  case c
111
- when 'e'
112
- Noid::XDIGIT.length
113
- when 'd'
114
- 10
127
+ when 'e'
128
+ Noid::XDIGIT.length
129
+ when 'd'
130
+ 10
115
131
  end
116
132
  end
117
133
 
@@ -119,26 +135,25 @@ module Noid
119
135
  # convert a minter position to a noid string under this template
120
136
  # @param [Integer] n
121
137
  # @return [String]
122
- def n2xdig n
123
- xdig = characters.reverse.split('').map do |c|
124
- value = n % character_space(c)
125
- n = n / character_space(c)
138
+ def n2xdig(n)
139
+ xdig = size_list.reverse.map { |size|
140
+ value = n % size
141
+ n /= size
126
142
  Noid::XDIGIT[value]
127
- end.compact.join('')
143
+ }.compact.join('')
128
144
 
129
145
  if generator == 'z'
130
- c = characters.split('').last
146
+ size = size_list.last
131
147
  while n > 0
132
- value = n % character_space(c)
133
- n = n / character_space(c)
148
+ value = n % size
149
+ n /= size
134
150
  xdig += Noid::XDIGIT[value]
135
151
  end
136
152
  end
137
153
 
138
- raise Exception if n > 0
154
+ raise 'Exhausted noid sequence pool' if n > 0
139
155
 
140
156
  xdig.reverse
141
157
  end
142
-
143
158
  end
144
159
  end
@@ -4,6 +4,6 @@ module Noid
4
4
  @version ||= File.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION')).chomp
5
5
  end
6
6
 
7
- VERSION = self.version
7
+ VERSION = version
8
8
  end
9
9
  end
@@ -1,27 +1,27 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "noid/version"
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'noid/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "noid"
6
+ s.name = 'noid'
7
7
  s.version = Noid::VERSION
8
- s.authors = ["Chris Beer"]
9
- s.email = ["chris@cbeer.info"]
10
- s.homepage = "http://github.com/microservices/noid"
11
- s.summary = %q{Nice Opaque Identifier}
12
- s.description = %q{Nice Opaque Identifier}
13
- s.licenses = ["MIT"]
8
+ s.authors = ['Chris Beer']
9
+ s.email = ['chris@cbeer.info']
10
+ s.homepage = 'http://github.com/microservices/noid'
11
+ s.summary = 'Nice Opaque Identifier'
12
+ s.description = 'Nice Opaque Identifier'
13
+ s.licenses = ['MIT']
14
14
 
15
- s.rubyforge_project = "noid"
15
+ s.rubyforge_project = 'noid'
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib"]
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
21
 
22
22
  s.required_ruby_version = '>= 1.9.3'
23
23
 
24
- s.add_development_dependency "bundler"
25
- s.add_development_dependency "rake"
26
- s.add_development_dependency "rspec", ">= 2.0"
24
+ s.add_development_dependency 'bundler'
25
+ s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'rspec', '>= 3.0'
27
27
  end
@@ -1,152 +1,158 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Noid::Minter do
4
- it "should mint a few random 3-digit numbers" do
5
- minter = Noid::Minter.new(:template => '.rddd')
6
- minter.mint.should =~ /\d\d\d/
4
+ it 'mints a few random 3-digit numbers' do
5
+ minter = described_class.new(template: '.rddd')
6
+ expect(minter.mint).to match(/\d\d\d/)
7
7
  end
8
8
 
9
- it "should mint random 3-digit numbers, stopping after the 1000th" do
10
- minter = Noid::Minter.new(:template => '.rddd')
11
- 1000.times { minter.mint.should =~ /^\d\d\d$/ }
12
- expect { minter.mint }.to raise_exception
9
+ it 'mints random 3-digit numbers, stopping after the 1000th' do
10
+ minter = described_class.new(template: '.rddd')
11
+ 1000.times { expect(minter.mint).to match(/^\d\d\d$/) }
12
+ expect { minter.mint }.to raise_exception(RuntimeError, /Exhausted noid sequence pool/)
13
13
  end
14
14
 
15
- it "should mint sequential numbers without limit, adding new digits as needed" do
16
- minter = Noid::Minter.new(:template => '.zd')
17
- minter.mint.should == "0"
18
- 999.times { minter.mint.should =~ /\d/ }
19
- minter.mint.should == "1000"
15
+ it 'mints sequential numbers without limit, adding new digits as needed' do
16
+ minter = described_class.new(template: '.zd')
17
+ expect(minter.mint).to eq('0')
18
+ 999.times { expect(minter.mint).to match(/\d/) }
19
+ expect(minter.mint).to eq('1000')
20
20
  end
21
21
 
22
- it "should mint random 4-digit numbers with constant prefix bc" do
23
- minter = Noid::Minter.new(:template => 'bc.rdddd')
24
- 1000.times { minter.mint.should =~ /^bc\d\d\d\d$/ }
22
+ it 'mints random 4-digit numbers with constant prefix bc' do
23
+ minter = described_class.new(template: 'bc.rdddd')
24
+ 1000.times { expect(minter.mint).to match(/^bc\d\d\d\d$/) }
25
25
  end
26
26
 
27
- it "should mint sequential 2-digit numbers with constant prefix 8rf" do
28
- minter = Noid::Minter.new(:template => '8rf.sdd')
29
- minter.mint.should == "8rf00"
30
- 10.times { minter.mint.should =~ /^8rf\d\d$/ }
31
- minter.mint.should == "8rf11"
32
- 88.times { minter.mint.should =~ /^8rf\d\d$/ }
33
- expect { minter.mint }.to raise_exception
27
+ it 'mints sequential 2-digit numbers with constant prefix 8rf' do
28
+ minter = described_class.new(template: '8rf.sdd')
29
+ expect(minter.mint).to eq('8rf00')
30
+ 10.times { expect(minter.mint).to match(/^8rf\d\d$/) }
31
+ expect(minter.mint).to eq('8rf11')
32
+ 88.times { expect(minter.mint).to match(/^8rf\d\d$/) }
33
+ expect { minter.mint }.to raise_exception(RuntimeError, /Exhausted noid sequence pool/)
34
34
  end
35
35
 
36
- it "should mint sequential extended-digits" do
37
- minter = Noid::Minter.new(:template => '.se')
38
- 29.times.map { minter.mint }.join('').should == "0123456789bcdfghjkmnpqrstvwxz"
36
+ it 'mints sequential extended-digits' do
37
+ minter = described_class.new(template: '.se')
38
+ expect(29.times.map { minter.mint }.join('')).to eq('0123456789bcdfghjkmnpqrstvwxz')
39
39
  end
40
-
41
- it "should mint random 3-extended-digit numbers with constant prefix h9" do
42
- minter = Noid::Minter.new(:template => 'h9.reee')
43
40
 
44
- (minter.template.max).times { minter.mint.should =~ /^h9\w\w\w$/ }
45
- expect { minter.mint }.to raise_exception
41
+ it 'mints random 3-extended-digit numbers with constant prefix h9' do
42
+ minter = described_class.new(template: 'h9.reee')
43
+
44
+ (minter.template.max).times { expect(minter.mint).to match(/^h9\w\w\w$/) }
45
+ expect { minter.mint }.to raise_exception(RuntimeError, /Exhausted noid sequence pool/)
46
46
  end
47
47
 
48
- it "should mint unlimited sequential numbers with at least 3 extended digits" do
49
- minter = Noid::Minter.new(:template => '.zeee')
50
- (29*29*29).times { minter.mint.should =~ /^\w\w\w/ }
51
- minter.mint.should =~ /^\w\w\w\w/
48
+ it 'mints unlimited sequential numbers with at least 3 extended digits' do
49
+ minter = described_class.new(template: '.zeee')
50
+ (29 * 29 * 29).times { expect(minter.mint).to match(/^\w\w\w/) }
51
+ expect(minter.mint).to match(/^\w\w\w\w/)
52
52
  end
53
53
 
54
- it "should mint random 7-char numbers, with extended digits at chars 2,4,and 5" do
55
- minter = Noid::Minter.new(:template => '.rdedeedd')
56
- 1000.times { minter.mint.should =~ /^\d\w\d\w\w\d\d$/ }
54
+ it 'mints random 7-char numbers, with extended digits at chars 2,4,and 5' do
55
+ minter = described_class.new(template: '.rdedeedd')
56
+ 1000.times { expect(minter.mint).to match(/^\d\w\d\w\w\d\d$/) }
57
57
  end
58
58
 
59
- it "should mint unlimited mixed digits, adding new extended digits as needed" do
60
- minter = Noid::Minter.new(:template => '.zedededed')
61
- minter.mint.should == "00000000"
59
+ it 'mints unlimited mixed digits, adding new extended digits as needed' do
60
+ minter = described_class.new(template: '.zedededed')
61
+ expect(minter.mint).to eq('00000000')
62
62
  end
63
63
 
64
- it "should mint sequential 4-mixed-digit with constant prefix sdd" do
65
- minter = Noid::Minter.new(:template => 'sdd.sdede')
66
- minter.mint.should == 'sdd0000'
67
- 1000.times { minter.mint.should =~ /^sdd\d\w\d\w$/}
68
- minter.mint.should == "sdd034h"
64
+ it 'mints sequential 4-mixed-digit with constant prefix sdd' do
65
+ minter = described_class.new(template: 'sdd.sdede')
66
+ expect(minter.mint).to eq('sdd0000')
67
+ 1000.times { expect(minter.mint).to match(/^sdd\d\w\d\w$/) }
68
+ expect(minter.mint).to eq('sdd034h')
69
69
  end
70
70
 
71
- it "should mint random 3 mixed digits plus final (4th) computed check character" do
72
- minter = Noid::Minter.new(:template => '.rdedk')
73
- 1000.times { minter.mint.should =~ /^\d\w\d\w$/ }
71
+ it 'mints random 3 mixed digits plus final (4th) computed check character' do
72
+ minter = described_class.new(template: '.rdedk')
73
+ 1000.times { expect(minter.mint).to match(/^\d\w\d\w$/) }
74
74
  end
75
75
 
76
- it "should mint 5 sequential mixed digits plus final extended digit check char" do
77
- minter = Noid::Minter.new(:template => '.sdeeedk')
78
- minter.mint.should == "000000"
79
- minter.mint.should == "000015"
80
- minter.mint.should == "00002b"
81
- 1000.times { minter.mint.should =~ /^\d\w\w\w\d\w$/ }
82
- minter.mint.should == "003f3m"
76
+ it 'mints 5 sequential mixed digits plus final extended digit check char' do
77
+ minter = described_class.new(template: '.sdeeedk')
78
+ expect(minter.mint).to eq('000000')
79
+ expect(minter.mint).to eq('000015')
80
+ expect(minter.mint).to eq('00002b')
81
+ 1000.times { expect(minter.mint).to match(/^\d\w\w\w\d\w$/) }
82
+ expect(minter.mint).to eq('003f3m')
83
83
  end
84
84
 
85
- it "should mint sequential digits plus check char, with new digits added as needed" do
86
- minter = Noid::Minter.new(:template => ".zdeek")
87
- minter.mint.should == "0000"
88
- minter.mint.should == "0013"
89
- (10*29*29-2).times { minter.mint.should =~ /^\d\w\w\w$/ }
90
- minter.mint.should == "10001"
85
+ it 'mints sequential digits plus check char, with new digits added as needed' do
86
+ minter = described_class.new(template: '.zdeek')
87
+ expect(minter.mint).to eq('0000')
88
+ expect(minter.mint).to eq('0013')
89
+ (10 * 29 * 29 - 2).times { expect(minter.mint).to match(/^\d\w\w\w$/) }
90
+ expect(minter.mint).to eq('10001')
91
91
  end
92
92
 
93
- it "should mint prefix plus random 3 mixed digits plus a check char" do
94
- minter = Noid::Minter.new(:template => "63q.redek")
95
- minter.mint.should =~ /63q\w\d\w\w/
93
+ it 'mints prefix plus random 3 mixed digits plus a check char' do
94
+ minter = described_class.new(template: '63q.redek')
95
+ expect(minter.mint).to match(/63q\w\d\w\w/)
96
96
  end
97
97
 
98
- describe "validate" do
99
- it "should validate a prefixed identifier" do
100
- minter = Noid::Minter.new(:template => "foobar.redek")
98
+ describe 'validate' do
99
+ it 'validates a prefixed identifier' do
100
+ minter = described_class.new(template: 'foobar.redek')
101
+ id = minter.mint
102
+ expect(minter.valid?(id)).to eq(true)
103
+ end
104
+ it 'validates a prefixless identifier' do
105
+ minter = described_class.new(template: '.redek')
101
106
  id = minter.mint
102
- minter.valid?(id).should be_true
107
+ expect(minter.valid?(id)).to eq(true)
103
108
  end
104
- it "should validate a prefixless identifier" do
105
- minter = Noid::Minter.new(:template => ".redek")
109
+ it 'validates with a new minter' do
110
+ minter = described_class.new(template: '.redek')
106
111
  id = minter.mint
107
- minter.valid?(id).should be_true
112
+ minter2 = described_class.new(template: '.redek')
113
+ expect(minter2.valid?(id)).to eq(true)
108
114
  end
109
- it "should validate with a new minter" do
110
- minter = Noid::Minter.new(:template => ".redek")
115
+ it 'validates an unlimited sequence with mixed digits' do
116
+ minter = described_class.new(template: '.zed')
117
+ 1000.times { minter.mint }
111
118
  id = minter.mint
112
- minter2 = Noid::Minter.new(:template => ".redek")
113
- minter2.valid?(id).should be_true
119
+ expect(minter.valid?(id)).to eq(true)
114
120
  end
115
121
  end
116
122
 
117
- describe "seed" do
118
- it "given a specific seed, identifiers should be replicable" do
119
- minter = Noid::Minter.new(:template => "63q.redek")
123
+ describe 'seed' do
124
+ it 'given a specific seed, identifiers should be replicable' do
125
+ minter = described_class.new(template: '63q.redek')
120
126
  minter.seed(1)
121
- minter.mint.should == "63q3706"
122
-
123
- minter = Noid::Minter.new(:template => "63q.redek")
127
+ expect(minter.mint).to eq('63q3706')
128
+
129
+ minter = described_class.new(template: '63q.redek')
124
130
  minter.seed(1)
125
- minter.mint.should == "63q3706"
131
+ expect(minter.mint).to eq('63q3706')
126
132
  end
127
133
 
128
- it "given a specific seed and sequence, identifiers should be replicable" do
129
- minter = Noid::Minter.new(:template => "63q.redek")
130
- minter.seed(23456789, 567)
134
+ it 'given a specific seed and sequence, identifiers should be replicable' do
135
+ minter = described_class.new(template: '63q.redek')
136
+ minter.seed(23_456_789, 567)
131
137
  mint1 = minter.mint
132
138
  dump1 = minter.dump
133
139
 
134
- minter = Noid::Minter.new(:template => "63q.redek")
135
- minter.seed(23456789, 567)
140
+ minter = described_class.new(template: '63q.redek')
141
+ minter.seed(23_456_789, 567)
136
142
  mint2 = minter.mint
137
143
  dump2 = minter.dump
138
144
  expect(dump1).to eql(dump2)
139
145
  expect(mint1).to eql(mint2)
140
- mint1.should == "63qb41v" # "63qh305" was the value from a slightly buggy impl
146
+ expect(mint1).to eq('63qb41v') # "63qh305" was the value from a slightly buggy impl
141
147
  end
142
148
  end
143
149
 
144
- describe "dump and reload" do
145
- it "should dump the minter state" do
146
- minter = Noid::Minter.new(:template => ".sddd")
150
+ describe 'dump and reload' do
151
+ it 'dumps the minter state' do
152
+ minter = described_class.new(template: '.sddd')
147
153
  d = minter.dump
148
- d[:template].should == ".sddd"
149
- d[:seq].should == 0
154
+ expect(d[:template]).to eq('.sddd')
155
+ expect(d[:seq]).to eq(0)
150
156
 
151
157
  minter.mint
152
158
  minter.mint
@@ -154,51 +160,63 @@ describe Noid::Minter do
154
160
  d[:seq] == 2
155
161
  end
156
162
 
157
- it "should dump the seed, sequence, and counters for the RNG" do
158
- minter = Noid::Minter.new(:template => ".rddd")
163
+ it 'dumps the seed, sequence, and counters for the RNG' do
164
+ minter = described_class.new(template: '.rddd')
159
165
  d = minter.dump
160
- d[:seq] == 0
161
- d[:seed].should == minter.instance_variable_get('@seed')
166
+ expect(d[:seq]).to eq 0
167
+ expect(d[:seed]).to eq(minter.instance_variable_get('@seed'))
162
168
  end
163
169
 
164
- it "should allow a random identifier minter to be 'replayed' accurately" do
165
- minter = Noid::Minter.new(:template => '.rd')
170
+ it "allows a random identifier minter to be 'replayed' accurately" do
171
+ minter = described_class.new(template: '.rd')
166
172
  d = minter.dump
167
173
  arr = 10.times.map { minter.mint }
168
174
 
169
- minter = Noid::Minter.new(d)
175
+ minter = described_class.new(d)
170
176
 
171
177
  arr2 = 10.times.map { minter.mint }
172
178
 
173
- arr.should == arr2
174
-
179
+ expect(arr).to eq(arr2)
175
180
  end
176
-
177
181
  end
178
182
 
179
- describe "with large seeds" do
180
- it "should not reproduce noids with constructed sequences" do
181
- minter = Noid::Minter.new(:template => 'ldpd:.reeeeeeee')
182
- minter.seed(192548637498850379850405658298152906991)
183
- first_values = (1..1000).collect {|c| minter.mint}
183
+ describe 'with large seeds' do
184
+ it 'does not reproduce noids with constructed sequences' do
185
+ minter = described_class.new(template: 'ldpd:.reeeeeeee')
186
+ minter.seed(192_548_637_498_850_379_850_405_658_298_152_906_991)
187
+ first_values = (1..1000).collect { |_c| minter.mint }
184
188
 
185
189
  values = []
186
190
  (0..999).each do |i|
187
- minter = Noid::Minter.new(:template => 'ldpd:.reeeeeeee')
188
- minter.seed(192548637498850379850405658298152906991, i)
191
+ minter = described_class.new(template: 'ldpd:.reeeeeeee')
192
+ minter.seed(192_548_637_498_850_379_850_405_658_298_152_906_991, i)
189
193
  values << minter.mint
190
- expect(values[i]).to eql first_values[i]
194
+ expect(values[i]).to eql first_values[i]
191
195
  end
192
196
  values.uniq!
193
197
  expect(values.length).to eql 1000
194
198
  end
195
199
  end
196
200
 
197
- describe "multithreading-safe example" do
201
+ describe 'multithreading-safe example' do
202
+ def stateful_minter
203
+ File.open('minter-state', File::RDWR | File::CREAT, 0644) do |f|
204
+ f.flock(File::LOCK_EX)
205
+ yaml = YAML.load(f.read)
206
+ minter = described_class.new(yaml)
207
+ yield minter
208
+ f.rewind
209
+ yaml = YAML.dump(minter.dump)
210
+ f.write yaml
211
+ f.flush
212
+ f.truncate(f.pos)
213
+ end
214
+ end
215
+
198
216
  before do
199
217
  require 'yaml'
200
- minter = Noid::Minter.new(:template => '.rd')
201
- yaml = YAML::dump(minter.dump)
218
+ minter = described_class.new(template: '.reek')
219
+ yaml = YAML.dump(minter.dump)
202
220
  File.open('minter-state', 'w') { |f| f.write yaml }
203
221
  end
204
222
 
@@ -206,23 +224,34 @@ describe Noid::Minter do
206
224
  File.delete('minter-state')
207
225
  end
208
226
 
209
- it "should persist state to the filesystem" do
227
+ it 'hops buckets between runs' do
228
+ bucket_list = []
229
+ 10.times do
230
+ stateful_minter do |minter|
231
+ bucket = minter.random_bucket
232
+ bucket_list << bucket
233
+ allow(minter).to receive(:random_bucket) { bucket }
234
+ minter.mint
235
+ end
236
+ end
237
+ expect(bucket_list.uniq.count).to be > 1
238
+ end
210
239
 
211
- File.open("minter-state", File::RDWR|File::CREAT, 0644) {|f|
240
+ it 'persists state to the filesystem' do
241
+ # TODO: This is not testing any expectations. Clarify intent and fix.
242
+ skip
243
+ File.open('minter-state', File::RDWR | File::CREAT, 0644) do|f|
212
244
  f.flock(File::LOCK_EX)
213
- yaml = YAML::load(f.read)
214
-
215
- minter = Noid::Minter.new(yaml)
245
+ yaml = YAML.load(f.read)
246
+
247
+ minter = described_class.new(yaml)
216
248
 
217
249
  f.rewind
218
- yaml = YAML::dump(minter.dump)
250
+ yaml = YAML.dump(minter.dump)
219
251
  f.write yaml
220
252
  f.flush
221
253
  f.truncate(f.pos)
222
- }
223
-
254
+ end
224
255
  end
225
256
  end
226
-
227
-
228
257
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Noid::Template do
4
+ context 'with a valid template' do
5
+ let(:template) { '.redek' }
6
+ it 'initializes without raising' do
7
+ expect { described_class.new(template) }.not_to raise_error
8
+ end
9
+ end
10
+ context 'with a bogus template' do
11
+ let(:template) { 'foobar' }
12
+ it 'raises Noid::TemplateError' do
13
+ expect { described_class.new(template) }.to raise_error(Noid::TemplateError)
14
+ end
15
+ end
16
+ end
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
3
 
4
- if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.9/
4
+ if ENV['COVERAGE'] && RUBY_VERSION =~ /^1.9/
5
5
  require 'simplecov'
6
6
 
7
7
  SimpleCov.start
@@ -9,6 +9,5 @@ end
9
9
 
10
10
  require 'noid'
11
11
 
12
- RSpec.configure do |config|
13
-
12
+ RSpec.configure do |_config|
14
13
  end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Beer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-16 00:00:00.000000000 Z
11
+ date: 2015-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '2.0'
47
+ version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '2.0'
54
+ version: '3.0'
55
55
  description: Nice Opaque Identifier
56
56
  email:
57
57
  - chris@cbeer.info
@@ -59,8 +59,9 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - .gitignore
63
- - .travis.yml
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
64
65
  - Gemfile
65
66
  - LICENSE.txt
66
67
  - README.md
@@ -72,6 +73,7 @@ files:
72
73
  - lib/noid/version.rb
73
74
  - noid.gemspec
74
75
  - spec/lib/minter_spec.rb
76
+ - spec/lib/template_spec.rb
75
77
  - spec/spec_helper.rb
76
78
  homepage: http://github.com/microservices/noid
77
79
  licenses:
@@ -83,20 +85,18 @@ require_paths:
83
85
  - lib
84
86
  required_ruby_version: !ruby/object:Gem::Requirement
85
87
  requirements:
86
- - - ! '>='
88
+ - - ">="
87
89
  - !ruby/object:Gem::Version
88
90
  version: 1.9.3
89
91
  required_rubygems_version: !ruby/object:Gem::Requirement
90
92
  requirements:
91
- - - ! '>='
93
+ - - ">="
92
94
  - !ruby/object:Gem::Version
93
95
  version: '0'
94
96
  requirements: []
95
97
  rubyforge_project: noid
96
- rubygems_version: 2.2.2
98
+ rubygems_version: 2.4.6
97
99
  signing_key:
98
100
  specification_version: 4
99
101
  summary: Nice Opaque Identifier
100
- test_files:
101
- - spec/lib/minter_spec.rb
102
- - spec/spec_helper.rb
102
+ test_files: []