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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile CHANGED
@@ -1,16 +1,4 @@
1
1
  source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
- gem "anvl"
6
- gem "json"
7
- gem "backports"
8
2
 
9
- # Add dependencies to develop your gem here.
10
- # Include everything needed to run rake, tests, features, etc.
11
- group :development do
12
- gem "shoulda", ">= 0"
13
- gem "bundler", "~> 1.0.0"
14
- gem "jeweler", "~> 1.5.1"
15
- gem "rcov", ">= 0"
16
- end
3
+ # Specify your gem's dependencies in noid.gemspec
4
+ gemspec
data/Rakefile CHANGED
@@ -1,53 +1,8 @@
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 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "noid"
16
- gem.homepage = "http://github.com/cbeer/noid"
17
- gem.license = "MIT"
18
- gem.summary = %Q{Nice Opaque Identifier}
19
- gem.description = %Q{}
20
- gem.email = "chris@cbeer.info"
21
- gem.authors = ["Chris Beer"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
- # gem.add_development_dependency 'rspec', '> 1.2.3'
26
- end
27
- Jeweler::RubygemsDotOrgTasks.new
28
-
29
- require 'rake/testtask'
30
- Rake::TestTask.new(:test) do |test|
31
- test.libs << 'lib' << 'test'
32
- test.pattern = 'test/**/test_*.rb'
33
- test.verbose = true
34
- end
35
-
36
- require 'rcov/rcovtask'
37
- Rcov::RcovTask.new do |test|
38
- test.libs << 'test'
39
- test.pattern = 'test/**/test_*.rb'
40
- test.verbose = true
41
- end
42
-
43
- task :default => :test
1
+ require 'bundler/gem_tasks'
44
2
 
45
- require 'rake/rdoctask'
46
- Rake::RDocTask.new do |rdoc|
47
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
3
+ require 'rspec/core/rake_task'
48
4
 
49
- rdoc.rdoc_dir = 'rdoc'
50
- rdoc.title = "noid #{version}"
51
- rdoc.rdoc_files.include('README*')
52
- rdoc.rdoc_files.include('lib/**/*.rb')
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
7
+ t.pattern = 'spec/**/*_spec.rb'
53
8
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
data/lib/noid.rb CHANGED
@@ -1,13 +1,8 @@
1
- require 'noid/base'
2
- require 'noid/minter'
3
- require 'noid/persistence'
4
- require 'noid/persistence/base'
5
- require 'noid/persistence/json'
6
- require 'noid/identifier'
7
- require 'noid/identifier/singleton'
8
- require 'noid/identifier/anvl'
9
- require 'noid/active_record'
10
- require 'noid/active_record/provider'
11
- module Noid
1
+ require "noid/version"
2
+ require "noid/minter"
3
+ require "noid/template"
12
4
 
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
13
8
  end
data/lib/noid/minter.rb CHANGED
@@ -1,5 +1,117 @@
1
+ require 'backports'
2
+ require 'backports'
3
+
1
4
  module Noid
2
5
  class Minter
3
- include Noid::Base
6
+ attr_reader :seed, :seq
7
+ attr_writer :counters
8
+
9
+ def initialize args = {}
10
+ @seq = 0
11
+ seed(args[:seed], args[:seq])
12
+ @template_string = args[:template]
13
+ @max_counters = args[:max_counters]
14
+ @counters = args[:counters]
15
+ end
16
+
17
+ ##
18
+ # Mint a new identifier
19
+ def mint
20
+ n = nil
21
+
22
+
23
+ case template.generator
24
+ when 's'
25
+ n = next_in_sequence
26
+ when 'z'
27
+ n = next_in_sequence
28
+ when 'r'
29
+ raise Exception if counters.size == 0
30
+ i = @rand.rand(counters.size)
31
+ next_in_sequence
32
+ n = counters[i][:value]
33
+ counters[i][:value] += 1
34
+ counters.delete_at(i) if counters[i][:value] == counters[i][:max]
35
+ end
36
+
37
+ template.mint(n)
38
+ end
39
+
40
+ ##
41
+ # Noid identifier template
42
+ #
43
+ # @return Noid::Template
44
+ def template
45
+ @template ||= Noid::Template.new(@template_string)
46
+ end
47
+
48
+ ##
49
+ # Is the identifier valid under the template string and checksum?
50
+ # @param [String] id
51
+ # @return bool
52
+ def valid? id
53
+ prefix = id[0..@prefix.length-1]
54
+ ch = id[@prefix.length..-1].split('')
55
+ check = ch.pop if @check
56
+ return false unless prefix == @prefix
57
+
58
+ return false unless @characters.length == ch.length
59
+ @characters.each_with_index do |c, i|
60
+ return false unless Noid::XDIGIT.include? ch[i]
61
+ return false if c == 'd' and ch[i] =~ /[^\d]/
62
+ end
63
+
64
+ return false unless check.nil? or check == checkdigit(id[0..-2])
65
+
66
+ true
67
+ end
68
+
69
+ ##
70
+ # Seed the random number generator with a seed and sequence offset
71
+ # @param [Integer] seed
72
+ # @param [Integer] seq
73
+ # @return [Random]
74
+ def seed seed = nil, seq = 0
75
+ @rand = Random.new(seed) if seed
76
+ @rand ||= Random.new
77
+ @seed = @rand.seed
78
+
79
+ seq.times { @rand.rand } if seq
80
+
81
+ @rand
82
+ end
83
+
84
+ def next_in_sequence
85
+ n = @seq
86
+ @seq += 1
87
+ n
88
+ end
89
+
90
+ ##
91
+ # Counters to use for quasi-random NOID sequences
92
+ def counters
93
+ return @counters if @counters
94
+ return [] unless template.generator == "r"
95
+
96
+ percounter = template.max / (@max_counters || Noid::MAX_COUNTERS) + 1
97
+ t = 0
98
+ @counters = []
99
+
100
+ while t < template.max
101
+ counter = {}
102
+ counter[:value] = t
103
+ counter[:max] = [t + percounter, template.max].min
104
+
105
+ t += percounter
106
+
107
+ @counters << counter
108
+ end
109
+
110
+ @counters
111
+ end
112
+
113
+ def dump
114
+ { :seq => @seq, :seed => @seed, :template => template.template, :counters => Marshal.load(Marshal.dump(counters)) }
115
+ end
4
116
  end
5
117
  end
@@ -0,0 +1,144 @@
1
+ module Noid
2
+ class Template
3
+ attr_reader :template
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
8
+ end
9
+
10
+ def mint n
11
+ str = prefix
12
+ str += n2xdig(n)
13
+ str += checkdigit(str) if checkdigit?
14
+
15
+ str
16
+ end
17
+
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.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
+
38
+ true
39
+ end
40
+
41
+ ##
42
+ # identifier prefix string
43
+ def prefix
44
+ @prefix ||= @template.split('.').first
45
+ end
46
+
47
+ ##
48
+ # identifier mask string
49
+ def mask
50
+ @mask ||= @template.split('.').last
51
+ end
52
+
53
+ ##
54
+ # generator type to use: r, s, z
55
+ def generator
56
+ @generator ||= mask[0..0]
57
+ end
58
+
59
+ ##
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
69
+ end
70
+
71
+ ##
72
+ # should generated identifiers have a checkdigit?
73
+ def checkdigit?
74
+ mask.split('').last == 'k'
75
+ end
76
+
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 ]
83
+ end
84
+
85
+ ##
86
+ # minimum sequence value
87
+ def min
88
+ @min ||= 0
89
+ end
90
+
91
+ ##
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
102
+ end
103
+
104
+
105
+ protected
106
+ ##
107
+ # total size of a given template character value
108
+ # @param [String] c
109
+ def character_space c
110
+ case c
111
+ when 'e'
112
+ Noid::XDIGIT.length
113
+ when 'd'
114
+ 10
115
+ end
116
+ end
117
+
118
+ ##
119
+ # convert a minter position to a noid string under this template
120
+ # @param [Integer] n
121
+ # @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)
126
+ Noid::XDIGIT[value]
127
+ end.compact.join('')
128
+
129
+ if generator == 'z'
130
+ c = characters.split('').last
131
+ while n > 0
132
+ value = n % character_space(c)
133
+ n = n / character_space(c)
134
+ xdig += Noid::XDIGIT[value]
135
+ end
136
+ end
137
+
138
+ raise Exception if n > 0
139
+
140
+ xdig.reverse
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,9 @@
1
+ module Noid
2
+ unless Noid.const_defined? :VERSION
3
+ def self.version
4
+ @version ||= File.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION')).chomp
5
+ end
6
+
7
+ VERSION = self.version
8
+ end
9
+ end
data/noid.gemspec CHANGED
@@ -1,87 +1,25 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "noid/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{noid}
8
- s.version = "0.4.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Chris Beer"]
12
- s.date = %q{2010-12-23}
6
+ s.name = "noid"
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}
13
12
  s.description = %q{}
