uuid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005 Assaf Arkin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,81 @@
1
+ = UUID Generator
2
+
3
+ Generates universally unique identifiers (UUIDs) for use in distributed
4
+ applications. Based on {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt].
5
+
6
+
7
+ == Download
8
+
9
+ The latest version of the UUID generator can be found in the Reliable
10
+ Messaging project
11
+
12
+ * http://rubyforge.org/projects/reliable-msg/
13
+
14
+ For more information
15
+
16
+ * http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
17
+
18
+
19
+ == Installation
20
+
21
+ You can download the sources directly, or install the GEM package
22
+ (recommended) with
23
+
24
+ gem install uuid
25
+
26
+ Once installed, create a <tt>uuid.state</tt> file by running
27
+
28
+ uuid-setup
29
+
30
+
31
+ == Example
32
+
33
+ require_gem 'uuid'
34
+
35
+ 10.times do
36
+ p UUID.new
37
+ end
38
+
39
+
40
+ == UUID state file
41
+
42
+ The UUID generator uses a state file (<tt>uuid.state</tt>) to hold the
43
+ MAC address and sequence number.
44
+
45
+ The MAC address is used to generate identifiers that are unique to your
46
+ machine, preventing conflicts in distributed applications. The MAC
47
+ address is six bytes (48 bit) long. It is automatically extracted from
48
+ one of the network cards on your machine by running +ipconfig+
49
+ (Windows) or +ifconfig+ (Linux, UNIX, et al). You can override the MAC
50
+ address by editing the <tt>uuid.state</tt> file yourself.
51
+
52
+ The sequence number is incremented each time the UUID generator is
53
+ first used by the application, to prevent multiple processes from
54
+ generating the same set of identifiers, and deal with changes to the
55
+ system clock.
56
+
57
+ The UUID state file is created with
58
+
59
+ uuid-setup
60
+
61
+ It is created in the installation directory. If you installed the UUID
62
+ generator as a Gem, it will be created in the Gem's directory. However,
63
+ the UUID generator will look for the <tt>uuid.state</tt> first in the
64
+ local directory, and then in the installation directory. You can use
65
+ that to maintain separate state files for different applications.
66
+ However, make sure they generate unique identifiers by using different
67
+ MAC addresses in each state file.
68
+
69
+
70
+ == Change log
71
+
72
+ :include: changelog.txt
73
+
74
+
75
+ == License
76
+
77
+ This package is licensed under the MIT license and/or the {Creative
78
+ Commons Attribution-ShareAlike}[http://creativecommons.org/licenses/by-sa/2.5/legalcode].
79
+
80
+ :include: MIT-LICENSE
81
+
@@ -0,0 +1,149 @@
1
+ # Adapted from the rake Rakefile.
2
+
3
+ require "rubygems"
4
+ Gem::manage_gems
5
+ require "rake/testtask"
6
+ require "rake/rdoctask"
7
+ require "rake/gempackagetask"
8
+
9
+
10
+ desc "Default Task"
11
+ task :default => [:test, :rdoc]
12
+
13
+
14
+ desc "Run all test cases"
15
+ Rake::TestTask.new do |test|
16
+ test.verbose = true
17
+ test.test_files = ["test/test-uuid.rb"]
18
+ #test.warning = true
19
+ end
20
+
21
+
22
+ # Create the documentation.
23
+ Rake::RDocTask.new do |rdoc|
24
+ rdoc.main = "README"
25
+ rdoc.rdoc_files.include("README", "lib/**/*.rb")
26
+ rdoc.title = "UUID generator"
27
+ end
28
+
29
+ # Handle version number.
30
+ class Version
31
+
32
+ PATTERN = /(\s*)VERSION.*(\d+\.\d+\.\d+)/
33
+
34
+ def initialize file, new_version
35
+ @file = file
36
+ @version = File.open @file, "r" do |file|
37
+ version = nil
38
+ file.each_line do |line|
39
+ match = line.match PATTERN
40
+ if match
41
+ version = match[2]
42
+ break
43
+ end
44
+ end
45
+ version
46
+ end
47
+ fail "Can't determine version number" unless @version
48
+ @new_version = new_version || @version
49
+ end
50
+
51
+ def changed?
52
+ @version != @new_version
53
+ end
54
+
55
+ def number
56
+ @version
57
+ end
58
+
59
+ def next
60
+ @new_version
61
+ end
62
+
63
+ def update
64
+ puts "Updating to version #{@new_version}"
65
+ copy = "#{@file}.new"
66
+ open @file, "r" do |input|
67
+ open copy, "w" do |output|
68
+ input.each_line do |line|
69
+ match = line.match PATTERN
70
+ if match
71
+ output.puts "#{match[1]}VERSION = '#{@new_version}'"
72
+ else
73
+ output.puts line
74
+ end
75
+ end
76
+ end
77
+ end
78
+ mv copy, @file
79
+ @version = @new_version
80
+ end
81
+
82
+ end
83
+ version = Version.new "lib/uuid.rb", ENV["version"]
84
+
85
+
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
+
99
+ spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile", "changelog.txt"].to_a
100
+ spec.require_path = "lib"
101
+ spec.autorequire = "uuid.rb"
102
+ spec.bindir = "bin"
103
+ spec.executables = ["uuid-setup"]
104
+ spec.default_executable = "uuid-setup"
105
+ spec.has_rdoc = true
106
+ spec.rdoc_options << "--main" << "README" << "--title" << "UUID generator" << "--line-numbers"
107
+ spec.extra_rdoc_files = ["README"]
108
+ spec.rubyforge_project = "reliable-msg"
109
+ end
110
+
111
+ gem = Rake::GemPackageTask.new(gem_spec) do |pkg|
112
+ pkg.need_tar = true
113
+ pkg.need_zip = true
114
+ end
115
+
116
+
117
+ desc "Look for TODO and FIXME tags in the code"
118
+ task :todo do
119
+ FileList["**/*.rb"].egrep /#.*(FIXME|TODO|TBD)/
120
+ end
121
+
122
+
123
+ # --------------------------------------------------------------------
124
+ # Creating a release
125
+
126
+ desc "Make a new release"
127
+ task :release => [:test, :prerelease, :clobber, :update_version, :package] do
128
+ puts
129
+ puts "**************************************************************"
130
+ puts "* Release #{version.number} Complete."
131
+ puts "* Packages ready to upload."
132
+ puts "**************************************************************"
133
+ puts
134
+ end
135
+
136
+ task :prerelease do
137
+ if !version.changed? && ENV["reuse"] != version.number
138
+ fail "Current version is #{version.number}, must specify reuse=ver to reuse existing version"
139
+ end
140
+ end
141
+
142
+ task :update_version => [:prerelease] do
143
+ if !version.changed?
144
+ puts "No version change ... skipping version update"
145
+ else
146
+ version.update
147
+ end
148
+ end
149
+
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'uuid'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require_gem 'uuid'
6
+ end
7
+ UUID::setup
8
+
@@ -0,0 +1,4 @@
1
+ Release 1.0.0 (Nov 20, 2005)
2
+
3
+ * Changed: Separated form reliable-msg into its own package.
4
+
@@ -0,0 +1,386 @@
1
+ #
2
+ # = uuid.rb - UUID generator
3
+ #
4
+ # Author:: Assaf Arkin assaf@labnotes.org
5
+ # Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
6
+ # Copyright:: Copyright (c) 2005 Assaf Arkin
7
+ # License:: MIT and/or Creative Commons Attribution-ShareAlike
8
+ #
9
+ #--
10
+ #++
11
+
12
+
13
+ require 'thread'
14
+ require 'yaml'
15
+ require 'singleton'
16
+ require 'logger'
17
+
18
+
19
+
20
+ # == Generating UUIDs
21
+ #
22
+ # Call UUID.new to generate and return a new UUID. The method returns a string in one of three
23
+ # formats. The default format is 36 characters long, and contains the 32 hexadecimal octets and
24
+ # hyphens separating the various value parts. The <tt>:compact</tt> format omits the hyphens,
25
+ # while the <tt>:urn</tt> format adds the <tt>:urn:uuid</tt> prefix.
26
+ #
27
+ # For example:
28
+ # 10.times do
29
+ # p UUID.new
30
+ # end
31
+ #
32
+ # ---
33
+ # == UUIDs in Brief
34
+ #
35
+ # UUID (universally unique identifier) are guaranteed to be unique across time and space.
36
+ #
37
+ # A UUID is 128 bit long, and consists of a 60-bit time value, a 16-bit sequence number and
38
+ # a 48-bit node identifier.
39
+ #
40
+ # The time value is taken from the system clock, and is monotonically incrementing. However,
41
+ # since it is possible to set the system clock backward, a sequence number is added. The
42
+ # sequence number is incremented each time the UUID generator is started. The combination
43
+ # guarantees that identifiers created on the same machine are unique with a high degree of
44
+ # probability.
45
+ #
46
+ # Note that due to the structure of the UUID and the use of sequence number, there is no
47
+ # guarantee that UUID values themselves are monotonically incrementing. The UUID value
48
+ # cannot itself be used to sort based on order of creation.
49
+ #
50
+ # To guarantee that UUIDs are unique across all machines in the network, use the IEEE 802
51
+ # MAC address of the machine's network interface card as the node identifier. Network interface
52
+ # cards have unique MAC address that are 47-bit long (the last bit acts as a broadcast flag).
53
+ # Use +ipconfig+ (Windows), or +ifconfig+ (Unix) to find the MAC address (aka physical address)
54
+ # of a network card. It takes the form of six pairs of hexadecimal digits, separated by hypen or
55
+ # colon, e.g. '<tt>08-0E-46-21-4B-35</tt>'
56
+ #
57
+ # For more information see {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt].
58
+ #
59
+ # == Configuring the UUID generator
60
+ #
61
+ # The UUID generator requires a state file which maintains the MAC address and next sequence
62
+ # number to use. By default, the UUID generator will use the file <tt>uuid.state</tt> contained
63
+ # in the current directory, or in the installation directory.
64
+ #
65
+ # Use UUID.config to specify a different location for the UUID state file. If the UUID state file
66
+ # does not exist, you can create one manually, or use UUID.config with the options <tt>:sequence</tt>
67
+ # and <tt>:mac_addr</tt>.
68
+ #
69
+ # A UUID state file looks like:
70
+ # ---
71
+ # last_clock: "0x28227f76122d80"
72
+ # mac_addr: 08-0E-46-21-4B-35
73
+ # sequence: "0x1639"
74
+ #
75
+ #
76
+ #--
77
+ # === Time-based UUID
78
+ #
79
+ # The UUID specification prescribes the following format for representing UUIDs. Four octets encode
80
+ # the low field of the time stamp, two octects encode the middle field of the timestamp, and two
81
+ # octets encode the high field of the timestamp with the version number. Two octets encode the
82
+ # clock sequence number and six octets encode the unique node identifier.
83
+ #
84
+ # The timestamp is a 60 bit value holding UTC time as a count of 100 nanosecond intervals since
85
+ # October 15, 1582. UUIDs generated in this manner are guaranteed not to roll over until 3400 AD.
86
+ #
87
+ # The clock sequence is used to help avoid duplicates that could arise when the clock is set backward
88
+ # in time or if the node ID changes. Although the system clock is guaranteed to be monotonic, the
89
+ # system clock is not guaranteed to be monotonic across system failures. The UUID cannot be sure
90
+ # that no UUIDs were generated with timestamps larger than the current timestamp.
91
+ #
92
+ # If the clock sequence can be determined at initialization, it is incremented by one. The clock sequence
93
+ # MUST be originally (i.e. once in the lifetime of a system) initialized to a random number to minimize the
94
+ # correlation across systems. The initial value must not be correlated to the node identifier.
95
+ #
96
+ # The node identifier must be unique for each UUID generator. This is accomplished using the IEEE 802
97
+ # network card address. For systems with multiple IEEE 802 addresses, any available address can be used.
98
+ # For systems with no IEEE address, a 47 bit random value is used and the multicast bit is set so it will
99
+ # never conflict with addresses obtained from network cards.
100
+ #
101
+ # === UUID state file
102
+ #
103
+ # The UUID state is contained in the UUID state file. The file name can be specified when configuring
104
+ # the UUID generator with UUID.config. The default is to use the file +uuid.state+ in the current directory,
105
+ # or the installation directory.
106
+ #
107
+ # The UUID state file is read once when the UUID generator is first used (or configured). The sequence
108
+ # number contained in the UUID is read and used, and the state file is updated to the next sequence
109
+ # number. The MAC address is also read from the state file. The current clock time (in 100ns resolution)
110
+ # is stored in the state file whenever the sequence number is updated, but is never read.
111
+ #
112
+ # If the UUID generator detects that the system clock has been moved backwards, it will obtain a new
113
+ # sequence in the same manner. So it is possible that the UUID state file will be updated while the
114
+ # application is running.
115
+ #++
116
+ module UUID
117
+
118
+ VERSION = '1.0.0'
119
+
120
+ PACKAGE = "uuid"
121
+
122
+ # Default state file.
123
+ STATE_FILE = "uuid.state"
124
+
125
+ # Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
126
+ CLOCK_MULTIPLIER = 10000000 #:nodoc:
127
+
128
+ # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
129
+ CLOCK_GAPS = 100000 #:nodoc:
130
+
131
+ # Version number stamped into the UUID to identify it as time-based.
132
+ VERSION_CLOCK = 0x0100 #:nodoc:
133
+
134
+ # Formats supported by the UUID generator.
135
+ FORMATS = {:compact=>"%08x%04x%04x%04x%012x", :default=>"%08x-%04x-%04x-%04x-%012x", :urn=>"urn:uuid:%08x-%04x-%04x-%04x-%012x"} #:nodoc:
136
+
137
+ # Length (in characters) of UUIDs generated for each of the formats.
138
+ FORMATS_LENGTHS = {:compact=>32, :default=>36, :urn=>45} #:nodoc:
139
+
140
+ ERROR_INVALID_SEQUENCE = "Invalid sequence number: found '%s', expected 4 hexdecimal digits" #:nodoc:
141
+
142
+ ERROR_NOT_A_SEQUENCE = "Not a sequence number: expected integer between 0 and 0xFFFF" #:nodoc:
143
+
144
+ ERROR_INVALID_MAC_ADDR = "Invalid MAC address: found '%s', expected a number in the format XX-XX-XX-XX-XX-XX" #:nodoc:
145
+
146
+ INFO_INITIALIZED = "Initialized UUID generator with sequence number 0x%04x and MAC address %s" #:nodoc:
147
+
148
+ ERROR_INITIALIZED_RANDOM_1 = "Initialized UUID generator with random sequence number/MAC address." #:nodoc:
149
+
150
+ ERROR_INITIALIZED_RANDOM_2 = "UUIDs are not guaranteed to be unique. Please create a uuid.state file as soon as possible." #:nodoc:
151
+
152
+ IFCONFIG_PATTERN = /[^:\-](?:[0-9A-Za-z][0-9A-Za-z][:\-]){5}[0-9A-Za-z][0-9A-Za-z][^:\-]/ #:nodoc:
153
+
154
+ @@mutex = Mutex.new
155
+ @@last_clock = @@logger = @@state_file = nil
156
+
157
+ # Generates and returns a new UUID string.
158
+ #
159
+ # The argument +format+ specifies which formatting template to use:
160
+ # * <tt>:default</tt> -- Produces 36 characters, including hyphens separating the UUID value parts
161
+ # * <tt>:compact</tt> -- Produces a 32 digits (hexadecimal) value with no hyphens
162
+ # * <tt>:urn</tt> -- Aadds the prefix <tt>urn:uuid:</tt> to the <tt>:default</tt> format
163
+ #
164
+ # For example:
165
+ # print UUID.new :default
166
+ # or just
167
+ # print UUID.new
168
+ #
169
+ # :call-seq:
170
+ # UUID.new([format]) -> string
171
+ #
172
+ def new format = nil
173
+ # Determine which format we're using for the UUID string.
174
+ template = FORMATS[format || :default] or
175
+ raise RuntimeError, "I don't know the format '#{format}'"
176
+
177
+ # The clock must be monotonically increasing. The clock resolution is at best 100 ns
178
+ # (UUID spec), but practically may be lower (on my setup, around 1ms). If this method
179
+ # is called too fast, we don't have a monotonically increasing clock, so the solution is
180
+ # to just wait.
181
+ # It is possible for the clock to be adjusted backwards, in which case we would end up
182
+ # blocking for a long time. When backward clock is detected, we prevent duplicates by
183
+ # asking for a new sequence number and continue with the new clock.
184
+ clock = @@mutex.synchronize do
185
+ # Initialize UUID generator if not already initialized. Uninitizlied UUID generator has no
186
+ # last known clock.
187
+ next_sequence unless @@last_clock
188
+ clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
189
+ if clock > @@last_clock
190
+ @@drift = 0
191
+ @@last_clock = clock
192
+ elsif clock = @@last_clock
193
+ drift = @@drift += 1
194
+ if drift < 10000
195
+ @@last_clock += 1
196
+ else
197
+ Thread.pass
198
+ nil
199
+ end
200
+ else
201
+ next_sequence
202
+ @@last_clock = clock
203
+ end
204
+ end while not clock
205
+ sprintf template, clock & 0xFFFFFFFF, (clock >> 32)& 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
206
+ @@sequence & 0xFFFF, @@mac_hex & 0xFFFFFFFFFFFF
207
+ end
208
+
209
+ alias uuid new
210
+ module_function :uuid, :new
211
+
212
+ # Configures the UUID generator. Use this method to specify the UUID state file, logger, etc.
213
+ #
214
+ # The method accepts the following options:
215
+ # * <tt>:state_file</tt> -- Specifies the location of the state file. If missing, the default
216
+ # is <tt>uuid.state</tt>
217
+ # * <tt>:logger<tt> -- The UUID generator will use this logger to report the state information (optional).
218
+ # * <tt>:sequence</tt> -- Specifies the sequence number (0 to 0xFFFF) to use. Required to
219
+ # create a new state file, ignored if the state file already exists.
220
+ # * <tt>:mac_addr</tt> -- Specifies the MAC address (xx-xx-xx-xx-xx) to use. Required to
221
+ # create a new state file, ignored if the state file already exists.
222
+ #
223
+ # For example, to create a new state file:
224
+ # UUID.config :state_file=>'my-uuid.state', :sequence=>rand(0x10000), :mac_addr=>'0C-0E-35-41-60-65'
225
+ # To use an existing state file and log to +STDOUT+:
226
+ # UUID.config :state_file=>'my-uuid.state', :logger=>Logger.new(STDOUT)
227
+ #
228
+ # :call-seq:
229
+ # UUID.config(config)
230
+ #
231
+ def self.config options
232
+ options ||= {}
233
+ @@mutex.synchronize do
234
+ @@logger = options[:logger]
235
+ next_sequence options
236
+ end
237
+ end
238
+
239
+ # Create a uuid.state file by finding the IEEE 802 NIC MAC address for this machine.
240
+ # Works for UNIX (ifconfig) and Windows (ipconfig). Creates the uuid.state file in the
241
+ # installation directory (typically the GEM's lib).
242
+ def self.setup
243
+ file = File.expand_path(File.dirname(__FILE__))
244
+ file = File.basename(file) == 'lib' ? file = File.join(file, '..', STATE_FILE) : file = File.join(file, STATE_FILE)
245
+ file = File.expand_path(file)
246
+ if File.exist? file
247
+ puts "#{PACKAGE}: Found an existing UUID state file: #{file}"
248
+ else
249
+ puts "#{PACKAGE}: No UUID state file found, attempting to create one for you:"
250
+ # Run ifconfig for UNIX, or ipconfig for Windows.
251
+ config = ""
252
+ begin
253
+ Kernel.open "|ifconfig" do |input|
254
+ input.each_line { |line| config << line }
255
+ end
256
+ rescue
257
+ end
258
+ begin
259
+ Kernel.open "|ipconfig /all" do |input|
260
+ input.each_line { |line| config << line }
261
+ end
262
+ rescue
263
+ end
264
+
265
+ addresses = config.scan(IFCONFIG_PATTERN).collect { |addr| addr[1..-2] }
266
+ if addresses.empty?
267
+ puts "Could not find any IEEE 802 NIC MAC addresses for this machine."
268
+ puts "You need to create the uuid.state file manually."
269
+ else
270
+ puts "Found the following IEEE 802 NIC MAC addresses on your computer:"
271
+ addresses.each { |addr| puts " #{addr}" }
272
+ puts "Selecting the first address #{addresses[0]} for use in your UUID state file."
273
+ File.open file, "w" do |output|
274
+ output.puts "mac_addr: #{addresses[0]}"
275
+ output.puts format("sequence: \"0x%04x\"", rand(0x10000))
276
+ end
277
+ puts "Created a new UUID state file: #{file}"
278
+ end
279
+ end
280
+ file
281
+ end
282
+
283
+ private
284
+ def self.state plus_one = false
285
+ return nil unless @@sequence && @@mac_addr
286
+ {"sequence"=>sprintf("0x%04x", (plus_one ? @@sequence + 1 : @@sequence) & 0xFFFF), "last_clock"=>sprintf("0x%x", @@last_clock || (Time.new.to_f * CLOCK_MULTIPLIER).to_i), "mac_addr" => @@mac_addr}
287
+ end
288
+
289
+ def self.next_sequence config = nil
290
+ # If called to advance the sequence number (config is nil), we have a state file that we're able to use.
291
+ # If called from configuration, use the specified or default state file.
292
+ state_file = (config && config[:state_file]) || @@state_file
293
+
294
+ unless state_file
295
+ state_file = if File.exist?(STATE_FILE)
296
+ STATE_FILE
297
+ else
298
+ file = File.expand_path(File.dirname(__FILE__))
299
+ file = File.basename(file) == 'lib' ? file = File.join(file, '..', STATE_FILE) : file = File.join(file, STATE_FILE)
300
+ file = File.expand_path(file)
301
+ File.exist?(file) ? file : setup
302
+ end
303
+ end
304
+ begin
305
+ File.open state_file, "r+" do |file|
306
+ # Lock the file for exclusive access, just to make sure it's not being read while we're
307
+ # updating its contents.
308
+ file.flock(File::LOCK_EX)
309
+ state = YAML::load file
310
+ # Get the sequence number. Must be a valid 16-bit hexadecimal value.
311
+ sequence = state['sequence']
312
+ if sequence
313
+ raise RuntimeError, format(ERROR_INVALID_SEQUENCE, sequence) unless sequence.is_a?(String) and sequence =~ /[0-9a-fA-F]{4}/
314
+ sequence = sequence.hex & 0xFFFF
315
+ else
316
+ sequence = rand(0x10000)
317
+ end
318
+ # Get the MAC address. Must be 6 pairs of hexadecimal octets. Convert MAC address into
319
+ # a 48-bit value with the higher bit being zero.
320
+ mac_addr = state['mac_addr']
321
+ raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless mac_addr.is_a?(String) and mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
322
+ mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
323
+
324
+ # If everything is OK, proceed to the next step. Grab the sequence number and store
325
+ # the new state. Start at beginning of file, and truncate file when done.
326
+ @@mac_addr, @@mac_hex, @@sequence, @@state_file = mac_addr, mac_hex, sequence, state_file
327
+ file.pos = 0
328
+ YAML::dump state(true), file
329
+ file.truncate file.pos
330
+ end
331
+ # Initialized.
332
+ if @@logger
333
+ @@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
334
+ else
335
+ warn "#{PACKAGE}: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
336
+ end
337
+ @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
338
+ rescue Errno::ENOENT=>error
339
+ if !config
340
+ # Generate random values.
341
+ @@mac_hex, @@sequence, @@state_file = rand(0x800000000000) | 0xF00000000000, rand(0x10000), nil
342
+ # Initialized.
343
+ if @@logger
344
+ @@logger.error ERROR_INITIALIZED_RANDOM_1
345
+ @@logger.error ERROR_INITIALIZED_RANDOM_2
346
+ else
347
+ warn "#{PACKAGE}: " + ERROR_INITIALIZED_RANDOM_1
348
+ warn "#{PACKAGE}: " + ERROR_INITIALIZED_RANDOM_2
349
+ end
350
+ @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
351
+ else
352
+ # No state file. If we were called for configuration with valid sequence number and MAC address,
353
+ # attempt to create state file. See code above for how we interpret these values.
354
+ sequence = config[:sequence]
355
+ raise RuntimeError, format(ERROR_NOT_A_SEQUENCE, sequence) unless sequence.is_a?(Integer)
356
+ sequence &= 0xFFFF
357
+ mac_addr = config[:mac_addr]
358
+ raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless mac_addr.is_a?(String) and mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
359
+ mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
360
+ File.open state_file, "w" do |file|
361
+ file.flock(File::LOCK_EX)
362
+ @@mac_addr, @@mac_hex, @@sequence, @@state_file = mac_addr, mac_hex, sequence, state_file
363
+ file.pos = 0
364
+ YAML::dump state(true), file
365
+ file.truncate file.pos
366
+ end
367
+ # Initialized.
368
+ if @@logger
369
+ @@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
370
+ else
371
+ warn "#{PACKAGE}: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
372
+ end
373
+ @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
374
+ end
375
+ rescue Exception=>error
376
+ @@last_clock = nil
377
+ raise error
378
+ end
379
+ end
380
+
381
+ end
382
+
383
+
384
+ if __FILE__ == $0
385
+ UUID.setup
386
+ end
@@ -0,0 +1,48 @@
1
+ #
2
+ # = test-uuid.rb - UUID generator test cases
3
+ #
4
+ # Author:: Assaf Arkin assaf@labnotes.org
5
+ # Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
6
+ # Copyright:: Copyright (c) 2005 Assaf Arkin
7
+ # License:: MIT and/or Creative Commons Attribution-ShareAlike
8
+ #
9
+ #--
10
+ #++
11
+
12
+
13
+ require 'test/unit'
14
+ require 'uuid'
15
+
16
+ class TestUUID < Test::Unit::TestCase
17
+
18
+ def setup
19
+ end
20
+
21
+ def teardown
22
+ end
23
+
24
+ def test_format
25
+ 10.times do
26
+ uuid = UUID.new :compact
27
+ assert uuid =~ /^[0-9a-fA-F]{32}$/, "UUID does not conform to :compact format"
28
+ uuid = UUID.new :default
29
+ 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"
30
+ uuid = UUID.new :urn
31
+ 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"
32
+ end
33
+ end
34
+
35
+ def test_monotonic
36
+ count = 100000
37
+ seen = {}
38
+ count.times do |i|
39
+ uuid = UUID.new
40
+ assert !seen.has_key?(uuid), "UUID repeated"
41
+ seen[uuid] = true
42
+ print '.' if (i % 10000) == 0
43
+ STDOUT.flush
44
+ end
45
+ end
46
+
47
+ end
48
+
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.3
3
+ specification_version: 1
4
+ name: uuid
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2005-11-20
8
+ summary: UUID generator
9
+ require_paths:
10
+ - lib
11
+ email: assaf@labnotes.org
12
+ homepage: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
13
+ rubyforge_project: reliable-msg
14
+ description: UUID generator for producing universally unique identifiers based on RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt).
15
+ autorequire: uuid.rb
16
+ default_executable: uuid-setup
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - Assaf Arkin
29
+ files:
30
+ - bin/uuid-setup
31
+ - test/test-uuid.rb
32
+ - lib/uuid.rb
33
+ - README
34
+ - MIT-LICENSE
35
+ - Rakefile
36
+ - changelog.txt
37
+ test_files: []
38
+ rdoc_options:
39
+ - "--main"
40
+ - README
41
+ - "--title"
42
+ - UUID generator
43
+ - "--line-numbers"
44
+ extra_rdoc_files:
45
+ - README
46
+ executables:
47
+ - uuid-setup
48
+ extensions: []
49
+ requirements: []
50
+ dependencies: []