lexical_uuid 0.0.1
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +26 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/lexical_uuid.rb +113 -0
- data/spec/lexical_uuid_spec.rb +173 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +109 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 James Golick
|
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.rdoc
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
= lexical_uuid
|
2
|
+
|
3
|
+
UUIDs that are byte-ordered lamport clocks (timestamp, worker_id). Much simpler than type-1 UUID's crappy, weirdo layout.
|
4
|
+
|
5
|
+
= Install
|
6
|
+
|
7
|
+
sudo gem install lexical_uuid
|
8
|
+
|
9
|
+
= Usage
|
10
|
+
|
11
|
+
ree-1.8.7-2010.02 > LexicalUUID.new
|
12
|
+
=> #<LexicalUUID:0x1019400f0 @worker_id=2842420286492008582, @timestamp=1281991807929511>
|
13
|
+
r ee-1.8.7-2010.02 > LexicalUUID.new.to_guid
|
14
|
+
=> "00048df6-faef-2bb5-2772-4e30d6b86086"
|
15
|
+
r ee-1.8.7-2010.02 > LexicalUUID.new.to_bytes
|
16
|
+
=> "\000\004\215\366\373\"\022\352'rN0\326\270206"
|
17
|
+
r ee-1.8.7-2010.02 > LexicalUUID.new(LexicalUUID.new.to_guid)
|
18
|
+
=> #<LexicalUUID:0x101936118 @worker_id=2842420286492008582, @timestamp=1281991837642798>
|
19
|
+
r ee-1.8.7-2010.02 > LexicalUUID.new(LexicalUUID.new.to_bytes)
|
20
|
+
=> #<LexicalUUID:0x101931690 @worker_id=2842420286492008582, @timestamp=1281991842330188>
|
21
|
+
r ee-1.8.7-2010.02 > LexicalUUID.new(Time.now)
|
22
|
+
=> #<LexicalUUID:0x10192df18 @worker_id=2842420286492008582, @timestamp=1281991847641387>
|
23
|
+
|
24
|
+
== Copyright
|
25
|
+
|
26
|
+
Copyright (c) 2010 James Golick. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "lexical_uuid"
|
8
|
+
gem.summary = %Q{UUIDs that are byte-ordered lamport clocks (timestamp, worker_id). Much simpler than type-1 UUID's crappy, weirdo layout.}
|
9
|
+
gem.description = %Q{UUIDs that are byte-ordered lamport clocks (timestamp, worker_id). Much simpler than type-1 UUID's crappy, weirdo layout.}
|
10
|
+
gem.email = "jamesgolick@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/jamesgolick/lexical_uuid"
|
12
|
+
gem.authors = ["James Golick"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_dependency "RubyInline", "=3.8.4"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "lexical_uuid #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/lexical_uuid.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "socket"
|
3
|
+
require "inline"
|
4
|
+
|
5
|
+
class String
|
6
|
+
inline :C do |builder|
|
7
|
+
builder.c <<-__END__
|
8
|
+
static long fnv1a() {
|
9
|
+
long hash = 0xcbf29ce484222325;
|
10
|
+
long i = 0;
|
11
|
+
|
12
|
+
for(i = 0; i < RSTRING(self)->len; i++) {
|
13
|
+
hash ^= RSTRING(self)->ptr[i];
|
14
|
+
hash *= 0x100000001b3;
|
15
|
+
}
|
16
|
+
|
17
|
+
return hash;
|
18
|
+
}
|
19
|
+
__END__
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Borrowed from the SimpleUUID gem
|
24
|
+
class Time
|
25
|
+
def self.stamp
|
26
|
+
Time.now.stamp
|
27
|
+
end
|
28
|
+
|
29
|
+
def stamp
|
30
|
+
to_i * 1_000_000 + usec
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class LexicalUUID
|
35
|
+
class << self
|
36
|
+
def worker_id
|
37
|
+
@worker_id ||= create_worker_id
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def create_worker_id
|
42
|
+
Socket.gethostbyname(Socket.gethostname).first.fnv1a
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :worker_id, :timestamp
|
47
|
+
|
48
|
+
def initialize(timestamp = nil, worker_id = nil)
|
49
|
+
case timestamp
|
50
|
+
when Fixnum, Bignum
|
51
|
+
@timestamp = timestamp
|
52
|
+
@worker_id = worker_id || self.class.worker_id
|
53
|
+
when String
|
54
|
+
case timestamp.size
|
55
|
+
when 16
|
56
|
+
from_bytes(timestamp)
|
57
|
+
when 36
|
58
|
+
elements = timestamp.split("-")
|
59
|
+
from_bytes(elements.join.to_a.pack('H32'))
|
60
|
+
else
|
61
|
+
raise ArgumentError,
|
62
|
+
"#{timestamp} was incorrectly sized. Must be 16 timestamp."
|
63
|
+
end
|
64
|
+
when Time
|
65
|
+
@timestamp = timestamp.stamp
|
66
|
+
@worker_id = self.class.worker_id
|
67
|
+
when nil
|
68
|
+
@worker_id = self.class.worker_id
|
69
|
+
@timestamp = Time.stamp
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_bytes
|
74
|
+
[timestamp >> 32,
|
75
|
+
timestamp & 0xffffffff,
|
76
|
+
worker_id >> 32,
|
77
|
+
worker_id & 0xffffffff].pack("NNNN")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Also borrowed from simple_uuid
|
81
|
+
def to_guid
|
82
|
+
elements = to_bytes.unpack("NnnCCa6")
|
83
|
+
node = elements[-1].unpack('C*')
|
84
|
+
elements[-1] = '%02x%02x%02x%02x%02x%02x' % node
|
85
|
+
"%08x-%04x-%04x-%02x%02x-%s" % elements
|
86
|
+
end
|
87
|
+
|
88
|
+
def <=>(other)
|
89
|
+
timestamp == other.timestamp ?
|
90
|
+
worker_id <=> other.worker_id : timestamp <=> other.timestamp
|
91
|
+
end
|
92
|
+
|
93
|
+
def ==(other)
|
94
|
+
other.is_a?(LexicalUUID) &&
|
95
|
+
timestamp == other.timestamp &&
|
96
|
+
worker_id == other.worker_id
|
97
|
+
end
|
98
|
+
|
99
|
+
def eql?(other)
|
100
|
+
self == other
|
101
|
+
end
|
102
|
+
|
103
|
+
def hash
|
104
|
+
to_bytes.hash
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def from_bytes(bytes)
|
109
|
+
time_high, time_low, worker_high, worker_low = bytes.unpack("NNNN")
|
110
|
+
@timestamp = (time_high << 32) | time_low
|
111
|
+
@worker_id = (worker_high << 32) | worker_low
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "LexicalUUID" do
|
4
|
+
describe "creating a UUID with no parameters" do
|
5
|
+
before do
|
6
|
+
@uuid = LexicalUUID.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has a worker id" do
|
10
|
+
@uuid.worker_id.should_not be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a timestamp in usecs" do
|
14
|
+
@uuid.timestamp.should < Time.stamp
|
15
|
+
end
|
16
|
+
|
17
|
+
it "serializes to bytes" do
|
18
|
+
expected_bytes = [@uuid.timestamp >> 32,
|
19
|
+
@uuid.timestamp & 0xffffffff,
|
20
|
+
@uuid.worker_id >> 32,
|
21
|
+
@uuid.worker_id & 0xffffffff].pack("NNNN")
|
22
|
+
@uuid.to_bytes.should == expected_bytes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "reinitializing the uuid from bytes" do
|
27
|
+
describe "with a correctly sized byte array" do
|
28
|
+
before do
|
29
|
+
@bytes = [1234567890 >> 32,
|
30
|
+
1234567890 & 0xffffffff,
|
31
|
+
9876543210 >> 32,
|
32
|
+
9876543210 & 0xffffffff].pack("NNNN")
|
33
|
+
@uuid = LexicalUUID.new(@bytes)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "correctly extracts the timestamp" do
|
37
|
+
@uuid.timestamp.should == 1234567890
|
38
|
+
end
|
39
|
+
|
40
|
+
it "correctly extracts the worker id" do
|
41
|
+
@uuid.worker_id.should == 9876543210
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "with a mis-sized byte array" do
|
46
|
+
it "raises ArgumentError" do
|
47
|
+
lambda {
|
48
|
+
LexicalUUID.new("asdf")
|
49
|
+
}.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "initializing a uuid from a timestamp and worker_id" do
|
55
|
+
before do
|
56
|
+
@timestamp = 15463021018891620831
|
57
|
+
@worker_id = 9964740229835689317
|
58
|
+
@uuid = LexicalUUID.new(@timestamp, @worker_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "sets the timestamp" do
|
62
|
+
@uuid.timestamp.should == @timestamp
|
63
|
+
end
|
64
|
+
|
65
|
+
it "sets the worker_id" do
|
66
|
+
@uuid.worker_id.should == @worker_id
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "converting a uuid in to a guid" do
|
71
|
+
before do
|
72
|
+
@uuid = LexicalUUID.new(15463021018891620831, 9964740229835689317)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "matches other uuid->guid implementations" do
|
76
|
+
@uuid.to_guid.should == "d697afb0-a96f-11df-8a49-de718e668d65"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "initializing from a guid" do
|
81
|
+
before do
|
82
|
+
@uuid = LexicalUUID.new("d697afb0-a96f-11df-8a49-de718e668d65")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "correctly initializes the timestamp" do
|
86
|
+
@uuid.timestamp.should == 15463021018891620831
|
87
|
+
end
|
88
|
+
|
89
|
+
it "correctly initializes the worker_id" do
|
90
|
+
@uuid.worker_id.should == 9964740229835689317
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "initializing with a timestamp with no worker_id" do
|
95
|
+
before do
|
96
|
+
@uuid = LexicalUUID.new(12345)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets the timestamp" do
|
100
|
+
@uuid.timestamp.should == 12345
|
101
|
+
end
|
102
|
+
|
103
|
+
it "uses the default worker_id" do
|
104
|
+
@uuid.worker_id.should == LexicalUUID.worker_id
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "comparing uuids" do
|
109
|
+
it "compares first by timestamp" do
|
110
|
+
(LexicalUUID.new(123) <=> LexicalUUID.new(234)).should == -1
|
111
|
+
(LexicalUUID.new(223) <=> LexicalUUID.new(134)).should == 1
|
112
|
+
end
|
113
|
+
|
114
|
+
it "compares by worker_id if the timestamps are equal" do
|
115
|
+
(LexicalUUID.new(123, 1) <=> LexicalUUID.new(123, 2)).should == -1
|
116
|
+
(LexicalUUID.new(123, 2) <=> LexicalUUID.new(123, 1)).should == 1
|
117
|
+
(LexicalUUID.new(123, 1) <=> LexicalUUID.new(123, 1)).should == 0
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "==" do
|
122
|
+
it "is equal when the timestamps and worker ids are equal" do
|
123
|
+
LexicalUUID.new(123, 123).should == LexicalUUID.new(123, 123)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "is not equal when the timestamps are not equal" do
|
127
|
+
LexicalUUID.new(223, 123).should_not == LexicalUUID.new(123, 123)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "is not equal when the worker_ids are not equal" do
|
131
|
+
LexicalUUID.new(123, 223).should_not == LexicalUUID.new(123, 123)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "eql?" do
|
136
|
+
it "is equal when the timestamps and worker ids are equal" do
|
137
|
+
LexicalUUID.new(123, 123).should eql(LexicalUUID.new(123, 123))
|
138
|
+
end
|
139
|
+
|
140
|
+
it "is not equal when the timestamps are not equal" do
|
141
|
+
LexicalUUID.new(223, 123).should_not eql(LexicalUUID.new(123, 123))
|
142
|
+
end
|
143
|
+
|
144
|
+
it "is not equal when the worker_ids are not equal" do
|
145
|
+
LexicalUUID.new(123, 223).should_not eql(LexicalUUID.new(123, 123))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "hash" do
|
150
|
+
it "has the same hash if the timestamp/worker_id are the same" do
|
151
|
+
LexicalUUID.new(123, 123).hash.should == LexicalUUID.new(123, 123).hash
|
152
|
+
end
|
153
|
+
|
154
|
+
it "has a different hash when the timestamps are different" do
|
155
|
+
LexicalUUID.new(223, 123).hash.should_not == LexicalUUID.new(123, 123).hash
|
156
|
+
end
|
157
|
+
|
158
|
+
it "has a different hash when the worker_ids are not equalc" do
|
159
|
+
LexicalUUID.new(123, 223).hash.should_not == LexicalUUID.new(123, 123).hash
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "initializing with a time object" do
|
164
|
+
before do
|
165
|
+
@time = Time.now
|
166
|
+
@uuid = LexicalUUID.new(@time)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "uses the time's stamp object" do
|
170
|
+
@uuid.timestamp.should == @time.stamp
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lexical_uuid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- James Golick
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-16 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 13
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 2
|
33
|
+
- 9
|
34
|
+
version: 1.2.9
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: RubyInline
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 47
|
46
|
+
segments:
|
47
|
+
- 3
|
48
|
+
- 8
|
49
|
+
- 4
|
50
|
+
version: 3.8.4
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: UUIDs that are byte-ordered lamport clocks (timestamp, worker_id). Much simpler than type-1 UUID's crappy, weirdo layout.
|
54
|
+
email: jamesgolick@gmail.com
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files:
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
files:
|
63
|
+
- .document
|
64
|
+
- .gitignore
|
65
|
+
- LICENSE
|
66
|
+
- README.rdoc
|
67
|
+
- Rakefile
|
68
|
+
- VERSION
|
69
|
+
- lib/lexical_uuid.rb
|
70
|
+
- spec/lexical_uuid_spec.rb
|
71
|
+
- spec/spec.opts
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
has_rdoc: true
|
74
|
+
homepage: http://github.com/jamesgolick/lexical_uuid
|
75
|
+
licenses: []
|
76
|
+
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --charset=UTF-8
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
hash: 3
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
requirements: []
|
101
|
+
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.3.7
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: UUIDs that are byte-ordered lamport clocks (timestamp, worker_id). Much simpler than type-1 UUID's crappy, weirdo layout.
|
107
|
+
test_files:
|
108
|
+
- spec/lexical_uuid_spec.rb
|
109
|
+
- spec/spec_helper.rb
|