uuid-2.3.9/0000755000175000017500000000000013312112451013222 5ustar utkarsh2102utkarsh2102uuid-2.3.9/test/0000755000175000017500000000000013312112451014201 5ustar utkarsh2102utkarsh2102uuid-2.3.9/test/test-uuid.rb0000644000175000017500000001021113312112451016444 0ustar utkarsh2102utkarsh2102# encoding: UTF-8 # Author:: Assaf Arkin assaf@labnotes.org # Eric Hodel drbrain@segment7.net # Copyright:: Copyright (c) 2005-2008 Assaf Arkin, Eric Hodel # License:: MIT and/or Creative Commons Attribution-ShareAlike require 'test/unit' require 'rubygems' require 'uuid' require 'mocha' class TestUUID < Test::Unit::TestCase def test_state_file_creation path = UUID.state_file File.delete path if File.exist?(path) UUID.new.generate File.exist?(path) end def test_state_file_creation_mode UUID.class_eval{ @state_file = nil; @mode = nil } UUID.state_file 0666 path = UUID.state_file File.delete path if File.exist?(path) old_umask = File.umask(0022) UUID.new.generate File.umask(old_umask) assert_equal '0666', sprintf('%04o', File.stat(path).mode & 0777) end def test_state_file_specify path = File.join("path", "to", "ruby-uuid") UUID.state_file = path assert_equal path, UUID.state_file end def test_mode_is_set_on_state_file_specify UUID.class_eval{ @state_file = nil; @mode = nil } path = File.join(Dir.tmpdir, "ruby-uuid-test") File.delete path if File.exist?(path) UUID.state_file = path old_umask = File.umask(0022) UUID.new.generate File.umask(old_umask) UUID.class_eval{ @state_file = nil; @mode = nil } assert_equal '0644', sprintf('%04o', File.stat(path).mode & 0777) end def test_with_no_state_file UUID.state_file = false assert !UUID.state_file uuid = UUID.new assert_match(/\A[\da-f]{32}\z/i, uuid.generate(:compact)) seq = uuid.next_sequence assert_equal seq + 1, uuid.next_sequence assert !UUID.state_file end def validate_uuid_generator(uuid) assert_match(/\A[\da-f]{32}\z/i, uuid.generate(:compact)) assert_match(/\A[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i, uuid.generate(:default)) assert_match(/^urn:uuid:[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i, uuid.generate(:urn)) e = assert_raise ArgumentError do uuid.generate :unknown end assert_equal 'invalid UUID format :unknown', e.message end def test_instance_generate uuid = UUID.new validate_uuid_generator(uuid) end def test_class_generate assert_match(/\A[\da-f]{32}\z/i, UUID.generate(:compact)) assert_match(/\A[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i, UUID.generate(:default)) assert_match(/^urn:uuid:[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i, UUID.generate(:urn)) e = assert_raise ArgumentError do UUID.generate :unknown end assert_equal 'invalid UUID format :unknown', e.message end def test_class_validate assert !UUID.validate('') assert UUID.validate('01234567abcd8901efab234567890123'), 'compact' assert UUID.validate('01234567-abcd-8901-efab-234567890123'), 'default' assert UUID.validate('urn:uuid:01234567-abcd-8901-efab-234567890123'), 'urn' assert UUID.validate('01234567ABCD8901EFAB234567890123'), 'compact' assert UUID.validate('01234567-ABCD-8901-EFAB-234567890123'), 'default' assert UUID.validate('urn:uuid:01234567-ABCD-8901-EFAB-234567890123'), 'urn' end def test_monotonic seen = {} uuid_gen = UUID.new 20_000.times do uuid = uuid_gen.generate assert !seen.has_key?(uuid), "UUID repeated" seen[uuid] = true end end def test_same_mac class << foo = UUID.new attr_reader :mac end class << bar = UUID.new attr_reader :mac end assert_equal foo.mac, bar.mac end def test_increasing_sequence class << foo = UUID.new attr_reader :sequence end class << bar = UUID.new attr_reader :sequence end assert_equal foo.sequence + 1, bar.sequence end def test_pseudo_random_mac_address uuid_gen = UUID.new Mac.stubs(:addr).returns "00:00:00:00:00:00" assert uuid_gen.iee_mac_address == 0 [:compact, :default, :urn].each do |format| assert UUID.validate(uuid_gen.generate(format)), format.to_s end validate_uuid_generator(uuid_gen) end end uuid-2.3.9/Rakefile0000644000175000017500000000164713312112451014677 0ustar utkarsh2102utkarsh2102require 'rake/testtask' #require 'rake/rdoctask' spec = Gem::Specification.load(File.expand_path("uuid.gemspec", File.dirname(__FILE__))) desc "Default Task" task :default => :test desc "Run all test cases" Rake::TestTask.new do |test| test.verbose = true test.test_files = ['test/*.rb'] test.warning = true end # Create the documentation. #Rake::RDocTask.new do |rdoc| # rdoc.rdoc_files.include "README.rdoc", "lib/**/*.rb" # rdoc.options = spec.rdoc_options #end desc "Push new release to rubyforge and git tag" task :push do sh "git push" puts "Tagging version #{spec.version} .." sh "git tag v#{spec.version}" sh "git push --tag" puts "Building and pushing gem .." sh "gem build #{spec.name}.gemspec" sh "gem push #{spec.name}-#{spec.version}.gem" end desc "Install #{spec.name} locally" task :install do sh "gem build #{spec.name}.gemspec" sh "gem install #{spec.name}-#{spec.version}.gem" end uuid-2.3.9/README.rdoc0000644000175000017500000001007713312112451015035 0ustar utkarsh2102utkarsh2102= UUID Generator Generates universally unique identifiers (UUIDs) for use in distributed applications. Based on {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt]. == Generating UUIDs Call #generate to generate a new UUID. The method returns a string in one of three formats. The default format is 36 characters long, and contains the 32 hexadecimal octets and hyphens separating the various value parts. The :compact format omits the hyphens, while the :urn format adds the :urn:uuid prefix. For example: uuid = UUID.new 10.times do p uuid.generate end == UUIDs in Brief UUID (universally unique identifier) are guaranteed to be unique across time and space. A UUID is 128 bit long, and consists of a 60-bit time value, a 16-bit sequence number and a 48-bit node identifier. The time value is taken from the system clock, and is monotonically incrementing. However, since it is possible to set the system clock backward, a sequence number is added. The sequence number is incremented each time the UUID generator is started. The combination guarantees that identifiers created on the same machine are unique with a high degree of probability. Note that due to the structure of the UUID and the use of sequence number, there is no guarantee that UUID values themselves are monotonically incrementing. The UUID value cannot itself be used to sort based on order of creation. To guarantee that UUIDs are unique across all machines in the network, the IEEE 802 MAC address of the machine's network interface card is used as the node identifier. For more information see {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt]. == UUID State File The UUID generator uses a state file to hold the MAC address and sequence number. The MAC address is used to generate identifiers that are unique to your machine, preventing conflicts in distributed applications. The MAC address is six bytes (48 bit) long. It is automatically extracted from one of the network cards on your machine. The sequence number is incremented each time the UUID generator is first used by the application, to prevent multiple processes from generating the same set of identifiers, and deal with changes to the system clock. The UUID state file is created in #Dir.tmpdir/ruby-uuid or the Windows common application data directory using mode 0644. If that directory is not writable, the file is created as .ruby-uuid in the home directory. If you need to create the file with a different mode, use UUID#state_file before running the UUID generator. Note: If you are running on a shared host where the state file is not shared between processes, or persisted across restarts (e.g. Heroku, Google App Engine) you can simple turn it off: UUID.state_file = false State files are not portable across machines. If you do not use the state file, UUID generation will attempt to use your server's MAC address using the macaddr gem, which runs system commands to identify the MAC address and then cache it. Since this can take a few seconds on some operating systems, when using UUID.state_file = false, you should add the following line after disabling the state file: UUID.generator.next_sequence Note: when using a forking server (Unicorn, Resque, Pipemaster, etc) you don't want your forked processes using the same sequence number. Make sure to increment the sequence number each time a worker forks. For example, in config/unicorn.rb: after_fork do |server, worker| UUID.generator.next_sequence end == Command Line You can run uuid from the command line, generating new UUID to stdout: $ uuid Multiple UUIDs in a sequence, separated with a newline: $ uuid --count 10 You can also run client and server from the command line $ uuid --server & $ uuid --socket /var/lib/uuid.sock == Latest and Greatest Source code and documentation hosted on Github: http://github.com/assaf/uuid To get UUID from source: git clone git://github.com/assaf/uuid.git == License This package is licensed under the MIT license and/or the Creative Commons Attribution-ShareAlike. :include: MIT-LICENSE uuid-2.3.9/uuid.gemspec0000644000175000017500000000144413312112451015540 0ustar utkarsh2102utkarsh2102Gem::Specification.new do |s| s.name = 'uuid' s.version = '2.3.9' s.summary = "UUID generator" s.description = <<-EOF UUID generator for producing universally unique identifiers based on RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt). EOF s.authors << 'Assaf Arkin' << 'Eric Hodel' s.email = 'assaf@labnotes.org' s.homepage = 'http://github.com/assaf/uuid' s.license = 'MIT' s.files = Dir['{bin,test,lib,docs}/**/*'] + ['README.rdoc', 'MIT-LICENSE', 'Rakefile', 'CHANGELOG', 'uuid.gemspec'] s.executables = "uuid" s.rdoc_options << '--main' << 'README.rdoc' << '--title' << 'UUID generator' << '--line-numbers' '--webcvs' << 'http://github.com/assaf/uuid' s.extra_rdoc_files = ['README.rdoc', 'MIT-LICENSE'] s.add_dependency 'macaddr', ['~>1.0'] end uuid-2.3.9/MIT-LICENSE0000644000175000017500000000206013312112451014654 0ustar utkarsh2102utkarsh2102Copyright (c) 2005-2012 Assaf Arkin, Eric Hodel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. uuid-2.3.9/CHANGELOG0000644000175000017500000000667113312112451014446 0ustar utkarsh2102utkarsh21022.3.7 (2013-02-18) Use Dir.tmpdir instead of hardcoded /var/tmp path (Justin Langhorst). 2.3.6 (2012-11-18) Manually setting the state_file path sets mode too (Tom Lea). When manually setting the state file, set the mode too. Failure to do so causes an implicit conversion of nil to integerĀ on first usage. Also provided a mode method to set the mode explicitly. Reverted executable loss (Michael Witrant ). 2.3.4 (2012-01-23) Source files are now UTF-8 (Vicente Reig) 2.3.4 (2011-09-01) Fixed State file mode isn't respected with standard umask (Brandon Turner). 2.3.3 (2011-08-01) Check if ruby-uuid is writable: it might have been claimed by another user on the system already (Andy Lo-A-Foe) 2.3.2 (2011-04-11) * Allow usage on environments that don't have MAC address accessible (mikezter) * Fix for JRuby expand_path interpretation on windows (pinnymz) 2.3.1 (2010-05-07) * Fix: Open state file in binary mode (http://github.com/assaf/uuid/issues#issue/8) 2.3.0 (2010-04-07) * Added: UUID.generator returns the current UUID generator. Particularly useful for calling next_sequence on the generator when forking a process. * Added: UUID::Server and UUID::Client so you can have one process serving you UUIDs. * Added: UUID command line tool. Yay! 2.2.0 (2010-02-18) * Added: set UUID.state_file = false if you cannot use a state file (e.g. shared hosting environment) 2.1.1 (2010-01-27) * Fixed bug which caused UUID.new to fail if the state file was somehow extant but empty 2.1.0 (2009-12-16) * Added uuid.validate -- easier to implement than explain why it's wrong. 2.0.2 (2009-06-10) * Maintenance release. Added uuid.gemspec file in packaging, tested against Ruby 1.9.1. 2.0.1 (2008-08-28) * Fixed: MAC address parses correctly when using colon as separator, not when using hyphen (ruby-mingw32). If your MAC address is all zero (check with UUID.new.inspect), remove the ruby-uuid file, and it will reset to the actual MAC address. (Rasha) * Fixed: UUID.new.inspect not showing full MAC address. 2.0.0 (2008-08-28) * Changed: API. UUID.generate still works as it always did, but the rest of the API is brand spanking new, so if you rely on anything besides UUID.generate, or just curious, check out the rdocs. * Changed: uuid.state replaced by ruby-uuid file. By default stored in /var/tmp, or if that path is not accessible, as .ruby-uuid in the home directory. * Changed: ruby-uuid is now stored as binary file (faster read/write), if you need to have a peek, open irb and type UUID.new.inspect. * Changed: Source code and documentation for this release hosted on the wonderful Github: http://github.com/assaf/uuid 1.0.4 (2007-08-28) * Changed: By default creates the uuid.state file in the working directory, not in the installation directory, which requires sudo privileges (e.g. gem). 1.0.3 (2006-11-08) * Fixed: Work around YAML bug in serializing that occurs when MAC address consists only of decimal digits. Credit: ebuprofen" 1.0.2 (2006-08-19) * Changed: Constants are not conditionally defined (removes warnings when using in Rails. 1.0.1 (2006-07-27) * Added: Regular expressions to test if a string is a UUID. * Changed: When used in ActiveRecord, adds as callback instead of overriding save. 1.0.0 (2005-11-20) * Changed: Separated form reliable-msg into its own package. uuid-2.3.9/lib/0000755000175000017500000000000013312112451013770 5ustar utkarsh2102utkarsh2102uuid-2.3.9/lib/uuid.rb0000644000175000017500000003354613312112451015276 0ustar utkarsh2102utkarsh2102# encoding: UTF-8 # # = uuid.rb - UUID generator # # Author:: Assaf Arkin assaf@labnotes.org # Eric Hodel drbrain@segment7.net # Copyright:: Copyright (c) 2005-2010 Assaf Arkin, Eric Hodel # License:: MIT and/or Creative Commons Attribution-ShareAlike require 'fileutils' require 'thread' require 'tmpdir' require 'socket' require 'macaddr' require 'digest/sha1' require 'tmpdir' ## # = Generating UUIDs # # Call #generate to generate a new UUID. The method returns a string in one of # three formats. The default format is 36 characters long, and contains the 32 # hexadecimal octets and hyphens separating the various value parts. The # :compact format omits the hyphens, while the :urn format # adds the :urn:uuid prefix. # # For example: # # uuid = UUID.new # # 10.times do # p uuid.generate # end # # = UUIDs in Brief # # UUID (universally unique identifier) are guaranteed to be unique across time # and space. # # A UUID is 128 bit long, and consists of a 60-bit time value, a 16-bit # sequence number and a 48-bit node identifier. # # The time value is taken from the system clock, and is monotonically # incrementing. However, since it is possible to set the system clock # backward, a sequence number is added. The sequence number is incremented # each time the UUID generator is started. The combination guarantees that # identifiers created on the same machine are unique with a high degree of # probability. # # Note that due to the structure of the UUID and the use of sequence number, # there is no guarantee that UUID values themselves are monotonically # incrementing. The UUID value cannot itself be used to sort based on order # of creation. # # To guarantee that UUIDs are unique across all machines in the network, # the IEEE 802 MAC address of the machine's network interface card is used as # the node identifier. # # For more information see {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt]. class UUID # Version number. module Version version = Gem::Specification.load(File.expand_path("../uuid.gemspec", File.dirname(__FILE__))).version.to_s.split(".").map { |i| i.to_i } MAJOR = version[0] MINOR = version[1] PATCH = version[2] STRING = "#{MAJOR}.#{MINOR}.#{PATCH}" end VERSION = Version::STRING ## # Clock multiplier. Converts Time (resolution: seconds) to UUID clock # (resolution: 10ns) CLOCK_MULTIPLIER = 10000000 ## # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time # ticks. CLOCK_GAPS = 100000 ## # Version number stamped into the UUID to identify it as time-based. VERSION_CLOCK = 0x0100 ## # 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 FORMATS = { :compact => '%08x%04x%04x%04x%012x', :default => '%08x-%04x-%04x-%04x-%012x', :urn => 'urn:uuid:%08x-%04x-%04x-%04x-%012x', } ## # MAC address (48 bits), sequence number and last clock STATE_FILE_FORMAT = 'SLLQ' @state_file = nil @mode = nil @uuid = nil ## # The access mode of the state file. Set it with state_file. def self.mode @mode end def self.mode=(mode) @mode = mode end ## # Generates a new UUID string using +format+. See FORMATS for a list of # supported formats. def self.generate(format = :default) @uuid ||= new @uuid.generate format end ## # Returns the UUID generator used by generate. Useful if you need to mess # with it, e.g. force next sequence when forking (e.g. Unicorn, Resque): # # after_fork do # UUID.generator.next_sequence # end def self.generator @uuid ||= new end ## # Call this to use a UUID Server. Expects address to bind to (SOCKET_NAME is # a good default) def self.server=(address) @uuid = Client.new(address) unless Client === @uuid end ## # Creates an empty state file in #Dir.tmpdir/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. def self.state_file(mode = 0644) return @state_file unless @state_file.nil? @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 = Dir.tmpdir end @state_file = File.join(state_dir, 'ruby-uuid') if !File.writable?(state_dir) || (File.exist?(@state_file) && !File.writable?(@state_file)) then @state_file = File.expand_path('.ruby-uuid', '~') end @state_file end ## # Specify the path of the state file. Use this if you need a different # location for your state file. # # Set to false if your system cannot use a state file (e.g. many shared # hosts). def self.state_file=(path) @state_file = path @mode ||= 0644 end ## # Returns true if +uuid+ is in compact, default or urn formats. Does not # validate the layout (RFC 4122 section 4) of the UUID. def self.validate(uuid) return true if uuid =~ /\A[\da-f]{32}\z/i return true if uuid =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i end ## # Generate a pseudo MAC address because we have no pure-ruby way # to know the MAC address of the NIC this system uses. Note # that cheating with pseudo arresses here is completely legal: # see Section 4.5 of RFC4122 for details. # # This implementation is shamelessly stolen from # https://github.com/spectra/ruby-uuid/blob/master/uuid.rb # Thanks spectra. # def pseudo_mac_address sha1 = ::Digest::SHA1.new 256.times do r = [rand(0x100000000)].pack "N" sha1.update r end str = sha1.digest r = rand 14 # 20-6 node = str[r, 6] || str if RUBY_VERSION >= "1.9.0" nnode = node.bytes.to_a nnode[0] |= 0x01 node = '' nnode.each { |s| node << s.chr } else node[0] |= 0x01 # multicast bit end node.bytes.collect{|b|b.to_s(16)}.join.hex & 0x7FFFFFFFFFFF end ## # Uses system calls to get a mac address # def iee_mac_address begin Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF rescue 0 end end ## # return iee_mac_address if available, pseudo_mac_address otherwise # def mac_address return iee_mac_address unless iee_mac_address == 0 return pseudo_mac_address end ## # Create a new UUID generator. You really only need to do this once. def initialize @drift = 0 @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i @mutex = Mutex.new state_file = self.class.state_file if state_file && File.size?(state_file) then next_sequence else @mac = mac_address fail "Cannot determine MAC address from any available interface, tried with #{mac_address}" if @mac == 0 @sequence = rand 0x10000 # Ensure the mode is respected, even with a restrictive umask File.open(state_file, 'w') { |f| f.chmod(self.class.mode) } if state_file && !File.exist?(state_file) if state_file open_lock 'wb' do |io| write_state io end end end end ## # Generates a new UUID string using +format+. See FORMATS for a list of # supported formats. 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 ## # Updates the state file with a new sequence number. def next_sequence if self.class.state_file open_lock 'rb+' do |io| @mac, @sequence, @last_clock = read_state(io) io.rewind io.truncate 0 @sequence += 1 write_state io end else @sequence += 1 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 def inspect mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':') "MAC: #{mac} Sequence: #{@sequence}" end protected ## # Open the state file with an exclusive lock and access mode +mode+. def open_lock(mode) File.open self.class.state_file, mode, self.class.mode do |io| begin io.flock File::LOCK_EX yield io ensure io.flock File::LOCK_UN end end end ## # Read the state from +io+ def read_state(io) mac1, mac2, seq, last_clock = io.read(32).unpack(STATE_FILE_FORMAT) mac = (mac1 << 32) + mac2 return mac, seq, last_clock end ## # Write that state to +io+ def write_state(io) mac2 = @mac & 0xffffffff mac1 = (@mac >> 32) & 0xffff io.write [mac1, mac2, @sequence, @last_clock].pack(STATE_FILE_FORMAT) end # You don't have to use this, it's just a good default. SOCKET_NAME ="/var/lib/uuid.sock" # With UUID server you don't have to worry about multiple processes # synchronizing over the state file, calling next_sequence when forking a # process and other things you're probably not worried about (because # statistically they're very unlikely to break your code). # # But if you are worried about and thought to yourself, "what would a simple # UUID server look like?", here's the answer. The protocol is dead simple: # client sends a byte, server responds with a UUID. Can use TCP or domain # sockets. class Server # Create new server. Nothing interesting happens until you call listen. def initialize() @generator = UUID.new end # Start the server listening on the specific address. Blocks and never # returns. Address can be: # - A Socket object # - UNIX domain socket name (e.g. /var/run/uuid.sock, must start with /) # - IP address, colon, port (e.g. localhost:1337) def listen(address) sock = bind(address) while client = sock.accept Thread.start(client) do |socket| while socket.read 1 socket.write @generator.generate end end end end # Returns UNIXServer or TCPServer from address. Returns argument if not a # string, so can pass through (see #listen). def bind(address) return address unless String === address if address[0] == ?/ if File.exist?(address) raise ArgumentError, "#{address} is not a socket" unless File.socket?(address) File.unlink(address) end sock = UNIXServer.new(address) File.chmod 0666, address elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/ sock = TCPServer.new($1, $2.to_i) else raise ArgumentError, "Don't know how to bind #{address}" end sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) if defined?(TCP_NODELAY) sock end end # Every server needs a client. Client provides you with the single ultimate # method: #generate. Typically you'll use this instead of the local UUID # generator: # UUID.server = UUID::SOCKET_NAME class Client def initialize(address) @socket = connect(address) at_exit { close } end # Talks to server and returns new UUID in specified format. def generate(format = :default) @socket.write "\0" uuid = @socket.read(36) return uuid if format == :default template = FORMATS[format] raise ArgumentError, "invalid UUID format #{format.inspect}" unless template template % uuid.split("-").map { |p| p.to_i(16) } end # Returns UNIXSocket or TCPSocket from address. Returns argument if not a # string, so can pass through. def connect(address) return address unless String === address if address[0] == ?/ sock = UNIXSocket.new(address) elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/ sock = TCPSocket.new($1, $2.to_i) else raise ArgumentError, "Don't know how to connect to #{address}" end sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) if defined?(TCP_NODELAY) sock end def next_sequence #:nodoc: Stubbed to do nothing. end def inspect @socket ? "Server on #{Socket.unpack_sockaddr_in(@socket.getsockname).reverse!.join(':')}" : "Connection closed" end # Close the socket. def close @socket.shutdown if @socket @socket = nil end end end