14
- s.email = %q{chris@cbeer.info}
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- "Gemfile",
22
- "LICENSE.txt",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION",
26
- "doc/active_record_sample.rb",
27
- "lib/noid.rb",
28
- "lib/noid/active_record.rb",
29
- "lib/noid/active_record/provider.rb",
30
- "lib/noid/base.rb",
31
- "lib/noid/identifier.rb",
32
- "lib/noid/identifier/anvl.rb",
33
- "lib/noid/identifier/singleton.rb",
34
- "lib/noid/minter.rb",
35
- "lib/noid/persistence.rb",
36
- "lib/noid/persistence/base.rb",
37
- "lib/noid/persistence/json.rb",
38
- "noid.gemspec",
39
- "test/helper.rb",
40
- "test/test_binding.rb",
41
- "test/test_noid.rb",
42
- "test/test_persistence.rb"
43
- ]
44
- s.homepage = %q{http://github.com/cbeer/noid}
45
- s.licenses = ["MIT"]
46
- s.require_paths = ["lib"]
47
- s.rubygems_version = %q{1.3.7}
48
- s.summary = %q{Nice Opaque Identifier}
49
- s.test_files = [
50
- "test/helper.rb",
51
- "test/test_binding.rb",
52
- "test/test_noid.rb",
53
- "test/test_persistence.rb"
54
- ]
55
13
 
56
- if s.respond_to? :specification_version then
57
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
- s.specification_version = 3
14
+ s.rubyforge_project = "noid"
59
15
 
60
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
61
- s.add_runtime_dependency(%q<anvl>, [">= 0"])
62
- s.add_runtime_dependency(%q<json>, [">= 0"])
63
- s.add_runtime_dependency(%q<backports>, [">= 0"])
64
- s.add_development_dependency(%q<shoulda>, [">= 0"])
65
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
66
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
67
- s.add_development_dependency(%q<rcov>, [">= 0"])
68
- else
69
- s.add_dependency(%q<anvl>, [">= 0"])
70
- s.add_dependency(%q<json>, [">= 0"])
71
- s.add_dependency(%q<backports>, [">= 0"])
72
- s.add_dependency(%q<shoulda>, [">= 0"])
73
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
- s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
75
- s.add_dependency(%q<rcov>, [">= 0"])
76
- end
77
- else
78
- s.add_dependency(%q<anvl>, [">= 0"])
79
- s.add_dependency(%q<json>, [">= 0"])
80
- s.add_dependency(%q<backports>, [">= 0"])
81
- s.add_dependency(%q<shoulda>, [">= 0"])
82
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
83
- s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
84
- s.add_dependency(%q<rcov>, [">= 0"])
85
- end
86
- end
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
87
20
 
21
+ s.add_dependency "backports"
22
+ s.add_development_dependency "bundler", "~> 1.0.0"
23
+ s.add_development_dependency "rspec", ">= 2.0"
24
+ s.add_development_dependency "rcov", ">= 0"
25
+ end