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 CHANGED
@@ -1,20 +1,19 @@
1
- Release 1.0.3 (Nov 8, 2006)
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
- Release 1.0.2 (Jul 28, 2006)
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
- Release 1.0.1 (Jul 26, 2006)
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
- Release 1.0.0 (Nov 20, 2005)
18
-
18
+ 1.0.0 (1/20/2005)
19
19
  * Changed: Separated form reliable-msg into its own package.
20
-
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005,2006 Assaf Arkin
1
+ Copyright (c) 2005,2007 Assaf Arkin
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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
- test.verbose = true
17
- test.test_files = ["test/test-uuid.rb"]
18
- #test.warning = true
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
- rdoc.main = "README"
25
- rdoc.rdoc_files.include("README", "lib/**/*.rb")
26
- rdoc.title = "UUID generator"
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
@@ -2,6 +2,6 @@ begin
2
2
  require 'uuid'
3
3
  rescue LoadError
4
4
  require 'rubygems'
5
- require_gem 'uuid'
5
+ require 'uuid'
6
6
  end
7
7
  UUID::setup
@@ -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,2006 Assaf Arkin
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, or in the installation 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
- unless const_defined?(:VERSION)
116
- VERSION = '1.0.3'
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
- # Default state file.
121
- STATE_FILE = "uuid.state"
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
- # Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
124
- CLOCK_MULTIPLIER = 10000000 #:nodoc:
120
+ # Regular expression to identify a 32 character UUID, no hyphens (compact) full match.
121
+ REGEXP_COMPACT = /^[[:xdigit:]]{32}$/
125
122
 
126
- # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
127
- CLOCK_GAPS = 100000 #:nodoc:
123
+ # Default state file.
124
+ STATE_FILE = 'uuid.state'
128
125
 
129
- # Version number stamped into the UUID to identify it as time-based.
130
- VERSION_CLOCK = 0x0100 #:nodoc:
126
+ # Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
127
+ CLOCK_MULTIPLIER = 10000000 #:nodoc:
131
128
 
132
- # Formats supported by the UUID generator.
133
- FORMATS = {:compact=>"%08x%04x%04x%04x%012x", :default=>"%08x-%04x-%04x-%04x-%012x", :urn=>"urn:uuid:%08x-%04x-%04x-%04x-%012x"} #:nodoc:
129
+ # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
130
+ CLOCK_GAPS = 100000 #:nodoc:
134
131
 
135
- # Length (in characters) of UUIDs generated for each of the formats.
136
- FORMATS_LENGTHS = {:compact=>32, :default=>36, :urn=>45} #:nodoc:
132
+ # Version number stamped into the UUID to identify it as time-based.
133
+ VERSION_CLOCK = 0x0100 #:nodoc:
137
134
 
138
- ERROR_INVALID_SEQUENCE = "Invalid sequence number: found '%s', expected 4 hexdecimal digits" #:nodoc:
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
- ERROR_NOT_A_SEQUENCE = "Not a sequence number: expected integer between 0 and 0xFFFF" #:nodoc:
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
- ERROR_INVALID_MAC_ADDR = "Invalid MAC address: found '%s', expected a number in the format XX-XX-XX-XX-XX-XX" #:nodoc:
143
+ ERROR_INVALID_SEQUENCE = "Invalid sequence number: found '%s', expected 4 hexdecimal digits" #:nodoc:
143
144
 
144
- INFO_INITIALIZED = "Initialized UUID generator with sequence number 0x%04x and MAC address %s" #:nodoc:
145
+ ERROR_NOT_A_SEQUENCE = "Not a sequence number: expected integer between 0 and 0xFFFF" #:nodoc:
145
146
 
146
- ERROR_INITIALIZED_RANDOM_1 = "Initialized UUID generator with random sequence number/MAC address." #:nodoc:
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
- ERROR_INITIALIZED_RANDOM_2 = "UUIDs are not guaranteed to be unique. Please create a uuid.state file as soon as possible." #:nodoc:
149
+ INFO_INITIALIZED = "Initialized UUID generator with sequence number 0x%04x and MAC address %s" #:nodoc:
149
150
 
150
- IFCONFIG_PATTERN = /[^:\-](?:[0-9A-Za-z][0-9A-Za-z][:\-]){5}[0-9A-Za-z][0-9A-Za-z][^:\-]/ #:nodoc:
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
- # Regular expression to identify a 36 character UUID. Can be used for a partial match.
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
- @@mutex = Mutex.new
162
- @@last_clock = nil
163
- @@logger = nil
164
- @@state_file = nil
165
-
166
-
167
- # Generates and returns a new UUID string.
168
- #
169
- # The argument +format+ specifies which formatting template to use:
170
- # * <tt>:default</tt> -- Produces 36 characters, including hyphens separating the UUID value parts
171
- # * <tt>:compact</tt> -- Produces a 32 digits (hexadecimal) value with no hyphens
172
- # * <tt>:urn</tt> -- Aadds the prefix <tt>urn:uuid:</tt> to the <tt>:default</tt> format
173
- #
174
- # For example:
175
- # print UUID.new :default
176
- # or just
177
- # print UUID.new
178
- #
179
- # :call-seq:
180
- # UUID.new([format]) -> string
181
- #
182
- def new(format = nil)
183
- # Determine which format we're using for the UUID string.
184
- template = FORMATS[format || :default] or
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
- # Create a uuid.state file by finding the IEEE 802 NIC MAC address for this machine.
253
- # Works for UNIX (ifconfig) and Windows (ipconfig). Creates the uuid.state file in the
254
- # installation directory (typically the GEM's lib).
255
- def self.setup()
256
- file = File.expand_path(File.dirname(__FILE__))
257
- file = File.basename(file) == 'lib' ? file = File.join(file, '..', STATE_FILE) : file = File.join(file, STATE_FILE)
258
- file = File.expand_path(file)
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 "Found the following IEEE 802 NIC MAC addresses on your computer:"
278
- addresses.each { |addr| puts " #{addr}" }
279
- puts "Selecting the first address #{addresses[0]} for use in your UUID state file."
280
- File.open file, "w" do |output|
281
- output.puts "mac_addr: \"#{addresses[0]}\""
282
- output.puts format("sequence: \"0x%04x\"", rand(0x10000))
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 self.next_sequence(config = nil)
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.dirname(__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) and mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
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 "#{PACKAGE}: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
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 "#{PACKAGE}: " + ERROR_INITIALIZED_RANDOM_1
354
- warn "#{PACKAGE}: " + ERROR_INITIALIZED_RANDOM_2
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) and mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
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 "#{PACKAGE}: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
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 self.dump(file, plus_one)
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
- end
398
-
322
+ @@mutex = Mutex.new
323
+ @@last_clock = nil
324
+ @@logger = nil
325
+ @@state_file = nil
399
326
 
400
- if defined?(ActiveRecord)
401
- class ActiveRecord::Base
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
- def self.uuid_primary_key()
404
- before_create { |record| record.id = UUID.new unless record.id }
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
@@ -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.0
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.3
7
- date: 2006-11-08 00:00:00 -08:00
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