uuidtools 2.1.0 → 3.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.
data/lib/uuidtools.rb CHANGED
@@ -1,26 +1,21 @@
1
+ # encoding:utf-8
1
2
  #--
2
- # UUIDTools, Copyright (c) 2005-2008 Bob Aman
3
+ # Copyright (C) 2005-2014 Bob Aman
3
4
  #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
11
8
  #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
14
10
  #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
22
16
  #++
23
17
 
18
+
24
19
  $:.unshift(File.dirname(__FILE__))
25
20
 
26
21
  require 'uri'
@@ -38,30 +33,50 @@ rescue LoadError
38
33
  end
39
34
 
40
35
  module UUIDTools
41
- #= uuidtools.rb
42
- #
36
+ ##
43
37
  # UUIDTools was designed to be a simple library for generating any
44
38
  # of the various types of UUIDs. It conforms to RFC 4122 whenever
45
39
  # possible.
46
40
  #
47
- #== Example
48
- # UUID.md5_create(UUID_DNS_NAMESPACE, "www.widgets.com")
49
- # => #<UUID:0x287576 UUID:3d813cbb-47fb-32ba-91df-831e1593ac29>
50
- # UUID.sha1_create(UUID_DNS_NAMESPACE, "www.widgets.com")
51
- # => #<UUID:0x2a0116 UUID:21f7f8de-8051-5b89-8680-0195ef798b6a>
52
- # UUID.timestamp_create
53
- # => #<UUID:0x2adfdc UUID:64a5189c-25b3-11da-a97b-00c04fd430c8>
54
- # UUID.random_create
55
- # => #<UUID:0x19013a UUID:984265dc-4200-4f02-ae70-fe4f48964159>
41
+ # @example
42
+ # UUID.md5_create(UUID_DNS_NAMESPACE, "www.widgets.com")
43
+ # # => #<UUID:0x287576 UUID:3d813cbb-47fb-32ba-91df-831e1593ac29>
44
+ # UUID.sha1_create(UUID_DNS_NAMESPACE, "www.widgets.com")
45
+ # # => #<UUID:0x2a0116 UUID:21f7f8de-8051-5b89-8680-0195ef798b6a>
46
+ # UUID.timestamp_create
47
+ # # => #<UUID:0x2adfdc UUID:64a5189c-25b3-11da-a97b-00c04fd430c8>
48
+ # UUID.random_create
49
+ # # => #<UUID:0x19013a UUID:984265dc-4200-4f02-ae70-fe4f48964159>
56
50
  class UUID
57
51
  include Comparable
58
52
 
53
+ ##
54
+ # @api private
59
55
  @@last_timestamp = nil
56
+
57
+ ##
58
+ # @api private
60
59
  @@last_node_id = nil
60
+
61
+ ##
62
+ # @api private
61
63
  @@last_clock_sequence = nil
64
+
65
+ ##
66
+ # @api private
62
67
  @@state_file = nil
68
+
69
+ ##
70
+ # @api private
63
71
  @@mutex = Mutex.new
64
72
 
73
+ ##
74
+ # Creates a new UUID structure from its component values.
75
+ # @see UUID.md5_create
76
+ # @see UUID.sha1_create
77
+ # @see UUID.timestamp_create
78
+ # @see UUID.random_create
79
+ # @api private
65
80
  def initialize(time_low, time_mid, time_hi_and_version,
66
81
  clock_seq_hi_and_reserved, clock_seq_low, nodes)
67
82
  unless time_low >= 0 && time_low < 4294967296
@@ -110,22 +125,38 @@ module UUIDTools
110
125
  @nodes = nodes
111
126
  end
112
127
 
128
+ ##
129
+ # Returns the value of attribute `time_low`
113
130
  attr_accessor :time_low
131
+
132
+ ##
133
+ # Returns the value of attribute `time_mid`
114
134
  attr_accessor :time_mid
135
+
136
+ ##
137
+ # Returns the value of attribute `time_hi_and_version`
115
138
  attr_accessor :time_hi_and_version
139
+
140
+ ##
141
+ # Returns the value of attribute `clock_seq_hi_and_reserved`
116
142
  attr_accessor :clock_seq_hi_and_reserved
