Class: SdbDal::UUID
- Inherits:
-
Object
- Object
- SdbDal::UUID
- Defined in:
- lib/sdb_dal/uuid.rb
Constant Summary collapse
- VERSION =
'2.0.1'
- CLOCK_MULTIPLIER =
Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
10000000
- CLOCK_GAPS =
Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
100000
- VERSION_CLOCK =
Version number stamped into the UUID to identify it as time-based.
0x0100
- FORMATS =
Formats supported by the UUID generator.
:default
-
Produces 36 characters, including hyphens separating the UUID value parts
:compact
-
Produces a 32 digits (hexadecimal) value with no hyphens
:urn
-
Adds the prefix
urn:uuid:
to the default format
{ :compact => '%08x%04x%04x%04x%012x', :default => '%08x-%04x-%04x-%04x-%012x', :urn => 'urn:uuid:%08x-%04x-%04x-%04x-%012x', }
- STATE_FILE_FORMAT =
MAC address (48 bits), sequence number and last clock
'SLLQ'
Class Method Summary collapse
-
.generate(format = :default) ⇒ Object
Generates a new UUID string using
format
. -
.mode ⇒ Object
The access mode of the state file.
-
.state_file(mode = 0644) ⇒ Object
Creates an empty state file in /var/tmp/ruby-uuid or the windows common application data directory using mode 0644.
Instance Method Summary collapse
-
#generate(format = :default) ⇒ Object
Generates a new UUID string using
format
. -
#initialize ⇒ UUID
constructor
Create a new UUID generator.
- #inspect ⇒ Object
-
#next_sequence ⇒ Object
Updates the state file with a new sequence number.
Constructor Details
#initialize ⇒ UUID
Create a new UUID generator. You really only need to do this once.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/sdb_dal/uuid.rb', line 157 def initialize @drift = 0 @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i @mutex = Mutex.new if File.exist?(self.class.state_file) then next_sequence else @mac = Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF fail "Cannot determine MAC address from any available interface, tried with #{Mac.addr}" if @mac == 0 @sequence = rand 0x10000 open_lock 'w' do |io| write_state io end end end |
Class Method Details
.generate(format = :default) ⇒ Object
Generates a new UUID string using format
. See FORMATS for a list of supported formats.
114 115 116 117 |
# File 'lib/sdb_dal/uuid.rb', line 114 def self.generate(format = :default) @uuid ||= new @uuid.generate format end |
.mode ⇒ Object
The access mode of the state file. Set it with state_file.
106 107 108 |
# File 'lib/sdb_dal/uuid.rb', line 106 def self.mode @mode end |
.state_file(mode = 0644) ⇒ Object
Creates an empty state file in /var/tmp/ruby-uuid or the windows common application data directory using mode 0644. Call with a different mode before creating a UUID generator if you want to open access beyond your user by default.
If the default state dir is not writable, UUID falls back to ~/.ruby-uuid.
State files are not portable across machines.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/sdb_dal/uuid.rb', line 128 def self.state_file(mode = 0644) return @state_file if @state_file @mode = mode begin require 'Win32API' csidl_common_appdata = 0x0023 path = 0.chr * 260 get_folder_path = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L') get_folder_path.call 0, csidl_common_appdata, 0, 1, path state_dir = File.join(path.strip) rescue LoadError state_dir = File.join('', 'var', 'tmp') end if File.writable?(state_dir) then @state_file = File.join(state_dir, 'ruby-uuid') else @state_file = File.(File.join('~', '.ruby-uuid')) end @state_file end |
Instance Method Details
#generate(format = :default) ⇒ Object
Generates a new UUID string using format
. See FORMATS for a list of supported formats.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/sdb_dal/uuid.rb', line 178 def generate(format = :default) template = FORMATS[format] raise ArgumentError, "invalid UUID format #{format.inspect}" unless template # The clock must be monotonically increasing. The clock resolution is at # best 100 ns (UUID spec), but practically may be lower (on my setup, # around 1ms). If this method is called too fast, we don't have a # monotonically increasing clock, so the solution is to just wait. # # It is possible for the clock to be adjusted backwards, in which case we # would end up blocking for a long time. When backward clock is detected, # we prevent duplicates by asking for a new sequence number and continue # with the new clock. clock = @mutex.synchronize do clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0 if clock > @last_clock then @drift = 0 @last_clock = clock elsif clock == @last_clock then drift = @drift += 1 if drift < 10000 then @last_clock += 1 else Thread.pass nil end else next_sequence @last_clock = clock end end until clock template % [ clock & 0xFFFFFFFF, (clock >> 32) & 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK), @sequence & 0xFFFF, @mac & 0xFFFFFFFFFFFF ] end |
#inspect ⇒ Object
245 246 247 248 |
# File 'lib/sdb_dal/uuid.rb', line 245 def inspect mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':') "MAC: #{mac} Sequence: #{@sequence}" end |
#next_sequence ⇒ Object
Updates the state file with a new sequence number.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/sdb_dal/uuid.rb', line 225 def next_sequence open_lock 'r+' do |io| @mac, @sequence, @last_clock = read_state(io) io.rewind io.truncate 0 @sequence += 1 write_state io end rescue Errno::ENOENT open_lock 'w' do |io| write_state io end ensure @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i @drift = 0 end |