noid 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +2 -14
- data/Rakefile +5 -50
- data/VERSION +1 -1
- data/lib/noid.rb +6 -11
- data/lib/noid/minter.rb +113 -1
- data/lib/noid/template.rb +144 -0
- data/lib/noid/version.rb +9 -0
- data/noid.gemspec +18 -80
- data/spec/lib/minter_spec.rb +153 -0
- data/spec/spec_helper.rb +7 -0
- metadata +65 -153
- data/.document +0 -5
- data/doc/active_record_sample.rb +0 -6
- data/lib/noid/active_record.rb +0 -5
- data/lib/noid/active_record/provider.rb +0 -36
- data/lib/noid/base.rb +0 -154
- data/lib/noid/identifier.rb +0 -24
- data/lib/noid/identifier/anvl.rb +0 -32
- data/lib/noid/identifier/singleton.rb +0 -14
- data/lib/noid/persistence.rb +0 -4
- data/lib/noid/persistence/base.rb +0 -11
- data/lib/noid/persistence/json.rb +0 -38
- data/test/helper.rb +0 -18
- data/test/test_binding.rb +0 -42
- data/test/test_noid.rb +0 -134
- data/test/test_persistence.rb +0 -47
data/lib/noid/base.rb
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
require 'backports'
|
2
|
-
|
3
|
-
module Noid
|
4
|
-
module Base
|
5
|
-
XDIGIT = ['0','1','2','3','4','5','6','7','8','9','b','c','d','f','g','h','j','k','l','n','p','q','r','s','t','v','w','x','z']
|
6
|
-
MAX_COUNTERS = 293
|
7
|
-
|
8
|
-
def initialize args = {}
|
9
|
-
@max = nil
|
10
|
-
@min = nil
|
11
|
-
@config = { :identifier => {} }.merge args
|
12
|
-
super() if respond_to?('super')
|
13
|
-
setup_mask args
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
def mint
|
18
|
-
str = @prefix
|
19
|
-
n = @s
|
20
|
-
@s += 1
|
21
|
-
|
22
|
-
case @type
|
23
|
-
when 's'
|
24
|
-
when 'z'
|
25
|
-
when 'r'
|
26
|
-
raise Exception if @counters.size == 0
|
27
|
-
i = @rand.rand(@counters.size)
|
28
|
-
n = @counters[i][:value]
|
29
|
-
@counters[i][:value] += 1
|
30
|
-
@counters.delete_at(i) if @counters[i][:value] == @counters[i][:max]
|
31
|
-
end
|
32
|
-
str += n2xdig(n)
|
33
|
-
|
34
|
-
str += checkdigit(str) if @check
|
35
|
-
|
36
|
-
identifier.new str
|
37
|
-
end
|
38
|
-
|
39
|
-
def valid? id
|
40
|
-
prefix = id[0..@prefix.length-1]
|
41
|
-
ch = id[@prefix.length..-1].split('')
|
42
|
-
check = ch.pop if @check
|
43
|
-
return false unless prefix == @prefix
|
44
|
-
|
45
|
-
return false unless @characters.length == ch.length
|
46
|
-
@characters.each_with_index do |c, i|
|
47
|
-
return false unless XDIGIT.include? ch[i]
|
48
|
-
return false if c == 'd' and ch[i] =~ /[^\d]/
|
49
|
-
end
|
50
|
-
|
51
|
-
return false unless check.nil? or check == checkdigit(id[0..-2])
|
52
|
-
|
53
|
-
true
|
54
|
-
end
|
55
|
-
|
56
|
-
protected
|
57
|
-
def identifier
|
58
|
-
@config[:identifier][:class] || String
|
59
|
-
end
|
60
|
-
|
61
|
-
def checkdigit str
|
62
|
-
i = 1
|
63
|
-
XDIGIT[str.split('').map { |x| XDIGIT.index(x).to_i }.inject { |sum, n| i+=1; sum += (n * i) } % XDIGIT.length]
|
64
|
-
end
|
65
|
-
|
66
|
-
def min
|
67
|
-
return @min if @min
|
68
|
-
@min = 0
|
69
|
-
end
|
70
|
-
|
71
|
-
def max
|
72
|
-
return @max if @max
|
73
|
-
return @max = nil if @type == 'z'
|
74
|
-
@max = @characters.inject(1) do |sum, n|
|
75
|
-
i = case n
|
76
|
-
when 'e'
|
77
|
-
XDIGIT.length
|
78
|
-
when 'd'
|
79
|
-
10
|
80
|
-
else
|
81
|
-
1
|
82
|
-
end
|
83
|
-
sum *= i
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def n2xdig n
|
88
|
-
xdig = @characters.reverse.map do |c|
|
89
|
-
div = case c
|
90
|
-
when 'e' then XDIGIT.length
|
91
|
-
when 'd' then 10
|
92
|
-
end
|
93
|
-
next if div.nil?
|
94
|
-
|
95
|
-
value = n % div
|
96
|
-
n = n / div
|
97
|
-
XDIGIT[value]
|
98
|
-
end.compact.join ''
|
99
|
-
|
100
|
-
if @type == 'z'
|
101
|
-
while n > 0
|
102
|
-
c = @characters.last
|
103
|
-
div = case c
|
104
|
-
when 'e' then XDIGIT.length
|
105
|
-
when 'd' then 10
|
106
|
-
end
|
107
|
-
next if div.nil?
|
108
|
-
|
109
|
-
value = n % div
|
110
|
-
n = n / div
|
111
|
-
xdig += XDIGIT[value]
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
raise Exception if n > 0
|
116
|
-
|
117
|
-
xdig.reverse
|
118
|
-
end
|
119
|
-
|
120
|
-
def setup_mask args
|
121
|
-
@template = args[:template]
|
122
|
-
@prefix, @mask = @template.split('.')
|
123
|
-
|
124
|
-
@prefix = "#{args[:namespace]}/#{@prefix}" if args[:namespace]
|
125
|
-
|
126
|
-
@type, @characters = @mask.split '', 2
|
127
|
-
@characters = @characters.split ''
|
128
|
-
@check = @characters.pop and true if @characters.last == 'k'
|
129
|
-
@s = args[:s] || 0
|
130
|
-
case @type
|
131
|
-
when 's'
|
132
|
-
when 'z'
|
133
|
-
when 'r'
|
134
|
-
@rand = Random.new(args[:seed]) if args[:seed]
|
135
|
-
@rand ||= Random.new
|
136
|
-
@seed = @rand.seed
|
137
|
-
|
138
|
-
if args[:s]
|
139
|
-
args[:s].times { @rand.rand }
|
140
|
-
end
|
141
|
-
|
142
|
-
percounter = max / (args[:max_counters] || MAX_COUNTERS) + 1
|
143
|
-
t = 0
|
144
|
-
@counters = args[:counters]
|
145
|
-
@counters ||= Array.new(max/percounter) do |i|
|
146
|
-
{ :value => case i
|
147
|
-
when 0 then 0
|
148
|
-
else t += percounter
|
149
|
-
end, :max => t + percounter }
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
data/lib/noid/identifier.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Noid::Identifier
|
2
|
-
class Base
|
3
|
-
def initialize id
|
4
|
-
@id = id
|
5
|
-
@metadata = {}
|
6
|
-
end
|
7
|
-
|
8
|
-
def id
|
9
|
-
@id
|
10
|
-
end
|
11
|
-
|
12
|
-
def [] key
|
13
|
-
@metadata[key]
|
14
|
-
end
|
15
|
-
|
16
|
-
def []= key, value
|
17
|
-
@metadata[key] = value
|
18
|
-
end
|
19
|
-
|
20
|
-
def push hash
|
21
|
-
@metadata.merge! hash
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/noid/identifier/anvl.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Noid::Identifier
|
2
|
-
class Anvl < Base
|
3
|
-
def initialize id
|
4
|
-
@id = id
|
5
|
-
@metadata = ::ANVL::Document.new
|
6
|
-
end
|
7
|
-
|
8
|
-
def []= key, value
|
9
|
-
super
|
10
|
-
save
|
11
|
-
end
|
12
|
-
|
13
|
-
def push hash
|
14
|
-
@metadata << hash
|
15
|
-
save
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
def file
|
20
|
-
@id
|
21
|
-
end
|
22
|
-
|
23
|
-
def save
|
24
|
-
File.open(file, 'w') do |f|
|
25
|
-
f.write @metadata.to_s
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
data/lib/noid/persistence.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
module Noid::Persistence
|
3
|
-
class JSON < Base
|
4
|
-
FILENAME = 'NOID.js'
|
5
|
-
def initialize args = {}
|
6
|
-
@file = args[:filename] || FILENAME
|
7
|
-
@json = args[:json] || File.read(@file) rescue nil
|
8
|
-
data = load_json if @json
|
9
|
-
data ||= {}
|
10
|
-
super data.merge(args)
|
11
|
-
save
|
12
|
-
end
|
13
|
-
|
14
|
-
protected
|
15
|
-
def load_json
|
16
|
-
data = ::JSON.parse(@json) rescue nil
|
17
|
-
data ||= {}
|
18
|
-
data = data.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} unless data.empty?
|
19
|
-
data[:counters] = data[:counters].map { |x| x.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} } if data[:counters]
|
20
|
-
data[:identifier] = { :class => Kernel.const_get(data[:identifier]['class']) } if data[:identifier]
|
21
|
-
data
|
22
|
-
end
|
23
|
-
|
24
|
-
def save
|
25
|
-
str = self.to_json
|
26
|
-
File.open(@file, 'w') do |f|
|
27
|
-
f.write(str)
|
28
|
-
end unless @file == '/dev/null'
|
29
|
-
|
30
|
-
str
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_json
|
34
|
-
str = { :template => @template, :identifier => { :class => identifier.name }, :s => @s, :counters => @counters, :seed => @seed }.to_json
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
data/test/helper.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
begin
|
4
|
-
Bundler.setup(:default, :development)
|
5
|
-
rescue Bundler::BundlerError => e
|
6
|
-
$stderr.puts e.message
|
7
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
-
exit e.status_code
|
9
|
-
end
|
10
|
-
require 'test/unit'
|
11
|
-
require 'shoulda'
|
12
|
-
|
13
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
-
require 'noid'
|
16
|
-
|
17
|
-
class Test::Unit::TestCase
|
18
|
-
end
|
data/test/test_binding.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'anvl'
|
3
|
-
require 'tmpdir'
|
4
|
-
|
5
|
-
class TestNoidBinding < Test::Unit::TestCase
|
6
|
-
context "Noid" do
|
7
|
-
should "accept identifier_class for NOID bindings" do
|
8
|
-
Dir.mktmpdir do |d|
|
9
|
-
Dir.chdir d
|
10
|
-
n = Noid::Minter.new :template => 'r.rek', :identifier => { :class => Noid::Identifier::Base }
|
11
|
-
id = n.mint
|
12
|
-
assert_equal(Noid::Identifier::Base, id.class)
|
13
|
-
id['abc'] = 123
|
14
|
-
assert_equal(123, id['abc'])
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
should "use singleton instance to persist NOID bindings" do
|
19
|
-
Dir.mktmpdir do |d|
|
20
|
-
Dir.chdir d
|
21
|
-
n = Noid::Minter.new :template => 'r.rek', :identifier => { :class => Noid::Identifier::Singleton }
|
22
|
-
id = n.mint
|
23
|
-
id['abc'] = 123
|
24
|
-
assert_equal(123, id['abc'])
|
25
|
-
|
26
|
-
id2 = Noid::Identifier::Singleton.new id.id
|
27
|
-
assert_equal(123, id2['abc'])
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
should "user anvl instance to persist NOID binding info" do
|
32
|
-
Dir.mktmpdir do |d|
|
33
|
-
Dir.chdir d
|
34
|
-
n = Noid::Minter.new :template => 'r.rek', :identifier => { :class => Noid::Identifier::Anvl }
|
35
|
-
id = n.mint
|
36
|
-
id['abc'] = "123"
|
37
|
-
assert_equal(Noid::Identifier::Anvl, id.class)
|
38
|
-
assert_equal("123", id['abc'])
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/test/test_noid.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class TestNoid < Test::Unit::TestCase
|
4
|
-
context "Noid" do
|
5
|
-
should "generate checkdigits correctly" do
|
6
|
-
n = Noid::Minter.new :template => 's.zd'
|
7
|
-
assert_equal('q', n.send(:checkdigit, '13030/xf93gt2'))
|
8
|
-
end
|
9
|
-
|
10
|
-
should "generate max sequence for type 'r'" do
|
11
|
-
n = Noid::Minter.new :template => 's.rd'
|
12
|
-
assert_equal(0, n.send(:min))
|
13
|
-
assert_equal(10, n.send(:max))
|
14
|
-
|
15
|
-
n = Noid::Minter.new :template => 's.rdd'
|
16
|
-
assert_equal(0, n.send(:min))
|
17
|
-
assert_equal(100, n.send(:max))
|
18
|
-
end
|
19
|
-
|
20
|
-
should "generate quasi-random counters for type 'r'" do
|
21
|
-
n = Noid::Minter.new :template => 's.rd'
|
22
|
-
assert_equal((0..9).map { |x| {:value => x, :max => (x + 1)} }, n.instance_variable_get('@counters'))
|
23
|
-
|
24
|
-
n = Noid::Minter.new :template => 's.rdde'
|
25
|
-
s = n.instance_variable_get('@counters')
|
26
|
-
assert_contains(s, {:value => 2890, :max => 2900})
|
27
|
-
end
|
28
|
-
|
29
|
-
should "generate random sequence for type 'r'" do
|
30
|
-
n = Noid::Minter.new :template => 's.rd'
|
31
|
-
a = 10.times.map { |i| n.mint }
|
32
|
-
|
33
|
-
10.times do |i|
|
34
|
-
assert_contains(a, "s#{i}")
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
should "generate numeric sequence for type 's'" do
|
40
|
-
n = Noid::Minter.new :template => 's.sd'
|
41
|
-
10.times do |i|
|
42
|
-
assert_equal("s#{i}", n.mint)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
should "generate extended sequence for type 's'" do
|
47
|
-
n = Noid::Minter.new :template => 's.se'
|
48
|
-
10.times do |i|
|
49
|
-
assert_equal("s#{i}", n.mint)
|
50
|
-
end
|
51
|
-
assert_equal("sb", n.mint)
|
52
|
-
10.times { n.mint }
|
53
|
-
assert_equal("sq", n.mint)
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
should "raise an exception when overflowing sequence" do
|
58
|
-
n = Noid::Minter.new :template => 's.sd'
|
59
|
-
10.times do |i|
|
60
|
-
assert_equal("s#{i}", n.mint)
|
61
|
-
end
|
62
|
-
|
63
|
-
assert_raise Exception do
|
64
|
-
n.mint
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
should "generate sequence for type 's' with checkdigit" do
|
69
|
-
n = Noid::Minter.new :template => 's.sdk'
|
70
|
-
assert_equal('s0s', n.mint)
|
71
|
-
assert_equal('s1v', n.mint)
|
72
|
-
assert_equal('s2x', n.mint)
|
73
|
-
end
|
74
|
-
|
75
|
-
should "generate sequence for type 'z' with checkdigit" do
|
76
|
-
n = Noid::Minter.new :template => 'z.zdk'
|
77
|
-
assert_equal('z0z', n.mint)
|
78
|
-
assert_equal('z11', n.mint)
|
79
|
-
assert_equal('z23', n.mint)
|
80
|
-
end
|
81
|
-
should "generate sequence for type 'z', adding new digits as needed" do
|
82
|
-
n = Noid::Minter.new :template => 'z.zdk'
|
83
|
-
assert_equal('z0z', n.mint)
|
84
|
-
assert_equal('z11', n.mint)
|
85
|
-
assert_equal('z23', n.mint)
|
86
|
-
10.times { n.mint }
|
87
|
-
assert_equal('z13b', n.mint)
|
88
|
-
end
|
89
|
-
|
90
|
-
should "generate sequence for type 'z', adding new xdigits as needed" do
|
91
|
-
n = Noid::Minter.new :template => 'z.zdek'
|
92
|
-
assert_equal('z00z', n.mint)
|
93
|
-
assert_equal('z012', n.mint)
|
94
|
-
assert_equal('z025', n.mint)
|
95
|
-
10.times {
|
96
|
-
assert_match(/^z[0-9]/, n.mint)
|
97
|
-
}
|
98
|
-
assert_equal('z0f9', n.mint)
|
99
|
-
100.times {
|
100
|
-
assert_match(/^z[0-9]/, n.mint)
|
101
|
-
}
|
102
|
-
assert_equal('z3xz', n.mint)
|
103
|
-
1000.times {
|
104
|
-
assert_match(/^z[0-9]/, n.mint)
|
105
|
-
}
|
106
|
-
assert_equal('z38fs', n.mint)
|
107
|
-
end
|
108
|
-
|
109
|
-
should "validate 'r' digit sequences" do
|
110
|
-
n = Noid::Minter.new :template => 'r.rd'
|
111
|
-
|
112
|
-
assert_equal(true, n.valid?('r1') )
|
113
|
-
assert_equal(true, n.valid?('r9') )
|
114
|
-
assert_equal(false, n.valid?('r11') )
|
115
|
-
assert_equal(false, n.valid?('ro'))
|
116
|
-
assert_equal(false, n.valid?('rb'))
|
117
|
-
end
|
118
|
-
|
119
|
-
should "validate 'r' xdigit sequences" do
|
120
|
-
n = Noid::Minter.new :template => 'r.re'
|
121
|
-
|
122
|
-
assert_equal(true, n.valid?('r1') )
|
123
|
-
assert_equal(true, n.valid?('r9') )
|
124
|
-
assert_equal(false, n.valid?('ro'))
|
125
|
-
assert_equal(true, n.valid?('rb'))
|
126
|
-
end
|
127
|
-
should "validate 'r' xdigit + checkdigit sequences" do
|
128
|
-
n = Noid::Minter.new :template => 'r.rek'
|
129
|
-
|
130
|
-
assert_equal(true, n.valid?('r2w') )
|
131
|
-
assert_equal(false, n.valid?('r2b') )
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|