usesguid 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.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ = 1.0.0 2009-11-13
2
+
3
+ * Ported from a plugin to gem by C. Jason Harrelson (midas)
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2005 Assembla, Inc.
2
+ Portions Copyright (c) 2008 Intuit
3
+
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:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
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.
data/README.rdoc ADDED
@@ -0,0 +1,50 @@
1
+ == Changes in this Fork
2
+
3
+ * Converted to Gem ported by C. Jason Harrelson (midas)
4
+
5
+
6
+ == DETAILS
7
+
8
+ This plugin for ActiveRecord makes the "ID" field into a URL-safe GUID
9
+ It is a mashup by Andy Singleton <andy@assembla.com> that includes
10
+ * the UUID class from Bob Aman.
11
+ * the plugin skeleton from Demetrius Nunes
12
+ * the 22 character URL-safe format from Andy Singleton
13
+ You can get standard 36 char UUID formats instead
14
+ TODO: Auto-detect a character ID field and use a GUID in this case (DRY principle)
15
+
16
+ It has been extended by Brian Morearty with:
17
+ * the addition of a mysql_create function (configurable with a guid_generator accessor)
18
+ for much better performance
19
+ * id assignment is now done before_create instead of after_initialize, to more closely
20
+ mimic the default Rails behavior of assigning an id upon save.
21
+
22
+ This library is free software; you can redistribute it and/or modify it
23
+ under the terms of the MIT license.
24
+
25
+
26
+ == TO USE
27
+
28
+ sudo gem install usesguid
29
+
30
+ In Rails environment file:
31
+
32
+ config.gem "usesguid"
33
+
34
+ define ID as char(22)
35
+ call "usesguid" in ActiveRecord class declaration, like:
36
+
37
+ class Mymodel < ActiveRecord::Base
38
+ usesguid
39
+ end
40
+
41
+ if your ID field is not called "ID", call "usesguid :column =>'IdColumnName' "
42
+
43
+ If you use MySQL as your database, you can make guid generation much faster
44
+ by putting this in config/environment.rb:
45
+
46
+ ActiveRecord::Base.guid_generator = :mysql
47
+
48
+ == TODO
49
+
50
+ * Add tests
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "usesguid"
8
+ gem.summary = %Q{A much faster version of the usesguid plugin for Rails}
9
+ gem.description = %Q{A much faster version of the usesguid plugin for Rails (uses MySQL to generate GUIDs)}
10
+ gem.email = "jason@lookforwardenterprises.com"
11
+ gem.homepage = "http://github.com/midas/usesguid"
12
+ gem.authors = ["Brian Morearty","Demetrio Nunes","Robert Aman","C. Jason Harrelson(midas)"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "usesguid #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,52 @@
1
+ # from Demetrio Nunes
2
+ # Modified by Andy Singleton to use different GUID generator
3
+ # Further modified by Brian Morearty to:
4
+ # 1. optionally use the MySQL GUID generator
5
+ # 2. respect the "column" option
6
+ # 3. set the id before create instead of after initialize
7
+ #
8
+ # MIT License
9
+
10
+ #require 'uuid22'
11
+ #require 'uuid_mysql'
12
+
13
+ module Usesguid
14
+ module ActiveRecordExtensions
15
+
16
+ #def self.append_features( base )
17
+ def self.included( base )
18
+ super
19
+ base.extend( ClassMethods )
20
+ end
21
+
22
+
23
+ module ClassMethods
24
+
25
+ # guid_generator can be :timestamp or :mysql
26
+ def guid_generator=(generator); class_eval { @guid_generator = generator } end
27
+ def guid_generator; class_eval { @guid_generator || :timestamp } end
28
+
29
+ def usesguid(options = {})
30
+
31
+ class_eval do
32
+ set_primary_key options[:column] if options[:column]
33
+
34
+ before_create :assign_guid
35
+
36
+ # Give this record a guid id. Public method so people can call it before save if necessary.
37
+ def assign_guid
38
+ self[self.class.primary_key] ||= case ActiveRecord::Base.guid_generator
39
+ when :mysql then UUID.mysql_create(self.connection)
40
+ when :timestamp then UUID.timestamp_create()
41
+ else raise "Unrecognized guid generator '#{ActiveRecord::Base.guid_generator.to_s}'"
42
+ end.to_s22
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2005 Assembla, Inc.
2
+ # MIT License
3
+
4
+ #require 'uuidtools'
5
+
6
+ class UUID
7
+
8
+ # Make an array of 64 URL-safe characters
9
+ @@chars64=('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['-','_']
10
+ #return a 22 byte URL-safe string, encoded six bits at a time using 64 characters
11
+ def to_s22
12
+ integer=self.to_i
13
+ rval=''
14
+ 22.times do
15
+ c=(integer & 0x3F)
16
+ rval+=@@chars64[c]
17
+ integer =integer >> 6
18
+ end
19
+ return rval.reverse
20
+ end
21
+ # Create a new UUID from a 22char string
22
+ def self.parse22(s)
23
+ # get the integer representation
24
+ integer=0
25
+ s.each_byte {|c|
26
+ integer = integer << 6
27
+ pos=@@chars64.index(c.chr)
28
+ integer+=pos
29
+ }
30
+
31
+ time_low = (integer >> 96) & 0xFFFFFFFF
32
+ time_mid = (integer >> 80) & 0xFFFF
33
+ time_hi_and_version = (integer >> 64) & 0xFFFF
34
+ clock_seq_hi_and_reserved = (integer >> 56) & 0xFF
35
+ clock_seq_low = (integer >> 48) & 0xFF
36
+ nodes = []
37
+ for i in 0..5 do
38
+ nodes << ((integer >> (40 - (i * 8))) & 0xFF)
39
+ end
40
+ return new(time_low, time_mid, time_hi_and_version,
41
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2008 Intuit
2
+ # Written by Brian Morearty
3
+ # MIT License
4
+
5
+ #require 'uuidtools'
6
+
7
+ class UUID
8
+
9
+ BUCKET_SIZE = 50
10
+ @@guid_bucket_mutex = Mutex.new
11
+ @@guid_bucket = nil
12
+
13
+ # We'll retrieve a bunch of guids at a time to reduce the # of DB round-trips.
14
+ # If the guid bucket is empty, re-fill it by calling MySQL. Then return a guid.
15
+ def UUID.mysql_create(connection=ActiveRecord::Base.connection)
16
+ raise "UUID.mysql_create only works with MySQL" unless connection.adapter_name.downcase =~ /mysql/
17
+ @@guid_bucket_mutex.synchronize do
18
+ if @@guid_bucket.blank?
19
+ uuid_functions = Array.new(BUCKET_SIZE, "UUID()")
20
+ @@guid_bucket = connection.execute("SELECT #{uuid_functions.join(',')}").fetch_row
21
+ end
22
+ # My tests show shift is much faster than slice!(0), pop, or delete_at(0)
23
+ parse @@guid_bucket.shift
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,561 @@
1
+ #--
2
+ # Copyright (c) 2005 Robert Aman
3
+ #
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:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
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.
22
+ #++
23
+
24
+ UUID_TOOLS_VERSION = "0.1.3"
25
+
26
+ $:.unshift(File.dirname(__FILE__))
27
+
28
+ require 'uri'
29
+ require 'time'
30
+ require 'thread'
31
+ require 'digest/sha1'
32
+ require 'digest/md5'
33
+
34
+ # Because it's impossible to hype a UUID generator on its genuine merits,
35
+ # I give you... Really bad ASCII art in the comments:
36
+ #
37
+ #
38
+ # \
39
+ # /
40
+ # +
41
+ # ]
42
+ # ]
43
+ # |
44
+ # /
45
+ # Mp___
46
+ # `~0NNp,
47
+ # __ggM'
48
+ # g0M~"`
49
+ # ]0M*-
50
+ #
51
+ # ___
52
+ # _g000M00g,
53
+ # j0M~ ~M&
54
+ # j0M" ~N,
55
+ # j0P M&
56
+ # jM 1
57
+ # j0 ]1
58
+ # .0P 0,
59
+ # 00' M&
60
+ # 0M ]0L
61
+ # ]0f ___ M0
62
+ # M0NN0M00MMM~"'M 0&
63
+ # `~ ~0 ]0,
64
+ # ]M ]0&
65
+ # M& M0,
66
+ # ____gp_ M& M0_
67
+ # __p0MPM8MM&_ M/ ^0&_
68
+ # gN"` M0N_j0, MM&__
69
+ # _gF `~M0P` __ M00g
70
+ # g0' gM0&, ~M0&
71
+ # _pM` 0, ]M1 "00&
72
+ # _00 /g1MMgj01 ]0MI
73
+ # _0F t"M,7MMM 00I
74
+ # g0' _ N&j& 40'
75
+ # g0' _p0Mq_ ' N0QQNM#g,
76
+ # 0' _g0000000g__ ~M@MMM000g
77
+ # f _jM00@` ~M0000Mgppg, "P00&
78
+ # | g000~ `~M000000&_ ~0&
79
+ # ]M _M00F "00MM` ~#&
80
+ # `0L m000F #E "0f
81
+ # 9r j000M` 40, 00
82
+ # ]0g_ j00M` ^M0MNggp#gqpg M0&
83
+ # ~MPM0f ~M000000000g_ ,_ygg&M00f
84
+ # `~~~M00000000000000
85
+ # `M0000000000f
86
+ # ~@@@MF~`
87
+ #
88
+ #
89
+
90
+ #= uuidtools.rb
91
+ #
92
+ # UUIDTools was designed to be a simple library for generating any
93
+ # of the various types of UUIDs. It conforms to RFC 4122 whenever
94
+ # possible.
95
+ #
96
+ #== Example
97
+ # UUID.md5_create(UUID_DNS_NAMESPACE, "www.widgets.com")
98
+ # => #<UUID:0x287576 UUID:3d813cbb-47fb-32ba-91df-831e1593ac29>
99
+ # UUID.sha1_create(UUID_DNS_NAMESPACE, "www.widgets.com")
100
+ # => #<UUID:0x2a0116 UUID:21f7f8de-8051-5b89-8680-0195ef798b6a>
101
+ # UUID.timestamp_create
102
+ # => #<UUID:0x2adfdc UUID:64a5189c-25b3-11da-a97b-00c04fd430c8>
103
+ # UUID.random_create
104
+ # => #<UUID:0x19013a UUID:984265dc-4200-4f02-ae70-fe4f48964159>
105
+ class UUID
106
+ @@last_timestamp = nil
107
+ @@last_node_id = nil
108
+ @@last_clock_sequence = nil
109
+ @@state_file = nil
110
+ @@mutex = Mutex.new
111
+
112
+ def initialize(time_low, time_mid, time_hi_and_version,
113
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
114
+ unless time_low >= 0 && time_low < 4294967296
115
+ raise ArgumentError,
116
+ "Expected unsigned 32-bit number for time_low, got #{time_low}."
117
+ end
118
+ unless time_mid >= 0 && time_mid < 65536
119
+ raise ArgumentError,
120
+ "Expected unsigned 16-bit number for time_mid, got #{time_mid}."
121
+ end
122
+ unless time_hi_and_version >= 0 && time_hi_and_version < 65536
123
+ raise ArgumentError,
124
+ "Expected unsigned 16-bit number for time_hi_and_version, " +
125
+ "got #{time_hi_and_version}."
126
+ end
127
+ unless clock_seq_hi_and_reserved >= 0 && clock_seq_hi_and_reserved < 256
128
+ raise ArgumentError,
129
+ "Expected unsigned 8-bit number for clock_seq_hi_and_reserved, " +
130
+ "got #{clock_seq_hi_and_reserved}."
131
+ end
132
+ unless clock_seq_low >= 0 && clock_seq_low < 256
133
+ raise ArgumentError,
134
+ "Expected unsigned 8-bit number for clock_seq_low, " +
135
+ "got #{clock_seq_low}."
136
+ end
137
+ unless nodes.respond_to? :size
138
+ raise ArgumentError,
139
+ "Expected nodes to respond to :size."
140
+ end
141
+ unless nodes.size == 6
142
+ raise ArgumentError,
143
+ "Expected nodes to have size of 6."
144
+ end
145
+ for node in nodes
146
+ unless node >= 0 && node < 256
147
+ raise ArgumentError,
148
+ "Expected unsigned 8-bit number for each node, " +
149
+ "got #{node}."
150
+ end
151
+ end
152
+ @time_low = time_low
153
+ @time_mid = time_mid
154
+ @time_hi_and_version = time_hi_and_version
155
+ @clock_seq_hi_and_reserved = clock_seq_hi_and_reserved
156
+ @clock_seq_low = clock_seq_low
157
+ @nodes = nodes
158
+ end
159
+
160
+ attr_accessor :time_low
161
+ attr_accessor :time_mid
162
+ attr_accessor :time_hi_and_version
163
+ attr_accessor :clock_seq_hi_and_reserved
164
+ attr_accessor :clock_seq_low
165
+ attr_accessor :nodes
166
+
167
+ # Parses a UUID from a string.
168
+ def UUID.parse(uuid_string)
169
+ unless uuid_string.kind_of? String
170
+ raise ArgumentError,
171
+ "Expected String, got #{uuid_string.class.name} instead."
172
+ end
173
+ uuid_components = uuid_string.downcase.scan(
174
+ Regexp.new("^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-" +
175
+ "([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})$")).first
176
+ raise ArgumentError, "Invalid UUID format." if uuid_components.nil?
177
+ time_low = uuid_components[0].to_i(16)
178
+ time_mid = uuid_components[1].to_i(16)
179
+ time_hi_and_version = uuid_components[2].to_i(16)
180
+ clock_seq_hi_and_reserved = uuid_components[3].to_i(16)
181
+ clock_seq_low = uuid_components[4].to_i(16)
182
+ nodes = []
183
+ for i in 0..5
184
+ nodes << uuid_components[5][(i * 2)..(i * 2) + 1].to_i(16)
185
+ end
186
+ return UUID.new(time_low, time_mid, time_hi_and_version,
187
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
188
+ end
189
+
190
+ # Parses a UUID from a raw byte string.
191
+ def UUID.parse_raw(raw_string)
192
+ unless raw_string.kind_of? String
193
+ raise ArgumentError,
194
+ "Expected String, got #{raw_string.class.name} instead."
195
+ end
196
+ integer = UUID.convert_byte_string_to_int(raw_string)
197
+
198
+ time_low = (integer >> 96) & 0xFFFFFFFF
199
+ time_mid = (integer >> 80) & 0xFFFF
200
+ time_hi_and_version = (integer >> 64) & 0xFFFF
201
+ clock_seq_hi_and_reserved = (integer >> 56) & 0xFF
202
+ clock_seq_low = (integer >> 48) & 0xFF
203
+ nodes = []
204
+ for i in 0..5
205
+ nodes << ((integer >> (40 - (i * 8))) & 0xFF)
206
+ end
207
+ return UUID.new(time_low, time_mid, time_hi_and_version,
208
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
209
+ end
210
+
211
+ # Creates a UUID from a random value.
212
+ def UUID.random_create()
213
+ new_uuid = UUID.parse_raw(UUID.true_random)
214
+ new_uuid.time_hi_and_version &= 0x0FFF
215
+ new_uuid.time_hi_and_version |= (4 << 12)
216
+ new_uuid.clock_seq_hi_and_reserved &= 0x3F
217
+ new_uuid.clock_seq_hi_and_reserved |= 0x80
218
+ return new_uuid
219
+ end
220
+
221
+ # Creates a UUID from a timestamp.
222
+ def UUID.timestamp_create(timestamp=nil)
223
+ # We need a lock here to prevent two threads from ever
224
+ # getting the same timestamp.
225
+ @@mutex.synchronize do
226
+ # Always use GMT to generate UUIDs.
227
+ if timestamp.nil?
228
+ gmt_timestamp = Time.now.gmtime
229
+ else
230
+ gmt_timestamp = timestamp.gmtime
231
+ end
232
+ # Convert to 100 nanosecond blocks
233
+ gmt_timestamp_100_nanoseconds = (gmt_timestamp.tv_sec * 10000000) +
234
+ (gmt_timestamp.tv_usec * 10) + 0x01B21DD213814000
235
+ nodes = UUID.get_mac_address.split(":").collect do |octet|
236
+ octet.to_i(16)
237
+ end
238
+ node_id = 0
239
+ for i in 0..5
240
+ node_id += (nodes[i] << (40 - (i * 8)))
241
+ end
242
+ clock_sequence = @@last_clock_sequence
243
+ if clock_sequence.nil?
244
+ clock_sequence = UUID.convert_byte_string_to_int(UUID.true_random)
245
+ end
246
+ if @@last_node_id != nil && @@last_node_id != node_id
247
+ # The node id has changed. Change the clock id.
248
+ clock_sequence = UUID.convert_byte_string_to_int(UUID.true_random)
249
+ elsif @@last_timestamp != nil &&
250
+ gmt_timestamp_100_nanoseconds < @@last_timestamp
251
+ clock_sequence = clock_sequence + 1
252
+ end
253
+ @@last_timestamp = gmt_timestamp_100_nanoseconds
254
+ @@last_node_id = node_id
255
+ @@last_clock_sequence = clock_sequence
256
+
257
+ time_low = gmt_timestamp_100_nanoseconds & 0xFFFFFFFF
258
+ time_mid = ((gmt_timestamp_100_nanoseconds >> 32) & 0xFFFF)
259
+ time_hi_and_version = ((gmt_timestamp_100_nanoseconds >> 48) & 0x0FFF)
260
+ time_hi_and_version |= (1 << 12)
261
+ clock_seq_low = clock_sequence & 0xFF;
262
+ clock_seq_hi_and_reserved = (clock_sequence & 0x3F00) >> 8
263
+ clock_seq_hi_and_reserved |= 0x80
264
+
265
+ return UUID.new(time_low, time_mid, time_hi_and_version,
266
+ clock_seq_hi_and_reserved, clock_seq_low, nodes)
267
+ end
268
+ end
269
+
270
+ # Creates a UUID using the MD5 hash. (Version 3)
271
+ def UUID.md5_create(namespace, name)
272
+ return UUID.create_from_hash(Digest::MD5, namespace, name)
273
+ end
274
+
275
+ # Creates a UUID using the SHA1 hash. (Version 5)
276
+ def UUID.sha1_create(namespace, name)
277
+ return UUID.create_from_hash(Digest::SHA1, namespace, name)
278
+ end
279
+
280
+ # This method applies only to version 1 UUIDs.
281
+ # Checks if the node ID was generated from a random number
282
+ # or from an IEEE 802 address (MAC address).
283
+ # Always returns false for UUIDs that aren't version 1.
284
+ # This should not be confused with version 4 UUIDs where
285
+ # more than just the node id is random.
286
+ def random_node_id?
287
+ return false if self.version != 1
288
+ return ((self.nodes.first & 0x01) == 1)
289
+ end
290
+
291
+ # Returns true if this UUID is the
292
+ # nil UUID (00000000-0000-0000-0000-000000000000).
293
+ def nil_uuid?
294
+ return false if self.time_low != 0
295
+ return false if self.time_mid != 0
296
+ return false if self.time_hi_and_version != 0
297
+ return false if self.clock_seq_hi_and_reserved != 0
298
+ return false if self.clock_seq_low != 0
299
+ self.nodes.each do |node|
300
+ return false if node != 0
301
+ end
302
+ return true
303
+ end
304
+
305
+ # Returns the UUID version type.
306
+ # Possible values:
307
+ # 1 - Time-based with unique or random host identifier
308
+ # 2 - DCE Security version (with POSIX UIDs)
309
+ # 3 - Name-based (MD5 hash)
310
+ # 4 - Random
311
+ # 5 - Name-based (SHA-1 hash)
312
+ def version
313
+ return (time_hi_and_version >> 12)
314
+ end
315
+
316
+ # Returns the UUID variant.
317
+ # Possible values:
318
+ # 0b000 - Reserved, NCS backward compatibility.
319
+ # 0b100 - The variant specified in this document.
320
+ # 0b110 - Reserved, Microsoft Corporation backward compatibility.
321
+ # 0b111 - Reserved for future definition.
322
+ def variant
323
+ variant_raw = (clock_seq_hi_and_reserved >> 5)
324
+ result = nil
325
+ if (variant_raw >> 2) == 0
326
+ result = 0x000
327
+ elsif (variant_raw >> 1) == 2
328
+ result = 0x100
329
+ else
330
+ result = variant_raw
331
+ end
332
+ return (result >> 6)
333
+ end
334
+
335
+ # Returns true if this UUID is valid.
336
+ def valid?
337
+ if [0b000, 0b100, 0b110, 0b111].include?(self.variant) &&
338
+ (1..5).include?(self.version)
339
+ return true
340
+ else
341
+ return false
342
+ end
343
+ end
344
+
345
+ # Returns the IEEE 802 address used to generate this UUID or
346
+ # nil if a MAC address was not used.
347
+ def mac_address
348
+ return nil if self.version != 1
349
+ return nil if self.random_node_id?
350
+ return (self.nodes.collect do |node|
351
+ sprintf("%2.2x", node)
352
+ end).join(":")
353
+ end
354
+
355
+ # Returns the timestamp used to generate this UUID
356
+ def timestamp
357
+ return nil if self.version != 1
358
+ gmt_timestamp_100_nanoseconds = 0
359
+ gmt_timestamp_100_nanoseconds +=
360
+ ((self.time_hi_and_version & 0x0FFF) << 48)
361
+ gmt_timestamp_100_nanoseconds += (self.time_mid << 32)
362
+ gmt_timestamp_100_nanoseconds += self.time_low
363
+ return Time.at(
364
+ (gmt_timestamp_100_nanoseconds - 0x01B21DD213814000) / 10000000.0)
365
+ end
366
+
367
+ # Compares two UUIDs lexically
368
+ def <=>(other_uuid)
369
+ check = self.time_low <=> other_uuid.time_low
370
+ return check if check != 0
371
+ check = self.time_mid <=> other_uuid.time_mid
372
+ return check if check != 0
373
+ check = self.time_hi_and_version <=> other_uuid.time_hi_and_version
374
+ return check if check != 0
375
+ check = self.clock_seq_hi_and_reserved <=>
376
+ other_uuid.clock_seq_hi_and_reserved
377
+ return check if check != 0
378
+ check = self.clock_seq_low <=> other_uuid.clock_seq_low
379
+ return check if check != 0
380
+ for i in 0..5
381
+ if (self.nodes[i] < other_uuid.nodes[i])
382
+ return -1
383
+ end
384
+ if (self.nodes[i] > other_uuid.nodes[i])
385
+ return 1
386
+ end
387
+ end
388
+ return 0
389
+ end
390
+
391
+ # Returns a representation of the object's state
392
+ def inspect
393
+ return "#<UUID:0x#{self.object_id.to_s(16)} UUID:#{self.to_s}>"
394
+ end
395
+
396
+ # Returns the hex digest of the UUID object.
397
+ def hexdigest
398
+ return self.to_i.to_s(16)
399
+ end
400
+
401
+ # Returns the raw bytes that represent this UUID.
402
+ def raw
403
+ return UUID.convert_int_to_byte_string(self.to_i, 16)
404
+ end
405
+
406
+ # Returns a string representation for this UUID.
407
+ def to_s
408
+ result = sprintf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", @time_low, @time_mid,
409
+ @time_hi_and_version, @clock_seq_hi_and_reserved, @clock_seq_low);
410
+ for i in 0..5
411
+ result << sprintf("%2.2x", @nodes[i])
412
+ end
413
+ return result
414
+ end
415
+
416
+ # Returns an integer representation for this UUID.
417
+ def to_i
418
+ bytes = (time_low << 96) + (time_mid << 80) +
419
+ (time_hi_and_version << 64) + (clock_seq_hi_and_reserved << 56) +
420
+ (clock_seq_low << 48)
421
+ for i in 0..5
422
+ bytes += (nodes[i] << (40 - (i * 8)))
423
+ end
424
+ return bytes
425
+ end
426
+
427
+ # Returns a URI for this UUID.
428
+ def to_uri
429
+ return URI.parse(self.to_uri_string)
430
+ end
431
+
432
+ # Returns a URI string for this UUID.
433
+ def to_uri_string
434
+ return "urn:uuid:#{self.to_s}"
435
+ end
436
+
437
+ def UUID.create_from_hash(hash_class, namespace, name) #:nodoc:
438
+ if hash_class == Digest::MD5
439
+ version = 3
440
+ elsif hash_class == Digest::SHA1
441
+ version = 5
442
+ else
443
+ raise ArgumentError,
444
+ "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
445
+ end
446
+ hash = hash_class.new
447
+ hash.update(namespace.raw)
448
+ hash.update(name)
449
+ hash_string = hash.to_s[0..31]
450
+ new_uuid = UUID.parse("#{hash_string[0..7]}-#{hash_string[8..11]}-" +
451
+ "#{hash_string[12..15]}-#{hash_string[16..19]}-#{hash_string[20..31]}")
452
+
453
+ new_uuid.time_hi_and_version &= 0x0FFF
454
+ new_uuid.time_hi_and_version |= (version << 12)
455
+ new_uuid.clock_seq_hi_and_reserved &= 0x3F
456
+ new_uuid.clock_seq_hi_and_reserved |= 0x80
457
+ return new_uuid
458
+ end
459
+
460
+ # Returns the MAC address of the current computer's network card.
461
+ # Returns nil if a MAC address could not be found.
462
+ def UUID.get_mac_address #:nodoc:
463
+ if RUBY_PLATFORM =~ /win/ && !(RUBY_PLATFORM =~ /darwin/)
464
+ begin
465
+ ifconfig_output = `ipconfig /all`
466
+ mac_addresses = ifconfig_output.scan(
467
+ Regexp.new("(#{(["[0-9A-F]{2}"] * 6).join("-")})"))
468
+ if mac_addresses.size > 0
469
+ return mac_addresses.first.first.downcase.gsub(/-/, ":")
470
+ end
471
+ rescue
472
+ end
473
+ else
474
+ begin
475
+ ifconfig_output = `ifconfig`
476
+ mac_addresses = ifconfig_output.scan(
477
+ Regexp.new("ether (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"))
478
+ if mac_addresses.size == 0
479
+ ifconfig_output = `ifconfig | grep HWaddr | cut -c39-`
480
+ mac_addresses = ifconfig_output.scan(
481
+ Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"))
482
+ end
483
+ if mac_addresses.size == 0
484
+ ifconfig_output = `/sbin/ifconfig`
485
+ mac_addresses = ifconfig_output.scan(
486
+ Regexp.new("ether (#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"))
487
+ end
488
+ if mac_addresses.size == 0
489
+ ifconfig_output = `/sbin/ifconfig | grep HWaddr | cut -c39-`
490
+ mac_addresses = ifconfig_output.scan(
491
+ Regexp.new("(#{(["[0-9a-fA-F]{2}"] * 6).join(":")})"))
492
+ end
493
+ if mac_addresses.size > 0
494
+ return mac_addresses.first.first
495
+ end
496
+ rescue
497
+ end
498
+ end
499
+ end
500
+
501
+ # Returns 128 bits of highly unpredictable data.
502
+ # The random number generator isn't perfect, but it's
503
+ # much, much better than the built-in pseudorandom number generators.
504
+ def UUID.true_random #:nodoc:
505
+ require 'benchmark'
506
+ hash = Digest::SHA1.new
507
+ performance = Benchmark.measure do
508
+ hash.update(rand.to_s)
509
+ hash.update(srand.to_s)
510
+ hash.update(rand.to_s)
511
+ hash.update(srand.to_s)
512
+ hash.update(Time.now.to_s)
513
+ hash.update(rand.to_s)
514
+ hash.update(self.object_id.to_s)
515
+ hash.update(rand.to_s)
516
+ hash.update(hash.object_id.to_s)
517
+ hash.update(self.methods.inspect)
518
+ begin
519
+ random_device = nil
520
+ if File.exists? "/dev/urandom"
521
+ random_device = File.open "/dev/urandom", "r"
522
+ elsif File.exists? "/dev/random"
523
+ random_device = File.open "/dev/random", "r"
524
+ end
525
+ hash.update(random_device.read(20)) if random_device != nil
526
+ rescue
527
+ end
528
+ begin
529
+ srand(hash.to_s.to_i(16) >> 128)
530
+ rescue
531
+ end
532
+ hash.update(rand.to_s)
533
+ hash.update(UUID.true_random) if (rand(2) == 0)
534
+ end
535
+ hash.update(performance.real.to_s)
536
+ hash.update(performance.inspect)
537
+ return UUID.convert_int_to_byte_string(hash.to_s[4..35].to_i(16), 16)
538
+ end
539
+
540
+ def UUID.convert_int_to_byte_string(integer, size) #:nodoc:
541
+ byte_string = ""
542
+ for i in 0..(size - 1)
543
+ byte_string << ((integer >> (((size - 1) - i) * 8)) & 0xFF)
544
+ end
545
+ return byte_string
546
+ end
547
+
548
+ def UUID.convert_byte_string_to_int(byte_string) #:nodoc:
549
+ integer = 0
550
+ size = byte_string.size
551
+ for i in 0..(size - 1)
552
+ integer += (byte_string[i] << (((size - 1) - i) * 8))
553
+ end
554
+ return integer
555
+ end
556
+ end
557
+
558
+ UUID_DNS_NAMESPACE = UUID.parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
559
+ UUID_URL_NAMESPACE = UUID.parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
560
+ UUID_OID_NAMESPACE = UUID.parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
561
+ UUID_X500_NAMESPACE = UUID.parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
data/lib/usesguid.rb ADDED
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'usesguid/active_record_extensions'
5
+ require 'usesguid/uuid22'
6
+ require 'usesguid/uuid_mysql'
7
+ require 'usesguid/uuidtools'
8
+
9
+ module Usesguid
10
+ VERSION = '1.0.0'
11
+ end
12
+
13
+ ActiveRecord::Base.class_eval { include Usesguid::ActiveRecordExtensions } if defined?( ActiveRecord::Base )
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/usesguid.rb'}"
9
+ puts "Loading usesguid gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'usesguid'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Usesguid" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/usesguid.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{usesguid}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Morearty", "Demetrio Nunes", "Robert Aman", "C. Jason Harrelson(midas)"]
12
+ s.date = %q{2009-11-13}
13
+ s.description = %q{A much faster version of the usesguid plugin for Rails (uses MySQL to generate GUIDs)}
14
+ s.email = %q{jason@lookforwardenterprises.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "History.txt",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/usesguid.rb",
26
+ "lib/usesguid/active_record_extensions.rb",
27
+ "lib/usesguid/uuid22.rb",
28
+ "lib/usesguid/uuid_mysql.rb",
29
+ "lib/usesguid/uuidtools.rb",
30
+ "script/console",
31
+ "spec/spec_helper.rb",
32
+ "spec/usesguid_spec.rb",
33
+ "usesguid.gemspec"
34
+ ]
35
+ s.homepage = %q{http://github.com/midas/usesguid}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{A much faster version of the usesguid plugin for Rails}
40
+ s.test_files = [
41
+ "spec/spec_helper.rb",
42
+ "spec/usesguid_spec.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<rspec>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<rspec>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<rspec>, [">= 0"])
56
+ end
57
+ end
58
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: usesguid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Morearty
8
+ - Demetrio Nunes
9
+ - Robert Aman
10
+ - C. Jason Harrelson(midas)
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+
15
+ date: 2009-11-13 00:00:00 -06:00
16
+ default_executable:
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: rspec
20
+ type: :development
21
+ version_requirement:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: "0"
27
+ version:
28
+ description: A much faster version of the usesguid plugin for Rails (uses MySQL to generate GUIDs)
29
+ email: jason@lookforwardenterprises.com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files:
35
+ - LICENSE
36
+ - README.rdoc
37
+ files:
38
+ - History.txt
39
+ - LICENSE
40
+ - README.rdoc
41
+ - Rakefile
42
+ - VERSION
43
+ - lib/usesguid.rb
44
+ - lib/usesguid/active_record_extensions.rb
45
+ - lib/usesguid/uuid22.rb
46
+ - lib/usesguid/uuid_mysql.rb
47
+ - lib/usesguid/uuidtools.rb
48
+ - script/console
49
+ - spec/spec_helper.rb
50
+ - spec/usesguid_spec.rb
51
+ - usesguid.gemspec
52
+ has_rdoc: true
53
+ homepage: http://github.com/midas/usesguid
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: A much faster version of the usesguid plugin for Rails
80
+ test_files:
81
+ - spec/spec_helper.rb
82
+ - spec/usesguid_spec.rb