noid 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []