gabe-uuid 0.3

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Gabriel Boyer
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.markdown ADDED
@@ -0,0 +1,13 @@
1
+ UUID
2
+ ====
3
+
4
+ A simple library for generating/using Universally Unique Identifiers, as defined
5
+ in RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt).
6
+
7
+ This library generates version 4 UUIDs, which are based on random bytes read from
8
+ SecureRandom (included for Ruby <1.8.7).
9
+
10
+ Author
11
+ ------
12
+
13
+ Gabriel Boyer (gboyer@gmail.com)
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require "rake/gempackagetask"
2
+ require "spec/rake/spectask"
3
+
4
+ task :default => :spec
5
+
6
+ desc "Run all specs"
7
+ Spec::Rake::SpecTask.new("spec") do |task|
8
+ task.spec_opts = ["--format", "specdoc", "--colour"]
9
+ task.spec_files = ["spec/**/*_spec.rb"]
10
+ end
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.name = 'uuid'
14
+ s.version = '0.3'
15
+ s.platform = Gem::Platform::RUBY
16
+ s.author = 'Gabriel Boyer'
17
+ s.email = 'gboyer@gmail.com'
18
+ s.homepage = 'http://github.com/gabe/uuid/'
19
+ s.summary = 'Simple UUID implementation'
20
+ s.description = 'Simple UUID implementation, as per RFC 4122'
21
+ s.require_path = "lib"
22
+ s.files = %w[ LICENSE README.markdown Rakefile
23
+ spec/spec_helper.rb spec/uuid_spec.rb
24
+ lib/uuid.rb lib/compat/securerandom.rb ]
25
+ s.required_ruby_version = ">= 1.8.6"
26
+ end
27
+
28
+ Rake::GemPackageTask.new(spec) do |package|
29
+ package.gem_spec = spec
30
+ end
@@ -0,0 +1,182 @@
1
+ # = Secure random number generator interface.
2
+ #
3
+ # This library is an interface for secure random number generator which is
4
+ # suitable for generating session key in HTTP cookies, etc.
5
+ #
6
+ # It supports following secure random number generators.
7
+ #
8
+ # * openssl
9
+ # * /dev/urandom
10
+ # * Win32
11
+ #
12
+ # == Example
13
+ #
14
+ # # random hexadecimal string.
15
+ # p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
16
+ # p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
17
+ # p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
18
+ # p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
19
+ # p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
20
+ # ...
21
+ #
22
+ # # random base64 string.
23
+ # p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
24
+ # p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
25
+ # p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
26
+ # p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
27
+ # p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
28
+ # p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
29
+ # ...
30
+ #
31
+ # # random binary string.
32
+ # p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
33
+ # p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
34
+ # ...
35
+
36
+ begin
37
+ require 'openssl'
38
+ rescue LoadError
39
+ end
40
+
41
+ module SecureRandom
42
+ # SecureRandom.random_bytes generates a random binary string.
43
+ #
44
+ # The argument n specifies the length of the result string.
45
+ #
46
+ # If n is not specified, 16 is assumed.
47
+ # It may be larger in future.
48
+ #
49
+ # If secure random number generator is not available,
50
+ # NotImplementedError is raised.
51
+ def self.random_bytes(n=nil)
52
+ n ||= 16
53
+
54
+ if defined? OpenSSL::Random
55
+ return OpenSSL::Random.random_bytes(n)
56
+ end
57
+
58
+ if !defined?(@has_urandom) || @has_urandom
59
+ flags = File::RDONLY
60
+ flags |= File::NONBLOCK if defined? File::NONBLOCK
61
+ flags |= File::NOCTTY if defined? File::NOCTTY
62
+ flags |= File::NOFOLLOW if defined? File::NOFOLLOW
63
+ begin
64
+ File.open("/dev/urandom", flags) {|f|
65
+ unless f.stat.chardev?
66
+ raise Errno::ENOENT
67
+ end
68
+ @has_urandom = true
69
+ ret = f.readpartial(n)
70
+ if ret.length != n
71
+ raise NotImplementedError, "Unexpected partial read from random device"
72
+ end
73
+ return ret
74
+ }
75
+ rescue Errno::ENOENT
76
+ @has_urandom = false
77
+ end
78
+ end
79
+
80
+ if !defined?(@has_win32)
81
+ begin
82
+ require 'Win32API'
83
+
84
+ crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
85
+ @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')
86
+
87
+ hProvStr = " " * 4
88
+ prov_rsa_full = 1
89
+ crypt_verifycontext = 0xF0000000
90
+
91
+ if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
92
+ raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
93
+ end
94
+ @hProv, = hProvStr.unpack('L')
95
+
96
+ @has_win32 = true
97
+ rescue LoadError
98
+ @has_win32 = false
99
+ end
100
+ end
101
+ if @has_win32
102
+ bytes = " " * n
103
+ if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
104
+ raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
105
+ end
106
+ return bytes
107
+ end
108
+
109
+ raise NotImplementedError, "No random device"
110
+ end
111
+
112
+ # SecureRandom.hex generates a random hex string.
113
+ #
114
+ # The argument n specifies the length of the random length.
115
+ # The length of the result string is twice of n.
116
+ #
117
+ # If n is not specified, 16 is assumed.
118
+ # It may be larger in future.
119
+ #
120
+ # If secure random number generator is not available,
121
+ # NotImplementedError is raised.
122
+ def self.hex(n=nil)
123
+ random_bytes(n).unpack("H*")[0]
124
+ end
125
+
126
+ # SecureRandom.base64 generates a random base64 string.
127
+ #
128
+ # The argument n specifies the length of the random length.
129
+ # The length of the result string is about 4/3 of n.
130
+ #
131
+ # If n is not specified, 16 is assumed.
132
+ # It may be larger in future.
133
+ #
134
+ # If secure random number generator is not available,
135
+ # NotImplementedError is raised.
136
+ def self.base64(n=nil)
137
+ [random_bytes(n)].pack("m*").delete("\n")
138
+ end
139
+
140
+ # SecureRandom.random_number generates a random number.
141
+ #
142
+ # If an positive integer is given as n,
143
+ # SecureRandom.random_number returns an integer:
144
+ # 0 <= SecureRandom.random_number(n) < n.
145
+ #
146
+ # If 0 is given or an argument is not given,
147
+ # SecureRandom.random_number returns an float:
148
+ # 0.0 <= SecureRandom.random_number() < 1.0.
149
+ def self.random_number(n=0)
150
+ if 0 < n
151
+ hex = n.to_s(16)
152
+ hex = '0' + hex if (hex.length & 1) == 1
153
+ bin = [hex].pack("H*")
154
+ mask = bin[0].ord
155
+ mask |= mask >> 1
156
+ mask |= mask >> 2
157
+ mask |= mask >> 4
158
+ begin
159
+ rnd = SecureRandom.random_bytes(bin.length)
160
+ rnd[0] = (rnd[0].ord & mask).chr
161
+ end until rnd < bin
162
+ rnd.unpack("H*")[0].hex
163
+ else
164
+ # assumption: Float::MANT_DIG <= 64
165
+ i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
166
+ Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
167
+ end
168
+ end
169
+
170
+ # Following code is based on David Garamond's GUID library for Ruby.
171
+ def self.lastWin32ErrorMessage # :nodoc:
172
+ get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
173
+ format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
174
+ format_message_ignore_inserts = 0x00000200
175
+ format_message_from_system = 0x00001000
176
+
177
+ code = get_last_error.call
178
+ msg = "\0" * 1024
179
+ len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
180
+ msg[0, len].tr("\r", '').chomp
181
+ end
182
+ end
data/lib/uuid.rb ADDED
@@ -0,0 +1,128 @@
1
+ begin
2
+ require 'securerandom'
3
+ rescue LoadError
4
+ require File.join(File.dirname(__FILE__), 'compat', 'securerandom')
5
+ end
6
+
7
+ class UUID
8
+ def initialize(value = nil)
9
+ @value = Integer(value)
10
+ unless (@value >= 0) && (@value < (1 << 128))
11
+ raise RangeError, "#{value} (Integer value #{@value}) is out of range (need unsigned 128-bit value)"
12
+ end
13
+ end
14
+
15
+ def ==(other)
16
+ eql?(other)
17
+ end
18
+
19
+ def clock_seq
20
+ ((clock_seq_hi_variant & 0x3f) << 8) | clock_seq_low
21
+ end
22
+
23
+ def clock_seq_hi_variant
24
+ (to_i >> 56) & 0xff
25
+ end
26
+
27
+ def clock_seq_low
28
+ (to_i >> 48) & 0xff
29
+ end
30
+
31
+ def eql?(other)
32
+ other.is_a?(self.class) && (other.to_i == to_i)
33
+ end
34
+
35
+ def hash
36
+ to_i.hash
37
+ end
38
+
39
+ def hex
40
+ '%032x' % to_i
41
+ end
42
+
43
+ def inspect
44
+ to_s
45
+ end
46
+
47
+ def node
48
+ to_i & 0xffffffffffff
49
+ end
50
+
51
+ def time
52
+ ((time_hi_version & 0x0fff) << 48) | (time_mid << 32) | time_low
53
+ end
54
+
55
+ def time_hi_version
56
+ (to_i >> 64) & 0xffff
57
+ end
58
+
59
+ def time_low
60
+ (to_i >> 96)
61
+ end
62
+
63
+ def time_mid
64
+ (to_i >> 80) & 0xffff
65
+ end
66
+
67
+ def to_i
68
+ @value
69
+ end
70
+
71
+ def to_s
72
+ h = hex
73
+ '%s-%s-%s-%s-%s' % [h[0...8], h[8...12], h[12...16], h[16...20], h[20...32]]
74
+ end
75
+
76
+ def urn
77
+ "urn:uuid:#{self}"
78
+ end
79
+
80
+ RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
81
+ 'reserved for NCS compatibility',
82
+ 'specified in RFC 4122',
83
+ 'reserved for Microsoft compatibility',
84
+ 'reserved for future definition'
85
+ ]
86
+
87
+ def variant
88
+ case 0
89
+ when to_i & (0x8000 << 48)
90
+ RESERVED_NCS
91
+ when to_i & (0x4000 << 48)
92
+ RFC_4122
93
+ when to_i & (0x2000 << 48)
94
+ RESERVED_MICROSOFT
95
+ else
96
+ RESERVED_FUTURE
97
+ end
98
+ end
99
+
100
+ def version
101
+ (variant == RFC_4122) ? ((to_i >> 76) & 0xf) : nil
102
+ end
103
+
104
+ class << self
105
+ def parse(uuid)
106
+ str = uuid.to_s
107
+ unless str =~ /^(urn:uuid:)?[0-9a-fA-F]{8}((-)?[0-9a-fA-F]{4}){3}(-)?[0-9a-fA-F]{12}$/
108
+ raise ArgumentError, "#{str} is not a recognized UUID representation"
109
+ end
110
+ new bytes_to_i(str.gsub(/(^urn:uuid:|-)/, '').downcase.unpack('a2' * 16).collect { |x| x.to_i(16) }.pack('C*'))
111
+ end
112
+
113
+ def uuid4
114
+ bytes = SecureRandom.random_bytes(16)
115
+ bytes[6] = (bytes[6] & 0x0f) | 0x40
116
+ bytes[8] = (bytes[8] & 0x3f) | 0x80
117
+ new bytes_to_i(bytes)
118
+ end
119
+
120
+ private
121
+
122
+ def bytes_to_i(bytes)
123
+ bytes.unpack('C*').inject { |value, i| value * 256 | i }
124
+ end
125
+ end
126
+ end
127
+
128
+ UUID::Nil = UUID.new(0)
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'uuid'
data/spec/uuid_spec.rb ADDED
@@ -0,0 +1,87 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe UUID, '.uuid4' do
4
+ it 'generates a new UUID' do
5
+ UUID.uuid4.should be_a_kind_of(UUID)
6
+ UUID.uuid4.should_not == UUID.new
7
+ end
8
+
9
+ it 'generates version 4 UUIDs' do
10
+ UUID.uuid4.to_s.should =~ /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
11
+ UUID.uuid4.version.should == 4
12
+ end
13
+
14
+ it 'generates UUIDs of the RFC 4122 variant' do
15
+ UUID.uuid4.variant.should == UUID::RFC_4122
16
+ end
17
+ end
18
+
19
+ describe UUID, '.parse' do
20
+ it 'creates a UUID from a valid string representation when passed one' do
21
+ id = UUID.uuid4
22
+ [id.to_s, id.hex, id.urn].each do |s|
23
+ UUID.parse(s).should == id
24
+ end
25
+ UUID.parse(UUID::Nil.to_s).should == UUID::Nil
26
+ end
27
+
28
+ it 'raises an ArgumentError when passed a string it cannot parse into a UUID' do
29
+ lambda { UUID.parse '<garbage>' }.should raise_error(ArgumentError)
30
+ end
31
+ end
32
+
33
+ describe UUID, '#initialize' do
34
+ it 'creates a UUID from an Integer when passed one that is within range for a UUID' do
35
+ id = UUID.uuid4
36
+ UUID.new(id.to_i).should == id
37
+ UUID.new(0).should == UUID::Nil
38
+ end
39
+
40
+ it 'raises an ArgumentError when passed a parameter that cannot be coerced into an Integer' do
41
+ lambda { UUID.new '<garbage>' }.should raise_error(ArgumentError)
42
+ end
43
+
44
+ it 'raises a RangeError when passed an Integer that is out of range for a UUID' do
45
+ lambda { UUID.new -1 }.should raise_error(RangeError)
46
+ lambda { UUID.new(1 << 128) }.should raise_error(RangeError)
47
+ end
48
+ end
49
+
50
+ describe UUID, '#to_i' do
51
+ it 'returns an unsigned integer representation of the UUID' do
52
+ i = UUID.uuid4.to_i
53
+ i.should be_a_kind_of(Integer)
54
+ i.should >= 0
55
+ i.should < (1 << 128)
56
+ end
57
+ end
58
+
59
+ delimited_hex_format = /[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}/
60
+
61
+ describe UUID, '#to_s' do
62
+ it 'returns a UUID string in delimited hex format (i.e., xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)' do
63
+ UUID.new.to_s.should =~ /^#{delimited_hex_format}$/
64
+ end
65
+ end
66
+
67
+ describe UUID, '#hex' do
68
+ it 'returns a UUID string in undelimited hex format (i.e., xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)' do
69
+ UUID.new.hex.should =~ /^[0-9a-fA-F]{8}([0-9a-fA-F]{4}){3}[0-9a-fA-F]{12}$/
70
+ end
71
+ end
72
+
73
+ describe UUID, '#urn' do
74
+ it 'returns a UUID string in delimited hex format with a urn prefix (i.e., urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)' do
75
+ UUID.new.urn.should =~ /^urn:uuid:#{delimited_hex_format}$/
76
+ end
77
+ end
78
+
79
+ describe 'UUID::Nil' do
80
+ it 'has a string representation (#to_s) of 00000000-0000-0000-0000-000000000000' do
81
+ UUID::Nil.to_s.should == '00000000-0000-0000-0000-000000000000'
82
+ end
83
+
84
+ it 'has an integer value (#to_i) of 0' do
85
+ UUID::Nil.to_i.should == 0
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gabe-uuid
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.3"
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Boyer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Simple UUID implementation, as per RFC 4122
17
+ email: gboyer@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - LICENSE
26
+ - README.markdown
27
+ - Rakefile
28
+ - spec/spec_helper.rb
29
+ - spec/uuid_spec.rb
30
+ - lib/uuid.rb
31
+ - lib/compat/securerandom.rb
32
+ has_rdoc: false
33
+ homepage: http://github.com/gabe/uuid/
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.8.6
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.2.0
55
+ signing_key:
56
+ specification_version: 2
57
+ summary: Simple UUID implementation
58
+ test_files: []
59
+