143
+
144
+ ##
145
+ # Returns the value of attribute `clock_seq_low`
117
146
  attr_accessor :clock_seq_low
147
+
148
+ ##
149
+ # Returns the value of attribute `nodes`
118
150
  attr_accessor :nodes
119
151
 
152
+ ##
120
153
  # Parses a UUID from a string.
121
154
  def self.parse(uuid_string)
122
155
  unless uuid_string.kind_of? String
123
156
  raise TypeError,
124
157
  "Expected String, got #{uuid_string.class.name} instead."
125
158
  end
126
- uuid_components = uuid_string.downcase.scan(
127
- Regexp.new("^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-" +
128
- "([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})$")).first
159
+ uuid_components = uuid_string.downcase.scan(UUIDTools::UUID_REGEXP).first
129
160
  raise ArgumentError, "Invalid UUID format." if uuid_components.nil?
130
161
  time_low = uuid_components[0].to_i(16)
131
162
  time_mid = uuid_components[1].to_i(16)
@@ -133,52 +164,114 @@ module UUIDTools
133
164
  clock_seq_hi_and_reserved = uuid_components[3].to_i(16)
134
165
  clock_seq_low = uuid_components[4].to_i(16)
135
166
  nodes = []
136
- for i in 0..5
167
+ 6.times do |i|
137
168
  nodes << uuid_components[5][(i * 2)..(i * 2) + 1].to_i(16)
138
169
  end
139
170
  return self.new(time_low, time_mid, time_hi_and_version,
140
171
  clock_seq_hi_and_reserved, clock_seq_low, nodes)
141
172
  end
142
173
 
174
+ ##
143
175
  # Parses a UUID from a raw byte string.
144
176
  def self.parse_raw(raw_string)
145
177
  unless raw_string.kind_of? String
146
178
  raise TypeError,
147
179
  "Expected String, got #{raw_string.class.name} instead."
148
180
  end
149
- integer = self.convert_byte_string_to_int(raw_string)
150
181
 
151
- time_low = (integer >> 96) & 0xFFFFFFFF
152
- time_mid = (integer >> 80) & 0xFFFF
153
- time_hi_and_version = (integer >> 64) & 0xFFFF
154
- clock_seq_hi_and_reserved = (integer >> 56) & 0xFF
155
- clock_seq_low = (integer >> 48) & 0xFF
182
+ if raw_string.respond_to?(:force_encoding)
183
+ raw_string.force_encoding(Encoding::ASCII_8BIT)
184
+ end
185
+
186
+ raw_length = raw_string.length
187
+ if raw_length < 16
188
+ # Option A: Enforce raw_string be 16 characters (More strict)
189
+ #raise ArgumentError,
190
+ # "Expected 16 bytes, got #{raw_string.length} instead."
191
+
192
+ # Option B: Pad raw_string to 16 characters (Compatible with existing behavior)
193
+ raw_string = raw_string.rjust(16, "\0")
194
+ elsif raw_length > 16
195
+ # NOTE: As per "Option B" above, existing behavior would use the lower
196
+ # 128-bits of an overly long raw_string instead of using the upper 128-bits.
197
+ start_index = raw_length - 16
198
+ raw_string = raw_string[start_index...raw_length]
199
+ end
200
+
201
+ raw_bytes = []
202
+ if raw_string[0].respond_to? :ord
203
+ for i in 0...raw_string.size
204
+ raw_bytes << raw_string[i].ord
205
+ end
206
+ else
207
+ raw_bytes = raw_string
208
+ end
209
+
210
+ time_low = ((raw_bytes[0] << 24) +
211
+ (raw_bytes[1] << 16) +
212
+ (raw_bytes[2] << 8) +
213
+ raw_bytes[3])
214
+ time_mid = ((raw_bytes[4] << 8) +
215
+ raw_bytes[5])
216
+ time_hi_and_version = ((raw_bytes[6] << 8) +
217
+ raw_bytes[7])
218
+ clock_seq_hi_and_reserved = raw_bytes[8]
219
+ clock_seq_low = raw_bytes[9]
156
220
  nodes = []
