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 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