uuid 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|