157
- for i in 0..5
158
- nodes << ((integer >> (40 - (i * 8))) & 0xFF)
221
+ for i in 10...16
222
+ nodes << raw_bytes[i]
159
223
  end
224
+
160
225
  return self.new(time_low, time_mid, time_hi_and_version,
161
- clock_seq_hi_and_reserved, clock_seq_low, nodes)
226
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
162
227
  end
163
228
 
229
+ ##
164
230
  # Parses a UUID from an Integer.
165
231
  def self.parse_int(uuid_int)
166
232
  unless uuid_int.kind_of?(Integer)
167
233
  raise ArgumentError,
168
234
  "Expected Integer, got #{uuid_int.class.name} instead."
169
235
  end
170
- return self.parse_raw(self.convert_int_to_byte_string(uuid_int, 16))
236
+
237
+ time_low = (uuid_int >> 96) & 0xFFFFFFFF
238
+ time_mid = (uuid_int >> 80) & 0xFFFF
239
+ time_hi_and_version = (uuid_int >> 64) & 0xFFFF
240
+ clock_seq_hi_and_reserved = (uuid_int >> 56) & 0xFF
241
+ clock_seq_low = (uuid_int >> 48) & 0xFF
242
+ nodes = []
243
+ for i in 0..5
244
+ nodes << ((uuid_int >> (40 - (i * 8))) & 0xFF)
245
+ end
246
+
247
+ return self.new(time_low, time_mid, time_hi_and_version,
248
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
171
249
  end
172
250
 
251
+ ##
173
252
  # Parse a UUID from a hexdigest String.
174
- def self.parse_hexdigest(uuid_hexdigest)
175
- unless uuid_hexdigest.kind_of?(String)
253
+ def self.parse_hexdigest(uuid_hex)
254
+ unless uuid_hex.kind_of?(String)
176
255
  raise ArgumentError,
177
- "Expected String, got #{uuid_hexdigest.class.name} instead."
256
+ "Expected String, got #{uuid_hex.class.name} instead."
257
+ end
258
+
259
+ time_low = uuid_hex[0...8].to_i(16)
260
+ time_mid = uuid_hex[8...12].to_i(16)
261
+ time_hi_and_version = uuid_hex[12...16].to_i(16)
262
+ clock_seq_hi_and_reserved = uuid_hex[16...18].to_i(16)
263
+ clock_seq_low = uuid_hex[18...20].to_i(16)
264
+ nodes_string = uuid_hex[20...32]
265
+ nodes = []
266
+ for i in 0..5
267
+ nodes << nodes_string[(i * 2)..(i * 2) + 1].to_i(16)
178
268
  end
179
- return self.parse_int(uuid_hexdigest.to_i(16))
269
+
270
+ return self.new(time_low, time_mid, time_hi_and_version,
271
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
180
272
  end
181
273
 
274
+ ##
182
275
  # Creates a UUID from a random value.
183
276
  def self.random_create()
184
277
  new_uuid = self.parse_raw(SecureRandom.random_bytes(16))
@@ -189,6 +282,7 @@ module UUIDTools
189
282
  return new_uuid
190
283
  end
191
284
 
285
+ ##
192
286
  # Creates a UUID from a timestamp.
193
287
  def self.timestamp_create(timestamp=nil)
194
288
  # We need a lock here to prevent two threads from ever
@@ -213,7 +307,7 @@ module UUIDTools
213
307
  nodes = SecureRandom.random_bytes(6).unpack("C*")
214
308
  nodes[0] |= 0b00000001
215
309
  end
216
- for i in 0..5
310
+ 6.times do |i|
217
311
  node_id += (nodes[i] << (40 - (i * 8)))
218
312
  end
219
313
  clock_sequence = @@last_clock_sequence
@@ -248,16 +342,19 @@ module UUIDTools
248
342
  end
249
343
  end
250
344
 
345
+ ##
251
346
  # Creates a UUID using the MD5 hash. (Version 3)
252
347
  def self.md5_create(namespace, name)
253
348
  return self.create_from_hash(Digest::MD5, namespace, name)
254
349
  end
255
350
 
351
+ ##
256
352
  # Creates a UUID using the SHA1 hash. (Version 5)
257
353
  def self.sha1_create(namespace, name)
258
354
  return self.create_from_hash(Digest::SHA1, namespace, name)
259
355
  end
260
356
 
357
+ ##
261
358
  # This method applies only to version 1 UUIDs.
262
359
  # Checks if the node ID was generated from a random number
263
360
  # or from an IEEE 802 address (MAC address).
@@ -269,6 +366,7 @@ module UUIDTools
269
366
  return ((self.nodes.first & 0x01) == 1)
270
367
  end
271
368
 
369
+ ##
272
370
  # Returns true if this UUID is the
273
371
  # nil UUID (00000000-0000-0000-0000-000000000000).
274
372
  def nil_uuid?
@@ -283,6 +381,7 @@ module UUIDTools
283
381
  return true
284
382
  end
285
383
 
384
+ ##
286
385
  # Returns the UUID version type.
287
386
  # Possible values:
288
387
  # 1 - Time-based with unique or random host identifier
@@ -294,6 +393,7 @@ module UUIDTools
294
393
  return (time_hi_and_version >> 12)
295
394
  end
296
395
 
396
+ ##
297
397
  # Returns the UUID variant.
298
398
  # Possible values:
299
399
  # 0b000 - Reserved, NCS backward compatibility.
@@ -313,16 +413,13 @@ module UUIDTools
313
413
  return (result >> 6)
314
414
  end
315
415
 
416
+ ##
316
417
  # Returns true if this UUID is valid.
317
418
  def valid?
318
- if [0b000, 0b100, 0b110, 0b111].include?(self.variant) &&
319
- (1..5).include?(self.version)
320
- return true
321
- else
322
- return false
323
- end
419
+ return self.variant == 0b100 && (1..8).cover?(self.version)
324
420
  end
325
421
 
422
+ ##
326
423
  # Returns the IEEE 802 address used to generate this UUID or
327
424
  # nil if a MAC address was not used.
328
425
  def mac_address
@@ -333,6 +430,7 @@ module UUIDTools
333
430
  end).join(":")
334
431
  end
335
432
 
433
+ ##
336
434
  # Returns the timestamp used to generate this UUID
337
435
  def timestamp
338
436
  return nil if self.version != 1
@@ -345,8 +443,10 @@ module UUIDTools
345
443
  (gmt_timestamp_100_nanoseconds - 0x01B21DD213814000) / 10000000.0)
