typed_uuid 4.0.rc1 → 4.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +37 -10
- data/db/migrate/20191222234546_add_typed_uuid_function.rb +4 -5
- data/lib/typed_uuid.rb +16 -15
- data/lib/typed_uuid/active_record.rb +2 -2
- data/lib/typed_uuid/psql_schema_dumper.rb +21 -9
- data/lib/typed_uuid/version.rb +1 -1
- data/test/test_helper.rb +3 -1
- data/test/typed_uuid_test.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 581721d0683d1e3bf4bfa2c4bc29d4a7f5079854c85d98d8672e2f9bec6934e9
|
4
|
+
data.tar.gz: cb660ea624b3be55d02084ca8175035ade7774604a7428cb4555fa00e95460e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25835d74e2cf54a76f4be490f95fa98e5b0cbde8ddec6bafdd990a17d9a900fa7033f15f63a4be23609ae1164d686132a0472bec2e168d93f19e6204af1ab315
|
7
|
+
data.tar.gz: 8dcd77488ddd9f45bba84db7faec4b3e1f37c4258df3dc6273bee7eef1b620799cd725d56338759dc1d488aece3c32102a7364232e03183fa123cef481aa0543
|
data/README.md
CHANGED
@@ -10,10 +10,10 @@ a hex representation of 4 bits.
|
|
10
10
|
- M is 4 bits and is the Version
|
11
11
|
- N is 3 bits and is the Variant of the Version followed a bit
|
12
12
|
|
13
|
-
We modify this and use the following structure where the
|
14
|
-
UUID are the enum XORed with the result of XORing bytes
|
13
|
+
We modify this and use the following structure where the 15th & 16th bytes in the
|
14
|
+
UUID are the enum XORed with the result of XORing bytes 5 & 6 with bytes 13 & 14.
|
15
15
|
|
16
|
-
`xxxxxxxx-xxxx-
|
16
|
+
`xxxxxxxx-YYYY-xxxx-xxxx-xxxxZZZZTTTT`
|
17
17
|
|
18
18
|
Where:
|
19
19
|
|
@@ -23,19 +23,20 @@ Where:
|
|
23
23
|
- YYYY bytes XORed with ZZZZ and the Type ENUM to produce the identifying bytes
|
24
24
|
- ZZZZ bytes XORed with YYYY and the Type ENUM to produce the identifying bytes
|
25
25
|
|
26
|
-
XORing bytes
|
26
|
+
XORing bytes 5 & 6 with 13 & 14 and XORing again with bytes 15 & 16 of the
|
27
27
|
Typed UUID will give us back the ENUM and Version of the Type using soley the UUID.
|
28
28
|
|
29
29
|
## Versions
|
30
30
|
|
31
31
|
As with regular UUID Typed UUIDs come in multiple version. The current versions are:
|
32
32
|
|
33
|
-
- Version 1: A timebased UUID where the first
|
34
|
-
representing the
|
35
|
-
|
33
|
+
- Version 1: A timebased UUID where the first 56 bits are an unsigned integer
|
34
|
+
representing the microseconds since epoch. Followed by 48 random
|
35
|
+
bits or a sequence counter. Then 8 random bits followed by 16 bits
|
36
|
+
which are the UUID type.
|
36
37
|
|
37
|
-
- Version 4: A random UUID where the first
|
38
|
-
16 bits are the UUID type
|
38
|
+
- Version 4: A random UUID where the first 112 bits are random. The following
|
39
|
+
16 bits are the UUID type.
|
39
40
|
|
40
41
|
## Install
|
41
42
|
|
@@ -81,10 +82,36 @@ class CreateProperties < ActiveRecord::Migration[5.2]
|
|
81
82
|
end
|
82
83
|
```
|
83
84
|
|
85
|
+
To add a typed UUID to an existing table:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
class UpdateProperties < ActiveRecord::Migration[6.1]
|
89
|
+
def change
|
90
|
+
klass_enum = ::ActiveRecord::Base.uuid_type_from_table_name(:properties)
|
91
|
+
|
92
|
+
# Add the column
|
93
|
+
add_column :properties, :typed_uuid, :uuid, default: -> { "typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}')" }
|
94
|
+
|
95
|
+
# Update existing properties with a new typed UUID
|
96
|
+
execute "UPDATE properties SET id = typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}');"
|
97
|
+
|
98
|
+
# Add null constraint since we'll swap these out for the primary key
|
99
|
+
change_column_null :properties, :typed_uuid, false
|
100
|
+
|
101
|
+
# TODO: Here you will want to update any reference to the old primary key
|
102
|
+
# with the new typed_uuid that will be the new primary key.
|
103
|
+
|
104
|
+
# Replace the old primary key with the typed_uuid
|
105
|
+
execute "ALTER TABLE properties DROP CONSTRAINT properties_pkey;"
|
106
|
+
rename_column :properties, :typed_uuid, :id
|
107
|
+
execute "ALTER TABLE properties ADD PRIMARY KEY (id);"
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
84
111
|
## STI Models
|
85
112
|
When using STI Model Rails will generate the UUID to be inserted. This UUID will
|
86
113
|
be calculated of the STI Model class and not the base class.
|
87
114
|
|
88
115
|
In the migration you can still used `id: :typed_uuid`, this will use the base
|
89
116
|
class to calculated the default type for the UUID. You could also set the
|
90
|
-
`id` to `:uuid` and the `default` to `false` so when no ID is given it will error.
|
117
|
+
`id` to `:uuid` and the `default` to `false` so when no ID is given it will error.
|
@@ -12,17 +12,16 @@ class AddTypedUuidFunction < ActiveRecord::Migration[6.0]
|
|
12
12
|
BEGIN
|
13
13
|
IF version = 1 THEN
|
14
14
|
bytes := decode(concat(
|
15
|
-
to_hex((extract(epoch from clock_timestamp())*
|
16
|
-
encode(gen_random_bytes(
|
15
|
+
lpad(right(to_hex((extract(epoch from clock_timestamp())*1000000)::bigint), 12), 12, '0'),
|
16
|
+
encode(gen_random_bytes(10), 'hex')
|
17
17
|
), 'hex');
|
18
18
|
ELSE
|
19
19
|
bytes := gen_random_bytes(16);
|
20
|
-
version := 4;
|
21
20
|
END IF;
|
22
21
|
|
23
22
|
type := decode( lpad(to_hex(((enum << 3) | version)), 4, '0'), 'hex');
|
24
|
-
bytes := set_byte(bytes,
|
25
|
-
bytes := set_byte(bytes,
|
23
|
+
bytes := set_byte(bytes, 14, (get_byte(bytes, 4) # get_byte(bytes, 12)) # get_byte(type, 0));
|
24
|
+
bytes := set_byte(bytes, 15, (get_byte(bytes, 5) # get_byte(bytes, 13)) # get_byte(type, 1));
|
26
25
|
|
27
26
|
RETURN encode( bytes, 'hex') :: uuid;
|
28
27
|
END;
|
data/lib/typed_uuid.rb
CHANGED
@@ -3,49 +3,50 @@ module TypedUUID
|
|
3
3
|
autoload :PsqlColumnMethods, 'typed_uuid/psql_column_methods'
|
4
4
|
autoload :PsqlSchemaDumper, 'typed_uuid/psql_schema_dumper'
|
5
5
|
|
6
|
-
def self.uuid(enum, version = 4)
|
6
|
+
def self.uuid(enum, version = 4, **options)
|
7
7
|
if enum < 0 || enum > 8_191
|
8
8
|
raise ArgumentError, "UUID type must be between 0 and 8,191"
|
9
9
|
end
|
10
10
|
|
11
11
|
if version == 1
|
12
|
-
timestamp_uuid(enum)
|
12
|
+
timestamp_uuid(enum, **options)
|
13
13
|
elsif version == 4
|
14
|
-
random_uuid(enum)
|
14
|
+
random_uuid(enum, **options)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.random_uuid(enum)
|
19
19
|
uuid = SecureRandom.random_bytes(16).unpack("nnnnnnnn")
|
20
20
|
|
21
|
-
uuid[
|
21
|
+
uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 4)
|
22
22
|
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
|
23
23
|
end
|
24
|
-
|
25
|
-
def self.timestamp_uuid(enum)
|
26
|
-
|
27
|
-
|
28
|
-
uuid
|
29
|
-
|
24
|
+
|
25
|
+
def self.timestamp_uuid(enum, timestamp: nil, sequence: nil)
|
26
|
+
timestamp ||= Time.now
|
27
|
+
|
28
|
+
uuid = [timestamp.to_i * 1_000_000 + timestamp.usec].pack('Q>')[1..-1]
|
29
|
+
uuid << (sequence&.pack('Q>') || SecureRandom.random_bytes(10))
|
30
|
+
|
30
31
|
uuid = uuid.unpack("nnnnnnnn")
|
31
|
-
uuid[
|
32
|
+
uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 1)
|
32
33
|
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
|
33
34
|
end
|
34
35
|
|
35
36
|
def self.enum(uuid)
|
36
37
|
uuid = uuid.gsub('-', '')
|
37
|
-
((uuid[
|
38
|
+
((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) >> 3
|
38
39
|
end
|
39
40
|
|
40
41
|
def self.version(uuid)
|
41
42
|
uuid = uuid.gsub('-', '')
|
42
|
-
((uuid[
|
43
|
+
((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) & 0b0000000000000111
|
43
44
|
end
|
44
45
|
|
45
46
|
def self.timestamp(uuid)
|
46
47
|
uuid = uuid.gsub('-', '')
|
47
|
-
Time.at(*uuid[0..
|
48
|
+
Time.at(*uuid[0..13].to_i(16).divmod(1_000_000), :usec)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
require 'typed_uuid/railtie' if defined? Rails
|
52
|
+
require 'typed_uuid/railtie' if defined? Rails
|
@@ -64,8 +64,8 @@ module TypedUUID::ActiveRecord
|
|
64
64
|
!!class_to_uuid_type_cache[self.base_class]
|
65
65
|
end
|
66
66
|
|
67
|
-
def typed_uuid
|
68
|
-
TypedUUID.uuid(uuid_enum_from_class(self), uuid_version_from_class(self))
|
67
|
+
def typed_uuid(**options)
|
68
|
+
TypedUUID.uuid(uuid_enum_from_class(self), uuid_version_from_class(self), **options)
|
69
69
|
end
|
70
70
|
|
71
71
|
def uuid_enum_from_table_name(table)
|
@@ -6,15 +6,27 @@ module TypedUUID::PsqlSchemaDumper
|
|
6
6
|
## These are functions that must be enabled in order to support typed_uuids
|
7
7
|
## in this database
|
8
8
|
execute <<-SQL
|
9
|
-
CREATE OR REPLACE FUNCTION typed_uuid(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
CREATE OR REPLACE FUNCTION typed_uuid(enum int, version int default 4)
|
10
|
+
RETURNS uuid AS $$
|
11
|
+
DECLARE
|
12
|
+
bytes bytea;
|
13
|
+
type bytea;
|
14
|
+
BEGIN
|
15
|
+
IF version = 1 THEN
|
16
|
+
bytes := decode(concat(
|
17
|
+
lpad(right(to_hex((extract(epoch from clock_timestamp())*1000000)::bigint), 12), 12, '0'),
|
18
|
+
encode(gen_random_bytes(10), 'hex')
|
19
|
+
), 'hex');
|
20
|
+
ELSE
|
21
|
+
bytes := gen_random_bytes(16);
|
22
|
+
END IF;
|
23
|
+
|
24
|
+
type := decode( lpad(to_hex(((enum << 3) | version)), 4, '0'), 'hex');
|
25
|
+
bytes := set_byte(bytes, 14, (get_byte(bytes, 4) # get_byte(bytes, 12)) # get_byte(type, 0));
|
26
|
+
bytes := set_byte(bytes, 15, (get_byte(bytes, 5) # get_byte(bytes, 13)) # get_byte(type, 1));
|
27
|
+
|
28
|
+
RETURN encode( bytes, 'hex') :: uuid;
|
29
|
+
END;
|
18
30
|
$$ LANGUAGE plpgsql;
|
19
31
|
SQL
|
20
32
|
|
data/lib/typed_uuid/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -28,7 +28,9 @@ module ActiveRecord::Tasks::DatabaseTasks
|
|
28
28
|
end
|
29
29
|
TypedUUID::Railtie.initializers.each(&:run)
|
30
30
|
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
31
|
-
|
31
|
+
def MiniTest.filter_backtrace(bt)
|
32
|
+
bt
|
33
|
+
end
|
32
34
|
class ActiveSupport::TestCase
|
33
35
|
|
34
36
|
# File 'lib/active_support/testing/declarative.rb'
|
data/test/typed_uuid_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typed_uuid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -205,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
205
|
- !ruby/object:Gem::Version
|
206
206
|
version: 1.3.1
|
207
207
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
208
|
+
rubygems_version: 3.2.3
|
209
209
|
signing_key:
|
210
210
|
specification_version: 4
|
211
211
|
summary: Typed UUIDs for ActiveRecord
|