typed_uuid 4.0.rc1 → 4.0.rc2
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.
- 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
|