346
444
  end
347
445
 
446
+ ##
348
447
  # Compares two UUIDs lexically
349
448
  def <=>(other_uuid)
449
+ return nil unless other_uuid.is_a?(UUIDTools::UUID)
350
450
  check = self.time_low <=> other_uuid.time_low
351
451
  return check if check != 0
352
452
  check = self.time_mid <=> other_uuid.time_mid
@@ -358,7 +458,7 @@ module UUIDTools
358
458
  return check if check != 0
359
459
  check = self.clock_seq_low <=> other_uuid.clock_seq_low
360
460
  return check if check != 0
361
- for i in 0..5
461
+ 6.times do |i|
362
462
  if (self.nodes[i] < other_uuid.nodes[i])
363
463
  return -1
364
464
  end
@@ -369,169 +469,252 @@ module UUIDTools
369
469
  return 0
370
470
  end
371
471
 
472
+ ##
372
473
  # Returns a representation of the object's state
373
474
  def inspect
374
475
  return "#<UUID:0x#{self.object_id.to_s(16)} UUID:#{self.to_s}>"
375
476
  end
376
477
 
478
+ ##
377
479
  # Returns the hex digest of the UUID object.
378
480
  def hexdigest
379
- return self.to_i.to_s(16).rjust(32, "0")
481
+ (self.frozen? ?
482
+ generate_hexdigest : (@hexdigest ||= generate_hexdigest)
483
+ ).dup
380
484
  end
381
485
 
486
+ ##
382
487
  # Returns the raw bytes that represent this UUID.
383
488
  def raw
384
- return self.class.convert_int_to_byte_string(self.to_i, 16)
489
+ (self.frozen? ? generate_raw : (@raw ||= generate_raw)).dup
385
490
  end
