uuid 1.0.0

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.
@@ -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: []