uuid 1.0.3 → 1.0.4
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.
- data/CHANGELOG +7 -8
- data/MIT-LICENSE +1 -1
- data/Rakefile +118 -46
- data/bin/uuid-setup +1 -1
- data/lib/uuid.rb +160 -178
- data/test/test-uuid.rb +5 -1
- metadata +3 -3
data/CHANGELOG
CHANGED
@@ -1,20 +1,19 @@
|
|
1
|
-
|
1
|
+
1.0.4 (1/2/2006)
|
2
|
+
* Changed: By default creates the uuid.state file in the working directory,
|
3
|
+
not in the installation directory, which requires sudo privileges (e.g. gem).
|
2
4
|
|
5
|
+
1.0.3 (1/8/2006)
|
3
6
|
* Fixed: Work around YAML bug in serializing that occurs when MAC address
|
4
7
|
consists only of decimal digits. Credit: ebuprofen"
|
5
8
|
|
6
|
-
|
7
|
-
|
9
|
+
1.0.2 (1/28/2006)
|
8
10
|
* Changed: Constants are not conditionally defined (removes warnings when
|
9
11
|
using in Rails.
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
1.0.1 (1/26/2006)
|
13
14
|
* Added: Regular expressions to test if a string is a UUID.
|
14
15
|
* Changed: When used in ActiveRecord, adds as callback instead of overriding
|
15
16
|
save.
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
1.0.0 (1/20/2005)
|
19
19
|
* Changed: Separated form reliable-msg into its own package.
|
20
|
-
|
data/MIT-LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -1,11 +1,35 @@
|
|
1
1
|
# Adapted from the rake Rakefile.
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
-
Gem::manage_gems
|
5
4
|
require "rake/testtask"
|
6
5
|
require "rake/rdoctask"
|
7
6
|
require "rake/gempackagetask"
|
8
7
|
|
8
|
+
VERSION_FILE = "lib/uuid.rb"
|
9
|
+
# Create the GEM package.
|
10
|
+
spec = Gem::Specification.new do |spec|
|
11
|
+
spec.name = "uuid"
|
12
|
+
spec.version = File.read(__FILE__.pathmap("%d/#{VERSION_FILE}")).scan(/VERSION\s*=\s*(['"])(.*)\1/)[0][1]
|
13
|
+
spec.summary = "UUID generator"
|
14
|
+
spec.description = <<-EOF
|
15
|
+
UUID generator for producing universally unique identifiers based
|
16
|
+
on RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt).
|
17
|
+
EOF
|
18
|
+
spec.author = "Assaf Arkin"
|
19
|
+
spec.email = "assaf@labnotes.org"
|
20
|
+
spec.homepage = "http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator"
|
21
|
+
spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile", "CHANGELOG"].to_a
|
22
|
+
spec.require_path = "lib"
|
23
|
+
spec.autorequire = "uuid.rb"
|
24
|
+
spec.bindir = "bin"
|
25
|
+
spec.executables = ["uuid-setup"]
|
26
|
+
spec.default_executable = "uuid-setup"
|
27
|
+
spec.has_rdoc = true
|
28
|
+
spec.rdoc_options << "--main" << "README" << "--title" << "UUID generator" << "--line-numbers"
|
29
|
+
spec.extra_rdoc_files = ["README"]
|
30
|
+
spec.rubyforge_project = "reliable-msg"
|
31
|
+
end
|
32
|
+
|
9
33
|
|
10
34
|
desc "Default Task"
|
11
35
|
task :default => [:test, :rdoc]
|
@@ -13,19 +37,104 @@ task :default => [:test, :rdoc]
|
|
13
37
|
|
14
38
|
desc "Run all test cases"
|
15
39
|
Rake::TestTask.new do |test|
|
16
|
-
|
17
|
-
|
18
|
-
|
40
|
+
test.verbose = true
|
41
|
+
test.test_files = ["test/*.rb"]
|
42
|
+
#test.warning = true
|
19
43
|
end
|
20
44
|
|
21
|
-
|
22
45
|
# Create the documentation.
|
23
46
|
Rake::RDocTask.new do |rdoc|
|
24
|
-
|
25
|
-
|
26
|
-
|
47
|
+
rdoc.main = "README"
|
48
|
+
rdoc.rdoc_files.include("README", "lib/**/*.rb")
|
49
|
+
rdoc.title = "UUID generator"
|
50
|
+
end
|
51
|
+
|
52
|
+
gem = Rake::GemPackageTask.new(spec) do |pkg|
|
53
|
+
pkg.need_tar = true
|
54
|
+
pkg.need_zip = true
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Install the package locally"
|
58
|
+
task :install=>:package do |task|
|
59
|
+
system "gem", "install", "pkg/#{spec.name}-#{spec.version}.gem"
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Uninstall previously installed packaged"
|
63
|
+
task :uninstall do |task|
|
64
|
+
system "gem", "uninstall", spec.name, "-v", spec.version.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
desc "Look for TODO and FIXME tags in the code"
|
69
|
+
task :todo do
|
70
|
+
FileList["**/*.rb"].egrep /#.*(FIXME|TODO|TBD)/
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# --------------------------------------------------------------------
|
75
|
+
# Creating a release
|
76
|
+
|
77
|
+
|
78
|
+
namespace :upload do
|
79
|
+
task :packages=>["rake:rdoc", "rake:package"] do |task|
|
80
|
+
require 'rubyforge'
|
81
|
+
|
82
|
+
# Read the changes for this release.
|
83
|
+
pattern = /(^(\d+\.\d+(?:\.\d+)?)\s+\(\d+\/\d+\/\d+\)\s*((:?^[^\n]+\n)*))/
|
84
|
+
changelog = File.read(__FILE__.pathmap("%d/CHANGELOG"))
|
85
|
+
changes = changelog.scan(pattern).inject({}) { |hash, set| hash[set[1]] = set[2] ; hash }
|
86
|
+
current = changes[spec.version.to_s]
|
87
|
+
if !current && spec.version.to_s =~ /\.0$/
|
88
|
+
current = changes[spec.version.to_s.split(".")[0..-2].join(".")]
|
89
|
+
end
|
90
|
+
fail "No changeset found for version #{spec.version}" unless current
|
91
|
+
|
92
|
+
puts "Uploading #{spec.name} #{spec.version}"
|
93
|
+
files = %w( gem tgz zip ).map { |ext| "pkg/#{spec.name}-#{spec.version}.#{ext}" }
|
94
|
+
rubyforge = RubyForge.new
|
95
|
+
rubyforge.login
|
96
|
+
File.open(".changes", 'w'){|f| f.write(current)}
|
97
|
+
rubyforge.userconfig.merge!("release_changes" => ".changes", "preformatted" => true)
|
98
|
+
rubyforge.add_release spec.rubyforge_project.downcase, spec.name.downcase, spec.version, *files
|
99
|
+
rm ".changes"
|
100
|
+
puts "Release #{spec.version} uploaded"
|
101
|
+
end
|
27
102
|
end
|
28
103
|
|
104
|
+
namespace :release do
|
105
|
+
task :ready? do
|
106
|
+
require 'highline'
|
107
|
+
require 'highline/import'
|
108
|
+
|
109
|
+
puts "This version: #{spec.version}"
|
110
|
+
puts
|
111
|
+
puts "Top 4 lines form CHANGELOG:"
|
112
|
+
puts File.readlines("CHANGELOG")[0..3].map { |l| " #{l}" }
|
113
|
+
puts
|
114
|
+
ask("Top-entry in CHANGELOG file includes today's date?") =~ /yes/i or
|
115
|
+
fail "Please update CHANGELOG to include the right date"
|
116
|
+
end
|
117
|
+
|
118
|
+
task :post do
|
119
|
+
# Practical example of functional read but not comprehend code:
|
120
|
+
next_version = spec.version.to_ints.zip([0, 0, 1]).map { |a| a.inject(0) { |t,i| t + i } }.join(".")
|
121
|
+
puts "Updating #{VERSION_FILE} to next version number: #{next_version}"
|
122
|
+
ver_file = File.read(__FILE__.pathmap("%d/#{VERSION_FILE}")).
|
123
|
+
sub(/(VERSION\s*=\s*)(['"])(.*)\2/) { |line| "#{$1}#{$2}#{next_version}#{$2}" }
|
124
|
+
File.open(__FILE__.pathmap("%d/#{VERSION_FILE}"), "w") { |file| file.write ver_file }
|
125
|
+
puts "Adding entry to CHANGELOG"
|
126
|
+
changelog = File.read(__FILE__.pathmap("%d/CHANGELOG"))
|
127
|
+
File.open(__FILE__.pathmap("%d/CHANGELOG"), "w") { |file| file.write "#{next_version} (Pending)\n\n#{changelog}" }
|
128
|
+
end
|
129
|
+
|
130
|
+
task :meat=>["clobber", "test", "upload:packages"]
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "Upload release to RubyForge including docs, tag SVN"
|
134
|
+
task :release=>[ "release:ready?", "release:meat", "release:post" ]
|
135
|
+
|
136
|
+
=begin
|
137
|
+
|
29
138
|
# Handle version number.
|
30
139
|
class Version
|
31
140
|
|
@@ -83,45 +192,8 @@ end
|
|
83
192
|
version = Version.new "lib/uuid.rb", ENV["version"]
|
84
193
|
|
85
194
|
|
86
|
-
# Create the GEM package.
|
87
|
-
gem_spec = Gem::Specification.new do |spec|
|
88
|
-
spec.name = "uuid"
|
89
|
-
spec.version = version.next
|
90
|
-
spec.summary = "UUID generator"
|
91
|
-
spec.description = <<-EOF
|
92
|
-
UUID generator for producing universally unique identifiers based
|
93
|
-
on RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt).
|
94
|
-
EOF
|
95
|
-
spec.author = "Assaf Arkin"
|
96
|
-
spec.email = "assaf@labnotes.org"
|
97
|
-
spec.homepage = "http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator"
|
98
|
-
spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile", "CHANGELOG"].to_a
|
99
|
-
spec.require_path = "lib"
|
100
|
-
spec.autorequire = "uuid.rb"
|
101
|
-
spec.bindir = "bin"
|
102
|
-
spec.executables = ["uuid-setup"]
|
103
|
-
spec.default_executable = "uuid-setup"
|
104
|
-
spec.has_rdoc = true
|
105
|
-
spec.rdoc_options << "--main" << "README" << "--title" << "UUID generator" << "--line-numbers"
|
106
|
-
spec.extra_rdoc_files = ["README"]
|
107
|
-
spec.rubyforge_project = "reliable-msg"
|
108
|
-
end
|
109
|
-
|
110
|
-
gem = Rake::GemPackageTask.new(gem_spec) do |pkg|
|
111
|
-
pkg.need_tar = true
|
112
|
-
pkg.need_zip = true
|
113
|
-
end
|
114
195
|
|
115
196
|
|
116
|
-
desc "Look for TODO and FIXME tags in the code"
|
117
|
-
task :todo do
|
118
|
-
FileList["**/*.rb"].egrep /#.*(FIXME|TODO|TBD)/
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
# --------------------------------------------------------------------
|
123
|
-
# Creating a release
|
124
|
-
|
125
197
|
desc "Make a new release"
|
126
198
|
task :release => [:test, :prerelease, :clobber, :update_version, :package] do
|
127
199
|
puts
|
@@ -145,4 +217,4 @@ task :update_version => [:prerelease] do
|
|
145
217
|
version.update
|
146
218
|
end
|
147
219
|
end
|
148
|
-
|
220
|
+
=end
|
data/bin/uuid-setup
CHANGED
data/lib/uuid.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# Author:: Assaf Arkin assaf@labnotes.org
|
5
5
|
# Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
|
6
|
-
# Copyright:: Copyright (c) 2005,
|
6
|
+
# Copyright:: Copyright (c) 2005,2007 Assaf Arkin
|
7
7
|
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
8
8
|
|
9
9
|
|
@@ -13,7 +13,6 @@ require 'singleton'
|
|
13
13
|
require 'logger'
|
14
14
|
|
15
15
|
|
16
|
-
|
17
16
|
# == Generating UUIDs
|
18
17
|
#
|
19
18
|
# Call UUID.new to generate and return a new UUID. The method returns a string in one of three
|
@@ -57,7 +56,7 @@ require 'logger'
|
|
57
56
|
#
|
58
57
|
# The UUID generator requires a state file which maintains the MAC address and next sequence
|
59
58
|
# number to use. By default, the UUID generator will use the file <tt>uuid.state</tt> contained
|
60
|
-
# in the current directory
|
59
|
+
# in the current directory.
|
61
60
|
#
|
62
61
|
# Use UUID.config to specify a different location for the UUID state file. If the UUID state file
|
63
62
|
# does not exist, you can create one manually, or use UUID.config with the options <tt>:sequence</tt>
|
@@ -69,7 +68,6 @@ require 'logger'
|
|
69
68
|
# mac_addr: 08-0E-46-21-4B-35
|
70
69
|
# sequence: "0x1639"
|
71
70
|
#
|
72
|
-
#
|
73
71
|
#--
|
74
72
|
# === Time-based UUID
|
75
73
|
#
|
@@ -98,8 +96,7 @@ require 'logger'
|
|
98
96
|
# === UUID state file
|
99
97
|
#
|
100
98
|
# The UUID state is contained in the UUID state file. The file name can be specified when configuring
|
101
|
-
# the UUID generator with UUID.config. The default is to use the file +uuid.state+ in the current directory
|
102
|
-
# or the installation directory.
|
99
|
+
# the UUID generator with UUID.config. The default is to use the file +uuid.state+ in the current directory.
|
103
100
|
#
|
104
101
|
# The UUID state file is read once when the UUID generator is first used (or configured). The sequence
|
105
102
|
# number contained in the UUID is read and used, and the state file is updated to the next sequence
|
@@ -111,186 +108,116 @@ require 'logger'
|
|
111
108
|
# application is running.
|
112
109
|
#++
|
113
110
|
module UUID
|
111
|
+
|
112
|
+
VERSION = '1.0.4'
|
114
113
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
PACKAGE = "uuid"
|
114
|
+
# Regular expression to identify a 36 character UUID. Can be used for a partial match.
|
115
|
+
REGEXP = /[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}/
|
119
116
|
|
120
|
-
|
121
|
-
|
117
|
+
# Regular expression to identify a 36 character UUID. Can only be used for a full match.
|
118
|
+
REGEXP_FULL = /^[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}$/
|
122
119
|
|
123
|
-
|
124
|
-
|
120
|
+
# Regular expression to identify a 32 character UUID, no hyphens (compact) full match.
|
121
|
+
REGEXP_COMPACT = /^[[:xdigit:]]{32}$/
|
125
122
|
|
126
|
-
|
127
|
-
|
123
|
+
# Default state file.
|
124
|
+
STATE_FILE = 'uuid.state'
|
128
125
|
|
129
|
-
|
130
|
-
|
126
|
+
# Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
|
127
|
+
CLOCK_MULTIPLIER = 10000000 #:nodoc:
|
131
128
|
|
132
|
-
|
133
|
-
|
129
|
+
# Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
|
130
|
+
CLOCK_GAPS = 100000 #:nodoc:
|
134
131
|
|
135
|
-
|
136
|
-
|
132
|
+
# Version number stamped into the UUID to identify it as time-based.
|
133
|
+
VERSION_CLOCK = 0x0100 #:nodoc:
|
137
134
|
|
138
|
-
|
135
|
+
# Formats supported by the UUID generator.
|
136
|
+
FORMATS = {
|
137
|
+
:compact=>'%08x%04x%04x%04x%012x',
|
138
|
+
:default=>'%08x-%04x-%04x-%04x-%012x', :urn=>'urn:uuid:%08x-%04x-%04x-%04x-%012x' } #:nodoc:
|
139
139
|
|
140
|
-
|
140
|
+
# Length (in characters) of UUIDs generated for each of the formats.
|
141
|
+
FORMATS_LENGTHS = { :compact=>32, :default=>36, :urn=>45 } #:nodoc:
|
141
142
|
|
142
|
-
|
143
|
+
ERROR_INVALID_SEQUENCE = "Invalid sequence number: found '%s', expected 4 hexdecimal digits" #:nodoc:
|
143
144
|
|
144
|
-
|
145
|
+
ERROR_NOT_A_SEQUENCE = "Not a sequence number: expected integer between 0 and 0xFFFF" #:nodoc:
|
145
146
|
|
146
|
-
|
147
|
+
ERROR_INVALID_MAC_ADDR = "Invalid MAC address: found '%s', expected a number in the format XX-XX-XX-XX-XX-XX" #:nodoc:
|
147
148
|
|
148
|
-
|
149
|
+
INFO_INITIALIZED = "Initialized UUID generator with sequence number 0x%04x and MAC address %s" #:nodoc:
|
149
150
|
|
150
|
-
|
151
|
+
ERROR_INITIALIZED_RANDOM_1 = "Initialized UUID generator with random sequence number/MAC address." #:nodoc:
|
151
152
|
|
153
|
+
ERROR_INITIALIZED_RANDOM_2 = "UUIDs are not guaranteed to be unique. Please create a uuid.state file as soon as possible." #:nodoc:
|
152
154
|
|
153
|
-
|
154
|
-
REGEXP = /[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}/
|
155
|
-
|
156
|
-
# Regular expression to identify a 36 character UUID. Can only be used for a full match.
|
157
|
-
REGEXP_FULL = /^[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}$/
|
158
|
-
end
|
155
|
+
IFCONFIG_PATTERN = /[^:\-](?:[0-9A-Za-z][0-9A-Za-z][:\-]){5}[0-9A-Za-z][0-9A-Za-z][^:\-]/ #:nodoc:
|
159
156
|
|
157
|
+
class << self
|
160
158
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
raise RuntimeError, "I don't know the format '#{format}'"
|
186
|
-
|
187
|
-
# The clock must be monotonically increasing. The clock resolution is at best 100 ns
|
188
|
-
# (UUID spec), but practically may be lower (on my setup, around 1ms). If this method
|
189
|
-
# is called too fast, we don't have a monotonically increasing clock, so the solution is
|
190
|
-
# to just wait.
|
191
|
-
# It is possible for the clock to be adjusted backwards, in which case we would end up
|
192
|
-
# blocking for a long time. When backward clock is detected, we prevent duplicates by
|
193
|
-
# asking for a new sequence number and continue with the new clock.
|
194
|
-
clock = @@mutex.synchronize do
|
195
|
-
# Initialize UUID generator if not already initialized. Uninitizlied UUID generator has no
|
196
|
-
# last known clock.
|
197
|
-
next_sequence unless @@last_clock
|
198
|
-
clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
|
199
|
-
if clock > @@last_clock
|
200
|
-
@@drift = 0
|
201
|
-
@@last_clock = clock
|
202
|
-
elsif clock = @@last_clock
|
203
|
-
drift = @@drift += 1
|
204
|
-
if drift < 10000
|
205
|
-
@@last_clock += 1
|
206
|
-
else
|
207
|
-
Thread.pass
|
208
|
-
nil
|
209
|
-
end
|
210
|
-
else
|
211
|
-
next_sequence
|
212
|
-
@@last_clock = clock
|
159
|
+
# Configures the UUID generator. Use this method to specify the UUID state file, logger, etc.
|
160
|
+
#
|
161
|
+
# The method accepts the following options:
|
162
|
+
# * <tt>:state_file</tt> -- Specifies the location of the state file. If missing, the default
|
163
|
+
# is <tt>uuid.state</tt>
|
164
|
+
# * <tt>:logger<tt> -- The UUID generator will use this logger to report the state information (optional).
|
165
|
+
# * <tt>:sequence</tt> -- Specifies the sequence number (0 to 0xFFFF) to use. Required to
|
166
|
+
# create a new state file, ignored if the state file already exists.
|
167
|
+
# * <tt>:mac_addr</tt> -- Specifies the MAC address (xx-xx-xx-xx-xx) to use. Required to
|
168
|
+
# create a new state file, ignored if the state file already exists.
|
169
|
+
#
|
170
|
+
# For example, to create a new state file:
|
171
|
+
# UUID.config :state_file=>'my-uuid.state', :sequence=>rand(0x10000), :mac_addr=>'0C-0E-35-41-60-65'
|
172
|
+
# To use an existing state file and log to +STDOUT+:
|
173
|
+
# UUID.config :state_file=>'my-uuid.state', :logger=>Logger.new(STDOUT)
|
174
|
+
#
|
175
|
+
# :call-seq:
|
176
|
+
# UUID.config(config)
|
177
|
+
#
|
178
|
+
def config(options)
|
179
|
+
options ||= {}
|
180
|
+
@@mutex.synchronize do
|
181
|
+
@@logger = options[:logger]
|
182
|
+
next_sequence options
|
213
183
|
end
|
214
|
-
end while not clock
|
215
|
-
sprintf template, clock & 0xFFFFFFFF, (clock >> 32)& 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
|
216
|
-
@@sequence & 0xFFFF, @@mac_hex & 0xFFFFFFFFFFFF
|
217
|
-
end
|
218
|
-
|
219
|
-
|
220
|
-
alias uuid new
|
221
|
-
module_function :uuid, :new
|
222
|
-
|
223
|
-
|
224
|
-
# Configures the UUID generator. Use this method to specify the UUID state file, logger, etc.
|
225
|
-
#
|
226
|
-
# The method accepts the following options:
|
227
|
-
# * <tt>:state_file</tt> -- Specifies the location of the state file. If missing, the default
|
228
|
-
# is <tt>uuid.state</tt>
|
229
|
-
# * <tt>:logger<tt> -- The UUID generator will use this logger to report the state information (optional).
|
230
|
-
# * <tt>:sequence</tt> -- Specifies the sequence number (0 to 0xFFFF) to use. Required to
|
231
|
-
# create a new state file, ignored if the state file already exists.
|
232
|
-
# * <tt>:mac_addr</tt> -- Specifies the MAC address (xx-xx-xx-xx-xx) to use. Required to
|
233
|
-
# create a new state file, ignored if the state file already exists.
|
234
|
-
#
|
235
|
-
# For example, to create a new state file:
|
236
|
-
# UUID.config :state_file=>'my-uuid.state', :sequence=>rand(0x10000), :mac_addr=>'0C-0E-35-41-60-65'
|
237
|
-
# To use an existing state file and log to +STDOUT+:
|
238
|
-
# UUID.config :state_file=>'my-uuid.state', :logger=>Logger.new(STDOUT)
|
239
|
-
#
|
240
|
-
# :call-seq:
|
241
|
-
# UUID.config(config)
|
242
|
-
#
|
243
|
-
def self.config(options)
|
244
|
-
options ||= {}
|
245
|
-
@@mutex.synchronize do
|
246
|
-
@@logger = options[:logger]
|
247
|
-
next_sequence options
|
248
184
|
end
|
249
|
-
end
|
250
|
-
|
251
185
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
if File.exist? file
|
260
|
-
puts "#{PACKAGE}: Found an existing UUID state file: #{file}"
|
261
|
-
else
|
262
|
-
puts "#{PACKAGE}: No UUID state file found, attempting to create one for you:"
|
263
|
-
# Run ifconfig for UNIX, or ipconfig for Windows.
|
264
|
-
config = ""
|
265
|
-
Kernel.open "|ifconfig" do |input|
|
266
|
-
input.each_line { |line| config << line }
|
267
|
-
end rescue nil
|
268
|
-
Kernel.open "|ipconfig /all" do |input|
|
269
|
-
input.each_line { |line| config << line }
|
270
|
-
end rescue nil
|
271
|
-
|
272
|
-
addresses = config.scan(IFCONFIG_PATTERN).collect { |addr| addr[1..-2] }
|
273
|
-
if addresses.empty?
|
274
|
-
puts "Could not find any IEEE 802 NIC MAC addresses for this machine."
|
275
|
-
puts "You need to create the uuid.state file manually."
|
186
|
+
# Create a uuid.state file by finding the IEEE 802 NIC MAC address for this machine.
|
187
|
+
# Works for UNIX (ifconfig) and Windows (ipconfig). Creates the uuid.state file in the
|
188
|
+
# current directory.
|
189
|
+
def setup()
|
190
|
+
file = File.expand_path(File.join(Dir.pwd, STATE_FILE))
|
191
|
+
if File.exist? file
|
192
|
+
puts "UUID: Found an existing UUID state file: #{file}"
|
276
193
|
else
|
277
|
-
puts "
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
194
|
+
puts "UUID: No UUID state file found, attempting to create one for you:"
|
195
|
+
# Run ifconfig for UNIX, or ipconfig for Windows.
|
196
|
+
config = ""
|
197
|
+
Kernel.open("|ifconfig") { |input| input.each_line { |line| config << line } } rescue nil
|
198
|
+
Kernel.open("|ipconfig /all") { |input| input.each_line { |line| config << line } } rescue nil
|
199
|
+
|
200
|
+
addresses = config.scan(IFCONFIG_PATTERN).map { |addr| addr[1..-2] }
|
201
|
+
if addresses.empty?
|
202
|
+
puts "Could not find any IEEE 802 NIC MAC addresses for this machine."
|
203
|
+
puts "You need to create the uuid.state file manually."
|
204
|
+
else
|
205
|
+
puts "Found the following IEEE 802 NIC MAC addresses on your computer:"
|
206
|
+
addresses.each { |addr| puts " #{addr}" }
|
207
|
+
puts "Selecting the first address #{addresses[0]} for use in your UUID state file."
|
208
|
+
File.open file, "w" do |output|
|
209
|
+
output.puts "mac_addr: \"#{addresses[0]}\""
|
210
|
+
output.puts format("sequence: \"0x%04x\"", rand(0x10000))
|
211
|
+
end
|
212
|
+
puts "Created a new UUID state file: #{file}"
|
283
213
|
end
|
284
|
-
puts "Created a new UUID state file: #{file}"
|
285
214
|
end
|
215
|
+
file
|
286
216
|
end
|
287
|
-
file
|
288
|
-
end
|
289
|
-
|
290
217
|
|
291
|
-
private
|
218
|
+
private
|
292
219
|
|
293
|
-
def
|
220
|
+
def next_sequence(config = nil)
|
294
221
|
# If called to advance the sequence number (config is nil), we have a state file that we're able to use.
|
295
222
|
# If called from configuration, use the specified or default state file.
|
296
223
|
state_file = (config && config[:state_file]) || @@state_file
|
@@ -299,9 +226,7 @@ private
|
|
299
226
|
if File.exist?(STATE_FILE)
|
300
227
|
state_file = STATE_FILE
|
301
228
|
else
|
302
|
-
file = File.expand_path(File.
|
303
|
-
file = File.basename(file) == 'lib' ? file = File.join(file, '..', STATE_FILE) : file = File.join(file, STATE_FILE)
|
304
|
-
file = File.expand_path(file)
|
229
|
+
file = File.expand_path(File.join(Dir.pwd, STATE_FILE))
|
305
230
|
state_file = File.exist?(file) ? file : setup
|
306
231
|
end
|
307
232
|
end
|
@@ -314,8 +239,7 @@ private
|
|
314
239
|
# Get the sequence number. Must be a valid 16-bit hexadecimal value.
|
315
240
|
sequence = state['sequence']
|
316
241
|
if sequence
|
317
|
-
raise RuntimeError, format(ERROR_INVALID_SEQUENCE, sequence) unless
|
318
|
-
sequence.is_a?(String) and sequence =~ /[0-9a-fA-F]{4}/
|
242
|
+
raise RuntimeError, format(ERROR_INVALID_SEQUENCE, sequence) unless sequence.is_a?(String) && sequence =~ /[0-9a-fA-F]{4}/
|
319
243
|
sequence = sequence.hex & 0xFFFF
|
320
244
|
else
|
321
245
|
sequence = rand(0x10000)
|
@@ -324,7 +248,7 @@ private
|
|
324
248
|
# a 48-bit value with the higher bit being zero.
|
325
249
|
mac_addr = state['mac_addr']
|
326
250
|
raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless
|
327
|
-
mac_addr.is_a?(String)
|
251
|
+
mac_addr.is_a?(String) && mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
|
328
252
|
mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
|
329
253
|
|
330
254
|
# If everything is OK, proceed to the next step. Grab the sequence number and store
|
@@ -338,7 +262,7 @@ private
|
|
338
262
|
if @@logger
|
339
263
|
@@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
|
340
264
|
else
|
341
|
-
warn "
|
265
|
+
warn "UUID: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
|
342
266
|
end
|
343
267
|
@@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
|
344
268
|
rescue Errno::ENOENT=>error
|
@@ -350,8 +274,8 @@ private
|
|
350
274
|
@@logger.error ERROR_INITIALIZED_RANDOM_1
|
351
275
|
@@logger.error ERROR_INITIALIZED_RANDOM_2
|
352
276
|
else
|
353
|
-
warn "
|
354
|
-
warn "
|
277
|
+
warn "UUID: " + ERROR_INITIALIZED_RANDOM_1
|
278
|
+
warn "UUID: " + ERROR_INITIALIZED_RANDOM_2
|
355
279
|
end
|
356
280
|
@@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
|
357
281
|
else
|
@@ -362,7 +286,7 @@ private
|
|
362
286
|
sequence &= 0xFFFF
|
363
287
|
mac_addr = config[:mac_addr]
|
364
288
|
raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless
|
365
|
-
mac_addr.is_a?(String)
|
289
|
+
mac_addr.is_a?(String) && mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
|
366
290
|
mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
|
367
291
|
File.open state_file, "w" do |file|
|
368
292
|
file.flock(File::LOCK_EX)
|
@@ -375,7 +299,7 @@ private
|
|
375
299
|
if @@logger
|
376
300
|
@@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
|
377
301
|
else
|
378
|
-
warn "
|
302
|
+
warn "UUID: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
|
379
303
|
end
|
380
304
|
@@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
|
381
305
|
end
|
@@ -385,7 +309,7 @@ private
|
|
385
309
|
end
|
386
310
|
end
|
387
311
|
|
388
|
-
def
|
312
|
+
def dump(file, plus_one)
|
389
313
|
# Gets around YAML weirdness, like ont storing the MAC address as a string.
|
390
314
|
if @@sequence && @@mac_addr
|
391
315
|
file.puts "mac_addr: \"#{@@mac_addr}\""
|
@@ -393,21 +317,79 @@ private
|
|
393
317
|
file.puts "last_clock: \"0x%x\"" % (@@last_clock || (Time.new.to_f * CLOCK_MULTIPLIER).to_i)
|
394
318
|
end
|
395
319
|
end
|
320
|
+
end
|
396
321
|
|
397
|
-
|
398
|
-
|
322
|
+
@@mutex = Mutex.new
|
323
|
+
@@last_clock = nil
|
324
|
+
@@logger = nil
|
325
|
+
@@state_file = nil
|
399
326
|
|
400
|
-
|
401
|
-
|
327
|
+
# Generates and returns a new UUID string.
|
328
|
+
#
|
329
|
+
# The argument +format+ specifies which formatting template to use:
|
330
|
+
# * <tt>:default</tt> -- Produces 36 characters, including hyphens separating the UUID value parts
|
331
|
+
# * <tt>:compact</tt> -- Produces a 32 digits (hexadecimal) value with no hyphens
|
332
|
+
# * <tt>:urn</tt> -- Aadds the prefix <tt>urn:uuid:</tt> to the <tt>:default</tt> format
|
333
|
+
#
|
334
|
+
# For example:
|
335
|
+
# print UUID.new :default
|
336
|
+
# or just
|
337
|
+
# print UUID.new
|
338
|
+
#
|
339
|
+
# :call-seq:
|
340
|
+
# UUID.new([format]) -> string
|
341
|
+
#
|
342
|
+
def new(format = nil)
|
343
|
+
# Determine which format we're using for the UUID string.
|
344
|
+
template = FORMATS[format || :default] or raise RuntimeError, "I don't know the format '#{format}'"
|
402
345
|
|
403
|
-
|
404
|
-
|
346
|
+
# The clock must be monotonically increasing. The clock resolution is at best 100 ns
|
347
|
+
# (UUID spec), but practically may be lower (on my setup, around 1ms). If this method
|
348
|
+
# is called too fast, we don't have a monotonically increasing clock, so the solution is
|
349
|
+
# to just wait.
|
350
|
+
# It is possible for the clock to be adjusted backwards, in which case we would end up
|
351
|
+
# blocking for a long time. When backward clock is detected, we prevent duplicates by
|
352
|
+
# asking for a new sequence number and continue with the new clock.
|
353
|
+
clock = @@mutex.synchronize do
|
354
|
+
# Initialize UUID generator if not already initialized. Uninitizlied UUID generator has no
|
355
|
+
# last known clock.
|
356
|
+
next_sequence unless @@last_clock
|
357
|
+
clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
|
358
|
+
if clock > @@last_clock
|
359
|
+
@@drift = 0
|
360
|
+
@@last_clock = clock
|
361
|
+
elsif clock = @@last_clock
|
362
|
+
drift = @@drift += 1
|
363
|
+
if drift < 10000
|
364
|
+
@@last_clock += 1
|
365
|
+
else
|
366
|
+
Thread.pass
|
367
|
+
nil
|
368
|
+
end
|
369
|
+
else
|
370
|
+
next_sequence
|
371
|
+
@@last_clock = clock
|
405
372
|
end
|
406
|
-
|
373
|
+
end while not clock
|
374
|
+
sprintf template, clock & 0xFFFFFFFF, (clock >> 32)& 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
|
375
|
+
@@sequence & 0xFFFF, @@mac_hex & 0xFFFFFFFFFFFF
|
407
376
|
end
|
377
|
+
|
378
|
+
alias uuid new
|
379
|
+
module_function :uuid, :new
|
380
|
+
|
408
381
|
end
|
409
382
|
|
410
383
|
|
384
|
+
class ActiveRecord::Base
|
385
|
+
class << self
|
386
|
+
def uuid_primary_key()
|
387
|
+
before_create { |record| record.id = UUID.new unless record.id }
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end if defined?(ActiveRecord)
|
391
|
+
|
392
|
+
|
411
393
|
if __FILE__ == $0
|
412
394
|
UUID.setup
|
413
395
|
end
|
data/test/test-uuid.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# Author:: Assaf Arkin assaf@labnotes.org
|
5
5
|
# Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
|
6
|
-
# Copyright:: Copyright (c) 2005 Assaf Arkin
|
6
|
+
# Copyright:: Copyright (c) 2005,2007 Assaf Arkin
|
7
7
|
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
8
8
|
#
|
9
9
|
#--
|
@@ -25,10 +25,14 @@ class TestUUID < Test::Unit::TestCase
|
|
25
25
|
10.times do
|
26
26
|
uuid = UUID.new :compact
|
27
27
|
assert uuid =~ /^[0-9a-fA-F]{32}$/, "UUID does not conform to :compact format"
|
28
|
+
assert uuid =~ UUID::REGEXP_COMPACT, "UUID does not conform to :compact format"
|
28
29
|
uuid = UUID.new :default
|
29
30
|
assert uuid =~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, "UUID does not conform to :default format"
|
31
|
+
assert uuid =~ UUID::REGEXP, "UUID does not conform to :compact format"
|
32
|
+
assert uuid =~ UUID::REGEXP_FULL, "UUID does not conform to :compact format"
|
30
33
|
uuid = UUID.new :urn
|
31
34
|
assert uuid =~ /^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i, "UUID does not conform to :urn format"
|
35
|
+
assert uuid =~ UUID::REGEXP, "UUID does not conform to :compact format"
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: uuid
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0.
|
7
|
-
date:
|
6
|
+
version: 1.0.4
|
7
|
+
date: 2007-08-28 00:00:00 -07:00
|
8
8
|
summary: UUID generator
|
9
9
|
require_paths:
|
10
10
|
- lib
|