386
491
 
492
+ ##
387
493
  # Returns a string representation for this UUID.
388
494
  def to_s
389
- result = sprintf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", @time_low, @time_mid,
390
- @time_hi_and_version, @clock_seq_hi_and_reserved, @clock_seq_low);
391
- for i in 0..5
392
- result << sprintf("%2.2x", @nodes[i])
393
- end
394
- return result.downcase
495
+ (self.frozen? ? generate_s : (@string ||= generate_s)).dup
395
496
  end
396
497
  alias_method :to_str, :to_s
397
498
 
499
+ ##
398
500
  # Returns an integer representation for this UUID.
399
501
  def to_i
400
- @integer ||= (begin
502
+ self.frozen? ? generate_i : (@integer ||= generate_i)
503
+ end
504
+
505
+ ##
506
+ # Returns a URI string for this UUID.
507
+ def to_uri
508
+ return "urn:uuid:#{self.to_s}"
509
+ end
510
+
511
+ ##
512
+ # Returns an integer hash value.
513
+ def hash
514
+ self.frozen? ? generate_hash : (@hash ||= generate_hash)
515
+ end
516
+
517
+ protected
518
+ ##
519
+ # Generates the hex digest of the UUID object.
520
+ #
521
+ # @api private
522
+ def generate_hexdigest
523
+ return self.to_i.to_s(16).rjust(32, "0")
524
+ end
525
+
526
+ # Generates an integer hash value.
527
+ #
528
+ # @api private
529
+ def generate_hash
530
+ return self.to_i % 0x3fffffff
531
+ end
532
+
533
+ ##
534
+ # Generates an integer representation for this UUID.
535
+ #
536
+ # @api private
537
+ def generate_i
538
+ return (begin
401
539
  bytes = (time_low << 96) + (time_mid << 80) +
402
540
  (time_hi_and_version << 64) + (clock_seq_hi_and_reserved << 56) +
403
541
  (clock_seq_low << 48)
404
- for i in 0..5
542
+ 6.times do |i|
405
543
  bytes += (nodes[i] << (40 - (i * 8)))
406
544
  end
407
545
  bytes
408
546
  end)
409
547
  end
410
548
 
411
- # Returns a URI string for this UUID.
412
- def to_uri
413
- return "urn:uuid:#{self.to_s}"
549
+ ##
550
+ # Generates a string representation for this UUID.
551
+ #
552
+ # @api private
553
+ def generate_s
554
+ result = sprintf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", @time_low, @time_mid,
555
+ @time_hi_and_version, @clock_seq_hi_and_reserved, @clock_seq_low);
556
+ 6.times do |i|
557
+ result << sprintf("%2.2x", @nodes[i])
558
+ end
559
+ return result.downcase
414
560
  end
415
561
 
416
- # Returns an integer hash value.
417
- def hash
418
- @hash ||= self.to_i % 0x3fffffff
562
+ ##
563
+ # Generates the raw bytes that represent this UUID.
564
+ #
565
+ # @api private
566
+ def generate_raw
567
+ return self.class.convert_int_to_byte_string(self.to_i, 16)
419
568
  end
420
569
 
570
+ public
571
+ ##
421
572
  # Returns true if this UUID is exactly equal to the other UUID.
422
573
  def eql?(other)
423
574
  return self == other
424
575
  end
425
576
 
577
+ #
578
+ # Determine what OS we're running on. Helps decide how to find the MAC
579
+ #
580
+ def self.os_class
581
+ require 'rbconfig'
582
+ os_platform = RbConfig::CONFIG['target_os']
583
+ if (os_platform =~ /win/i && !(os_platform =~ /darwin/i)) ||
584
+ os_platform =~ /w32/i
585
+ :windows
586
+ elsif os_platform =~ /solaris/i
587
+ :solaris
588
+ elsif os_platform =~ /netbsd/i
589
+ :netbsd
590
+ elsif os_platform =~ /openbsd/i
591
+ :openbsd
592
+ end
593
+ end
594
+
595
+ # making these class variables helps with testing
596
+ @ipconfig_command = "ipconfig"
597
+ @ipconfig_path_default = "c:\\windows\\system32\\ipconfig.exe"
598
+ @ifconfig_command = "ifconfig"
599
+ @ifconfig_path_default = "/sbin/ifconfig"
600
+ @ip_command = "ip"
601
+ @ip_path_default = "/sbin/ip"
602
+
603
+ class << self
604
+ attr_accessor :ipconfig_command, :ipconfig_path_default
605
+ attr_accessor :ifconfig_command, :ifconfig_path_default
606
+ attr_accessor :ip_command, :ip_path_default
607
+ end
608
+
609
+ #
610
+ # Find the path of the ipconfig command if it is present
611
+ #
612
+ def self.ipconfig_path
613
+ path = `where #{UUID.ipconfig_command}`.strip
614
+ path = UUID.ipconfig_path_default if path == "" && File.exist?(UUID.ipconfig_path_default)
615
+ return (path === "" ? nil : path)
616
+ end
617
+
618
+ #
619
+ # Find the path of the ifconfig(8) command if it is present
620
+ #
621
+ def self.ifconfig_path
622
+ path = `which #{UUID.ifconfig_command} 2>/dev/null`.strip
623
+ path = UUID.ifconfig_path_default if path == "" && File.exist?(UUID.ifconfig_path_default)
624
+ return (path === "" ? nil : path)
625
+ end
626
+
627
+ #
628
+ # Find the path of the ip(8) command if it is present
629
+ #
630
+ def self.ip_path
631
+ path = `which #{UUID.ip_command} 2>/dev/null`.strip
632
+ path = UUID.ip_path_default if path == "" && File.exist?(UUID.ip_path_default)
633
+ return (path === "" ? nil : path)
634
+ end
635
+
636
+ #
637
+ # Call the ipconfig command that is found
638
+ #
639
+ def self.ipconfig(all = nil)
640
+ ipconfig_path = UUID.ipconfig_path
641
+ command =
642
+ if ipconfig_path
643
+ "#{ipconfig_path}#{all ? ' /all' : ''}"
644
+ end
645
+ `#{command}` if command
646
+ end
647
+
648
+ #
649
+ # Call the ifconfig or ip command that is found
650
+ #
651
+ def self.ifconfig(all = nil)
652
+ ifconfig_path = UUID.ifconfig_path
653
+ ip_path = UUID.ip_path
654
+ command =
655
+ if ifconfig_path
656
+ "#{ifconfig_path}#{all ? ' -a' : ''}"
657
+ elsif ip_path
658
+ "#{ip_path} addr list"
659
+ end
660
+ `#{command}` if command
661
+ end
662
+
663
+ # Match and return the first Mac address found
664
+ def self.first_mac(instring)
665
+ return nil if instring.nil?
666
+
667
+ mac_regexps = [
668
+ Regexp.new("address:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
669
+ Regexp.new("addr:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
670
+ Regexp.new("ether:? (#{(["[0-9a-fA-F]{1,2}"] * 6).join(":")})"),
671
+ Regexp.new("HWaddr:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
672
+ Regexp.new("link/ether? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
673
+ Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
674
+ Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join("-")})")
675
+ ]
676
+ parse_mac = lambda do |output|
677
+ (mac_regexps.map do |regexp|
678
+ result = output[regexp, 1]
679
+ result.downcase.gsub(/-/, ":") if result != nil
680
+ end).compact.first
681
+ end
682
+
683
+ mac = parse_mac.call(instring)
684
+ if mac
685
+ # expand octets that were compressed (solaris)
686
+ return (mac.split(':').map do |octet|
687
+ (octet.length == 1 ? "0#{octet}" : octet)
688
+ end).join(':')
689
+ else
690
+ return nil
691
+ end
692
+ end
693
+
694
+ ##
426
695
  # Returns the MAC address of the current computer's network card.
427
696
  # Returns nil if a MAC address could not be found.
428
- def self.mac_address #:nodoc:
697
+ def self.mac_address
429
698
  if !defined?(@@mac_address)
699
+ @@mac_address = nil
430
700
  require 'rbconfig'
431
- os_platform = Config::CONFIG['target_os']
432
- os_class = nil
433
- if (os_platform =~ /win/i && !(os_platform =~ /darwin/i)) ||
434
- os_platform =~ /w32/i
435
- os_class = :windows
436
- elsif os_platform =~ /solaris/i
437
- os_class = :solaris
438
- elsif os_platform =~ /netbsd/i
439
- os_class = :netbsd
440
- elsif os_platform =~ /openbsd/i
441
- os_class = :openbsd
442
- end
443
- mac_regexps = [
444
- Regexp.new("address:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
445
- Regexp.new("addr:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
446
- Regexp.new("ether:? (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
447
- Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"),
448
- Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join("-")})")
449
- ]
450
- parse_mac = lambda do |output|
451
- (mac_regexps.map do |regexp|
452
- result = output[regexp, 1]
453
- result.downcase.gsub(/-/, ":") if result != nil
454
- end).compact.first
455
- end
701
+
702
+ os_class = UUID.os_class
703
+
456
704
  if os_class == :windows
457
- script_in_path = true
458
- else
459
- script_in_path = Kernel.system("which ifconfig 2>&1 > /dev/null")
460
- end
461
- if os_class == :solaris
462
- begin
463
- ifconfig_output =
464
- (script_in_path ? `ifconfig -a` : `/sbin/ifconfig -a`)
465
- ip_addresses = ifconfig_output.scan(
466
- /inet\s?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/)
467
- ip = ip_addresses.find {|addr| addr[0] != '127.0.0.1'}[0]
468
- @@mac_address = `/usr/sbin/arp #{ip}`.split(' ')[3]
469
- rescue Exception
470
- end
471
- if @@mac_address == "" || @@mac_address == nil
472
- begin
473
- ifconfig_output =
474
- (script_in_path ?
475
- `ifconfig -a` : `/sbin/ifconfig -a`).split(' ')
476
- index = ifconfig_output.index("inet") + 1
477
- ip = ifconfig_output[index]
478
- @@mac_address = `arp #{ip}`.split(' ')[3]
479
- rescue Exception
480
- end
481
- end
482
- elsif os_class == :windows
483
705
  begin
484
- @@mac_address = parse_mac.call(`ipconfig /all`)
706
+ ipconfig_output = UUID.ipconfig(:all)
707
+ @@mac_address = UUID.first_mac ipconfig_output
485
708
  rescue
486
709
  end
487
710
  else
488
- begin
489
- if os_class == :netbsd
490
- @@mac_address = parse_mac.call(
491
- script_in_path ? `ifconfig -a 2>&1` : `/sbin/ifconfig -a 2>&1`
492
- )
493
- elsif os_class == :openbsd
494
- @@mac_address = parse_mac.call(
495
- script_in_path ? `ifconfig -a 2>&1` : `/sbin/ifconfig -a 2>&1`
496
- )
497
- elsif File.exists?('/sbin/ifconfig')
498
- @@mac_address = parse_mac.call(
499
- script_in_path ? `ifconfig 2>&1` : `/sbin/ifconfig 2>&1`
500
- )
501
- if @@mac_address == nil
502
- @@mac_address = parse_mac.call(
503
- script_in_path ?
504
- `ifconfig -a 2>&1` : `/sbin/ifconfig -a 2>&1`
505
- )
506
- end
507
- if @@mac_address == nil
508
- @@mac_address = parse_mac.call(
509
- script_in_path ?
510
- `ifconfig | grep HWaddr | cut -c39- 2>&1` :
511
- `/sbin/ifconfig | grep HWaddr | cut -c39- 2>&1`
512
- )
513
- end
514
- else
515
- @@mac_address = parse_mac.call(
516
- script_in_path ? `ifconfig 2>&1` : `/sbin/ifconfig 2>&1`
517
- )
518
- if @@mac_address == nil
519
- @@mac_address = parse_mac.call(
520
- script_in_path ?
521
- `ifconfig -a 2>&1` : `/sbin/ifconfig -a 2>&1`
522
- )
523
- end
524
- if @@mac_address == nil
525
- @@mac_address = parse_mac.call(
526
- script_in_path ?
527
- `ifconfig | grep HWaddr | cut -c39- 2>&1` :
528
- `/sbin/ifconfig | grep HWaddr | cut -c39- 2>&1`
529
- )
530
- end
531
- end
532
- rescue
711
+ ifconfig_output = UUID.ifconfig(:all)
712
+ if ifconfig_output
713
+ # linux, bsd, macos, solaris
714
+ @@mac_address = UUID.first_mac ifconfig_output
533
715
  end
534
716
  end
717
+
535
718
  if @@mac_address != nil
536
719
  if @@mac_address.respond_to?(:to_str)
537
720
  @@mac_address = @@mac_address.to_str
@@ -552,6 +735,7 @@ module UUIDTools
552
735
  return @@mac_address
553
736
  end
554
737
 
738
+ ##
555
739
  # Allows users to set the MAC address manually in cases where the MAC
556
740
  # address cannot be obtained programatically.
557
741
  def self.mac_address=(new_mac_address)
@@ -561,8 +745,12 @@ module UUIDTools
561
745
  # The following methods are not part of the public API,
562
746
  # and generally should not be called directly.
563
747
 
748
+
749
+ ##
564
750
  # Creates a new UUID from a SHA1 or MD5 hash
565
- def self.create_from_hash(hash_class, namespace, name) #:nodoc:
751
+ #
752
+ # @api private
753
+ def self.create_from_hash(hash_class, namespace, name)
566
754
  if hash_class == Digest::MD5
567
755
  version = 3
568
756
  elsif hash_class == Digest::SHA1
@@ -585,28 +773,60 @@ module UUIDTools
585
773
  return new_uuid
586
774
  end
587
775
 
588
- def self.convert_int_to_byte_string(integer, size) #:nodoc:
589
- byte_string = ""
590
- for i in 0..(size - 1)
776
+ ##
777
+ # @api private
778
+ def self.convert_int_to_byte_string(integer, size)
779
+ byte_string = +""
780
+ if byte_string.respond_to?(:force_encoding)
781
+ byte_string.force_encoding(Encoding::ASCII_8BIT)
782
+ end
783
+ size.times do |i|
591
784
  byte_string << ((integer >> (((size - 1) - i) * 8)) & 0xFF)
592
785
  end
593
786
  return byte_string
594
787
  end
595
788
 
596
- def self.convert_byte_string_to_int(byte_string) #:nodoc:
789
+ ##
790
+ # @api private
791
+ def self.convert_byte_string_to_int(byte_string)
792
+ if byte_string.respond_to?(:force_encoding)
793
+ byte_string.force_encoding(Encoding::ASCII_8BIT)
794
+ end
795
+
597
796
  integer = 0
598
797
  size = byte_string.size
599
- for i in 0..(size - 1)
600
- ordinal = (byte_string[i].respond_to?(:ord) ?
601
- byte_string[i].ord : byte_string[i])
602
- integer += (ordinal << (((size - 1) - i) * 8))
798
+ if byte_string[0].respond_to? :ord
799
+ for i in 0...size
800
+ integer += (byte_string[i].ord << (((size - 1) - i) * 8))
801
+ end
802
+ else
803
+ for i in 0...size
804
+ integer += (byte_string[i] << (((size - 1) - i) * 8))
805
+ end
603
806
  end
807
+
604
808
  return integer
605
809
  end
606
810
  end
607
811
 
812
+ ##
813
+ # Constant Regexp that matches a UUID and captures its components.
814
+ UUID_REGEXP = Regexp.new("\\A([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-" +
815
+ "([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})\\z")
816
+
817
+ ##
818
+ # Constant that represents the DNS namespace.
608
819
  UUID_DNS_NAMESPACE = UUID.parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
820
+
821
+ ##
822
+ # Constant that represents the URL namespace.
609
823
  UUID_URL_NAMESPACE = UUID.parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
824
+
825
+ ##
826
+ # Constant that represents the OID namespace.
610
827
  UUID_OID_NAMESPACE = UUID.parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
828
+
829
+ ##
830
+ # Constant that represents the X500 namespace.
611
831
  UUID_X500_NAMESPACE = UUID.parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
612
832
  end