typed_uuid 3.1 → 4.0
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/.github/workflows/ci.yml +28 -0
- data/README.md +60 -13
- data/db/migrate/20191222234546_add_typed_uuid_function.rb +23 -9
- data/lib/typed_uuid.rb +61 -10
- data/lib/typed_uuid/active_record.rb +47 -23
- data/lib/typed_uuid/psql_column_methods.rb +3 -2
- data/lib/typed_uuid/psql_schema_dumper.rb +21 -9
- data/lib/typed_uuid/railtie.rb +5 -4
- data/lib/typed_uuid/version.rb +1 -1
- data/test/test_helper.rb +14 -13
- data/test/typed_uuid_test.rb +102 -44
- data/typed_uuid.gemspec +4 -4
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb375ca568f121a2a996b9afd41123e18b820621e4417b2bce05024364eef414
|
4
|
+
data.tar.gz: 15d885d11a1dbcb19ca60733312ad0b21bcf77a0f9ce165ce8b25fa24f43cecf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38c295658ce41fac5b51c27fdb4df1922ef426e71c2bb234e7af0e0b87abdb11e8110c3e4604e84909f30aa4a3fd2782b740b443aae9ee926fc7ad567a31a8f6
|
7
|
+
data.tar.gz: 42139374aa96e9a43b5f0f7d749a410617fa14c1affd0fa334e1fe59661189fb58d911b26056aa9e5cc6092709db6ab62541c88f788be54cfe946f45bca5868c
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
name: TypedUUID Test
|
12
|
+
runs-on: ubuntu-20.04
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: 3.0
|
18
|
+
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
|
21
|
+
- run: |
|
22
|
+
sudo systemctl start postgresql.service
|
23
|
+
sudo -u postgres createuser runner --superuser
|
24
|
+
sudo -u postgres createdb uuid-types-test
|
25
|
+
|
26
|
+
- run: bundle
|
27
|
+
|
28
|
+
- run: bundle exec rake test
|
data/README.md
CHANGED
@@ -7,24 +7,44 @@ a hex representation of 4 bits.
|
|
7
7
|
|
8
8
|
`xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx`
|
9
9
|
|
10
|
-
Where:
|
11
|
-
|
12
10
|
- M is 4 bits and is the Version
|
13
11
|
- N is 3 bits and is the Variant of the Version followed a bit
|
14
12
|
|
15
|
-
We modify this and use the following structure where the
|
16
|
-
UUID are enum XORed with the result of XORing bytes 5 & 6 with 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.
|
17
15
|
|
18
|
-
`xxxxxxxx-YYYY-
|
16
|
+
`xxxxxxxx-YYYY-xxxx-xxxx-xxxxZZZZTTTT`
|
19
17
|
|
20
18
|
Where:
|
21
19
|
|
22
|
-
- TTTT is the Type ENUM
|
20
|
+
- TTTT is the Type ENUM & Version 0bEEEE_EEEE_EEEE_EVVV; XORed with (YYYY xor ZZZZ)
|
21
|
+
- The Es are the bits of the 13 bit ENUM supporting 8,192 enums/types (0 - 8,191)
|
22
|
+
- The Vs are the bits in the 3 bit version supporting 8 versions (0 - 7)
|
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 5 & 6 with
|
27
|
-
will give us back the ENUM of the Type using soley the UUID.
|
26
|
+
XORing bytes 5 & 6 with 13 & 14 and XORing again with bytes 15 & 16 of the
|
27
|
+
Typed UUID will give us back the ENUM and Version of the Type using soley the UUID.
|
28
|
+
|
29
|
+
## Versions
|
30
|
+
|
31
|
+
As with regular UUID Typed UUIDs come in multiple version. The current versions are:
|
32
|
+
|
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.
|
37
|
+
|
38
|
+
- Version 3: A name-based UUID where the first 112 bits are based off the MD5
|
39
|
+
digest of the namespace and name. The following 16 bits are the
|
40
|
+
UUID type.
|
41
|
+
|
42
|
+
- Version 4: A random UUID where the first 112 bits are random. The following
|
43
|
+
16 bits are the UUID type.
|
44
|
+
|
45
|
+
- Version 5: A name-based UUID where the first 112 bits are based off the SHA1
|
46
|
+
digest of the namespace and name. The following 16 bits are the
|
47
|
+
UUID type.
|
28
48
|
|
29
49
|
## Install
|
30
50
|
|
@@ -40,8 +60,9 @@ below. This maps the __Model Classes__ to an integer between 0 and 65,535.
|
|
40
60
|
|
41
61
|
ActiveRecord::Base.register_uuid_types({
|
42
62
|
Listing: 0,
|
43
|
-
|
44
|
-
|
63
|
+
Address: {enum: 5},
|
64
|
+
Building: {enum: 512, version: 1},
|
65
|
+
'Building::SkyScrpaer' => 8_191
|
45
66
|
})
|
46
67
|
|
47
68
|
# Or:
|
@@ -49,7 +70,7 @@ ActiveRecord::Base.register_uuid_types({
|
|
49
70
|
ActiveRecord::Base.register_uuid_types({
|
50
71
|
0 => :Listing,
|
51
72
|
512 => :Building,
|
52
|
-
|
73
|
+
8_191 => 'Building::SkyScrpaer'
|
53
74
|
})
|
54
75
|
```
|
55
76
|
|
@@ -61,7 +82,7 @@ a table.
|
|
61
82
|
|
62
83
|
```ruby
|
63
84
|
class CreateProperties < ActiveRecord::Migration[5.2]
|
64
|
-
def change
|
85
|
+
def change
|
65
86
|
create_table :properties, id: :typed_uuid do |t|
|
66
87
|
t.string "name", limit: 255
|
67
88
|
end
|
@@ -69,10 +90,36 @@ class CreateProperties < ActiveRecord::Migration[5.2]
|
|
69
90
|
end
|
70
91
|
```
|
71
92
|
|
93
|
+
To add a typed UUID to an existing table:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class UpdateProperties < ActiveRecord::Migration[6.1]
|
97
|
+
def change
|
98
|
+
klass_enum = ::ActiveRecord::Base.uuid_type_from_table_name(:properties)
|
99
|
+
|
100
|
+
# Add the column
|
101
|
+
add_column :properties, :typed_uuid, :uuid, default: -> { "typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}')" }
|
102
|
+
|
103
|
+
# Update existing properties with a new typed UUID
|
104
|
+
execute "UPDATE properties SET id = typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}');"
|
105
|
+
|
106
|
+
# Add null constraint since we'll swap these out for the primary key
|
107
|
+
change_column_null :properties, :typed_uuid, false
|
108
|
+
|
109
|
+
# TODO: Here you will want to update any reference to the old primary key
|
110
|
+
# with the new typed_uuid that will be the new primary key.
|
111
|
+
|
112
|
+
# Replace the old primary key with the typed_uuid
|
113
|
+
execute "ALTER TABLE properties DROP CONSTRAINT properties_pkey;"
|
114
|
+
rename_column :properties, :typed_uuid, :id
|
115
|
+
execute "ALTER TABLE properties ADD PRIMARY KEY (id);"
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
72
119
|
## STI Models
|
73
120
|
When using STI Model Rails will generate the UUID to be inserted. This UUID will
|
74
121
|
be calculated of the STI Model class and not the base class.
|
75
122
|
|
76
123
|
In the migration you can still used `id: :typed_uuid`, this will use the base
|
77
124
|
class to calculated the default type for the UUID. You could also set the
|
78
|
-
`id` to `:uuid` and the `default` to `false` so when no ID is given it will error.
|
125
|
+
`id` to `:uuid` and the `default` to `false` so when no ID is given it will error.
|
@@ -1,18 +1,32 @@
|
|
1
1
|
class AddTypedUuidFunction < ActiveRecord::Migration[6.0]
|
2
2
|
def up
|
3
|
-
|
3
|
+
if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
4
|
+
enable_extension 'pgcrypto'
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
execute <<-SQL
|
7
|
+
CREATE OR REPLACE FUNCTION typed_uuid(enum int, version int default 4)
|
8
|
+
RETURNS uuid AS $$
|
7
9
|
DECLARE
|
8
|
-
bytes bytea
|
9
|
-
|
10
|
+
bytes bytea;
|
11
|
+
type bytea;
|
10
12
|
BEGIN
|
11
|
-
|
12
|
-
|
13
|
+
IF version = 1 THEN
|
14
|
+
bytes := decode(concat(
|
15
|
+
lpad(right(to_hex((extract(epoch from clock_timestamp())*1000000)::bigint), 12), 12, '0'),
|
16
|
+
encode(gen_random_bytes(10), 'hex')
|
17
|
+
), 'hex');
|
18
|
+
ELSE
|
19
|
+
bytes := gen_random_bytes(16);
|
20
|
+
END IF;
|
21
|
+
|
22
|
+
type := decode( lpad(to_hex(((enum << 3) | version)), 4, '0'), 'hex');
|
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));
|
25
|
+
|
13
26
|
RETURN encode( bytes, 'hex') :: uuid;
|
14
27
|
END;
|
15
|
-
|
16
|
-
|
28
|
+
$$ LANGUAGE plpgsql;
|
29
|
+
SQL
|
30
|
+
end
|
17
31
|
end
|
18
32
|
end
|
data/lib/typed_uuid.rb
CHANGED
@@ -1,17 +1,68 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module TypedUUID
|
2
4
|
autoload :ActiveRecord, 'typed_uuid/active_record'
|
3
5
|
autoload :PsqlColumnMethods, 'typed_uuid/psql_column_methods'
|
4
6
|
autoload :PsqlSchemaDumper, 'typed_uuid/psql_schema_dumper'
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
uuid
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def uuid(enum, version = 4, **options)
|
11
|
+
if enum < 0 || enum > 8_191
|
12
|
+
raise ArgumentError, "UUID type must be between 0 and 8,191"
|
13
|
+
end
|
14
|
+
|
15
|
+
case version
|
16
|
+
when 1
|
17
|
+
timestamp_uuid(enum, **options)
|
18
|
+
when 3
|
19
|
+
namebased_uuid(enum, digester: Digest::MD5, **options)
|
20
|
+
when 4
|
21
|
+
random_uuid(enum, **options)
|
22
|
+
when 5
|
23
|
+
namebased_uuid(enum, digester: Digest::SHA1, **options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def random_uuid(enum)
|
28
|
+
uuid = SecureRandom.random_bytes(16).unpack("nnnnnnnn")
|
29
|
+
|
30
|
+
uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 4)
|
31
|
+
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
|
32
|
+
end
|
33
|
+
|
34
|
+
def timestamp_uuid(enum, timestamp: nil, sequence: nil)
|
35
|
+
timestamp ||= Time.now
|
36
|
+
|
37
|
+
uuid = [timestamp.to_i * 1_000_000 + timestamp.usec].pack('Q>')[1..-1]
|
38
|
+
uuid << (sequence&.pack('Q>') || SecureRandom.random_bytes(10))
|
39
|
+
|
40
|
+
uuid = uuid.unpack("nnnnnnnn")
|
41
|
+
uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 1)
|
42
|
+
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
|
43
|
+
end
|
44
|
+
|
45
|
+
def namebased_uuid(enum, digester:, name:, namespace: "")
|
46
|
+
uuid = digester.digest(name + namespace).unpack("nnnnnnnn")
|
47
|
+
uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 5)
|
48
|
+
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
|
49
|
+
end
|
50
|
+
|
51
|
+
def enum(uuid)
|
52
|
+
uuid = uuid.gsub('-', '')
|
53
|
+
((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) >> 3
|
54
|
+
end
|
55
|
+
|
56
|
+
def version(uuid)
|
57
|
+
uuid = uuid.gsub('-', '')
|
58
|
+
((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) & 0b0000000000000111
|
59
|
+
end
|
60
|
+
|
61
|
+
def timestamp(uuid)
|
62
|
+
uuid = uuid.gsub('-', '')
|
63
|
+
Time.at(*uuid[0..13].to_i(16).divmod(1_000_000), :usec)
|
64
|
+
end
|
65
|
+
|
15
66
|
end
|
16
67
|
end
|
17
68
|
|
@@ -10,9 +10,11 @@ module TypedUUID::ActiveRecord
|
|
10
10
|
included do
|
11
11
|
class_attribute(:defined_uuid_types, instance_writer: false, default: {})
|
12
12
|
class_attribute(:class_to_uuid_type_cache, instance_writer: false, default: Hash.new { |hash, klass|
|
13
|
-
|
13
|
+
if type = defined_uuid_types.find { |k, v| v[:class] == klass.name }
|
14
|
+
hash[klass] = type[1]
|
15
|
+
end
|
14
16
|
})
|
15
|
-
class_attribute(:
|
17
|
+
class_attribute(:uuid_enum_to_class_cache, instance_writer: false, default: {})
|
16
18
|
end
|
17
19
|
|
18
20
|
def _create_record
|
@@ -26,17 +28,25 @@ module TypedUUID::ActiveRecord
|
|
26
28
|
end
|
27
29
|
|
28
30
|
class_methods do
|
29
|
-
def register_uuid_type(class_name,
|
30
|
-
if
|
31
|
+
def register_uuid_type(class_name, enum_or_options)
|
32
|
+
if enum_or_options.is_a?(Hash)
|
33
|
+
enum = enum_or_options[:enum]
|
34
|
+
version = enum_or_options[:version] || 4
|
35
|
+
else
|
36
|
+
enum = enum_or_options
|
37
|
+
version = 4
|
38
|
+
end
|
39
|
+
|
40
|
+
if enum < 0 || enum > 8_191
|
31
41
|
raise ArgumentError, "UUID type must be between 0 and 65,535"
|
32
|
-
elsif defined_uuid_types.has_key?(
|
42
|
+
elsif defined_uuid_types.has_key?(enum)
|
33
43
|
raise ArgumentError, UUID_TYPE_CONFLICT_MESSAGE % {
|
34
|
-
int:
|
44
|
+
int: enum,
|
35
45
|
class_name: class_name,
|
36
|
-
other: defined_uuid_types[
|
46
|
+
other: defined_uuid_types[enum]
|
37
47
|
}
|
38
48
|
else
|
39
|
-
defined_uuid_types[
|
49
|
+
defined_uuid_types[enum] = { class: class_name.to_s, version: version, enum: enum }
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
@@ -54,37 +64,51 @@ module TypedUUID::ActiveRecord
|
|
54
64
|
!!class_to_uuid_type_cache[self.base_class]
|
55
65
|
end
|
56
66
|
|
57
|
-
def typed_uuid
|
58
|
-
TypedUUID.uuid(
|
67
|
+
def typed_uuid(**options)
|
68
|
+
TypedUUID.uuid(uuid_enum_from_class(self), uuid_version_from_class(self), **options)
|
59
69
|
end
|
60
|
-
|
61
|
-
def
|
62
|
-
|
70
|
+
|
71
|
+
def uuid_enum_from_table_name(table)
|
72
|
+
uuid_enum_from_class(class_from_table_name(table))
|
63
73
|
end
|
64
74
|
|
65
|
-
def
|
75
|
+
def uuid_version_from_table_name(table)
|
76
|
+
uuid_version_from_class(class_from_table_name(table))
|
77
|
+
end
|
78
|
+
|
79
|
+
def uuid_enum_from_class(klass)
|
66
80
|
type = class_to_uuid_type_cache[klass]
|
81
|
+
if type.nil?
|
82
|
+
raise ArgumentError, "UUID Type for \"#{klass.name}\" not defined"
|
83
|
+
end
|
84
|
+
|
85
|
+
type[:enum]
|
86
|
+
end
|
67
87
|
|
88
|
+
def uuid_version_from_class(klass)
|
89
|
+
type = class_to_uuid_type_cache[klass]
|
68
90
|
if type.nil?
|
69
91
|
raise ArgumentError, "UUID Type for \"#{klass.name}\" not defined"
|
70
92
|
end
|
71
93
|
|
72
|
-
type
|
94
|
+
type[:version]
|
73
95
|
end
|
74
|
-
|
75
|
-
def
|
76
|
-
|
77
|
-
|
96
|
+
|
97
|
+
def class_from_uuid_enum(enum)
|
98
|
+
if uuid_enum_to_class_cache.has_key?(enum)
|
99
|
+
uuid_enum_to_class_cache[enum]
|
78
100
|
else
|
79
101
|
Rails.application.eager_load! if !Rails.application.config.eager_load
|
80
102
|
|
81
103
|
::ActiveRecord::Base.descendants.each do |klass|
|
82
104
|
next if klass.table_name.nil?
|
83
|
-
|
84
|
-
|
105
|
+
|
106
|
+
if key = defined_uuid_types.find { |enum, info| info[:class] == klass.name }
|
107
|
+
uuid_enum_to_class_cache[key[0]] = klass
|
108
|
+
end
|
85
109
|
end
|
86
110
|
|
87
|
-
|
111
|
+
uuid_enum_to_class_cache[enum]
|
88
112
|
end
|
89
113
|
end
|
90
114
|
|
@@ -101,7 +125,7 @@ module TypedUUID::ActiveRecord
|
|
101
125
|
end
|
102
126
|
|
103
127
|
def class_from_uuid(uuid)
|
104
|
-
|
128
|
+
class_from_uuid_enum(TypedUUID.enum(uuid))
|
105
129
|
end
|
106
130
|
end
|
107
131
|
|
@@ -2,9 +2,10 @@ module TypedUUID::PsqlColumnMethods
|
|
2
2
|
|
3
3
|
def primary_key(name, type = :primary_key, **options)
|
4
4
|
if type == :typed_uuid
|
5
|
-
|
5
|
+
klass_type_enum = ::ActiveRecord::Base.uuid_enum_from_table_name(self.name)
|
6
|
+
klass_type_version = ::ActiveRecord::Base.uuid_version_from_table_name(self.name)
|
6
7
|
options[:id] = :uuid
|
7
|
-
options[:default] ||= -> { "typed_uuid(
|
8
|
+
options[:default] ||= -> { "typed_uuid(#{klass_type_enum}, #{klass_type_version})" }
|
8
9
|
super(name, :uuid, **options)
|
9
10
|
else
|
10
11
|
super
|
@@ -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/railtie.rb
CHANGED
@@ -2,16 +2,17 @@ class TypedUUID::Railtie < Rails::Railtie
|
|
2
2
|
|
3
3
|
initializer :typed_uuid do |app|
|
4
4
|
ActiveRecord::Tasks::DatabaseTasks.migrations_paths << File.expand_path('../../../db/migrate', __FILE__)
|
5
|
-
|
5
|
+
|
6
6
|
ActiveSupport.on_load(:active_record) do
|
7
7
|
ActiveRecord::Base.include TypedUUID::ActiveRecord
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
11
11
|
ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.include(TypedUUID::PsqlColumnMethods)
|
12
|
-
|
12
|
+
|
13
|
+
require 'active_record/connection_adapters/abstract/schema_dumper'
|
13
14
|
require 'active_record/connection_adapters/postgresql/schema_dumper'
|
14
15
|
ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(TypedUUID::PsqlSchemaDumper)
|
15
16
|
end
|
16
17
|
|
17
|
-
end
|
18
|
+
end
|
data/lib/typed_uuid/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -32,7 +32,7 @@ def MiniTest.filter_backtrace(bt)
|
|
32
32
|
bt
|
33
33
|
end
|
34
34
|
class ActiveSupport::TestCase
|
35
|
-
|
35
|
+
|
36
36
|
# File 'lib/active_support/testing/declarative.rb'
|
37
37
|
def self.test(name, &block)
|
38
38
|
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
|
@@ -46,31 +46,32 @@ class ActiveSupport::TestCase
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def self.schema(&block)
|
51
51
|
self.class_variable_set(:@@schema, block)
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
set_callback(:setup, :before) do
|
55
55
|
Rails.stubs(:application).returns(stub(config: stub(eager_load: true)))
|
56
56
|
if !self.class.class_variable_defined?(:@@suite_setup_run)
|
57
57
|
ActiveRecord::Base.defined_uuid_types.clear
|
58
|
-
ActiveRecord::Base.
|
58
|
+
ActiveRecord::Base.uuid_enum_to_class_cache.clear
|
59
59
|
ActiveRecord::Base.class_to_uuid_type_cache.clear
|
60
|
-
|
60
|
+
|
61
61
|
configuration = {
|
62
62
|
adapter: "postgresql",
|
63
63
|
database: "uuid-types-test",
|
64
64
|
encoding: "utf8"
|
65
65
|
}.stringify_keys
|
66
|
-
|
67
|
-
db_tasks = ActiveRecord::Tasks::PostgreSQLDatabaseTasks.new(configuration)
|
68
|
-
db_tasks.purge
|
69
|
-
|
66
|
+
|
70
67
|
ActiveRecord::Base.establish_connection(configuration)
|
68
|
+
|
69
|
+
db_tasks = ActiveRecord::Tasks::PostgreSQLDatabaseTasks.new(ActiveRecord::Base.connection_db_config)
|
70
|
+
db_tasks.purge
|
71
|
+
|
71
72
|
ActiveRecord::Migration.suppress_messages do
|
72
73
|
AddTypedUuidFunction.migrate :up
|
73
|
-
|
74
|
+
|
74
75
|
if self.class.class_variable_defined?(:@@schema)
|
75
76
|
ActiveRecord::Schema.define(&self.class.class_variable_get(:@@schema))
|
76
77
|
ActiveRecord::Migration.execute("SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'").each_row do |row|
|
@@ -134,7 +135,7 @@ class ActiveSupport::TestCase
|
|
134
135
|
end
|
135
136
|
end
|
136
137
|
end
|
137
|
-
|
138
|
+
|
138
139
|
class SQLLogger
|
139
140
|
class << self
|
140
141
|
attr_accessor :ignored_sql, :log, :log_all
|
@@ -197,5 +198,5 @@ class ActiveSupport::TestCase
|
|
197
198
|
alias :assert_not_predicate :refute_predicate
|
198
199
|
alias :assert_not_respond_to :refute_respond_to
|
199
200
|
alias :assert_not_same :refute_same
|
200
|
-
|
201
|
-
end
|
201
|
+
|
202
|
+
end
|
data/test/typed_uuid_test.rb
CHANGED
@@ -1,46 +1,49 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class UUIDTest < ActiveSupport::TestCase
|
4
4
|
|
5
5
|
schema do
|
6
6
|
ActiveRecord::Base.register_uuid_types({
|
7
|
-
'
|
8
|
-
'
|
9
|
-
'
|
7
|
+
'UUIDTest::Listing' => {enum: 0, version: 1},
|
8
|
+
'UUIDTest::Building' => 592,
|
9
|
+
'UUIDTest::SkyScraper' => {enum: 1_952}
|
10
10
|
})
|
11
|
-
|
11
|
+
|
12
12
|
create_table :listings, id: :typed_uuid do |t|
|
13
13
|
t.string "name", limit: 255
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
create_table :buildings, id: :typed_uuid do |t|
|
17
17
|
t.string "name", limit: 255
|
18
18
|
t.string "type", limit: 255
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
class Listing < ActiveRecord::Base
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
class Building < ActiveRecord::Base
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
class SkyScraper < Building
|
29
29
|
end
|
30
30
|
|
31
31
|
class SingleFamilyHome < Building
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
class Property < ActiveRecord::Base
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
|
+
class Lot < ActiveRecord::Base
|
38
|
+
end
|
39
|
+
|
37
40
|
test 'adding primary key as a typed_uuid in a migration' do
|
38
41
|
ActiveRecord::Base.register_uuid_types({
|
39
|
-
1 => '
|
42
|
+
1 => 'UUIDTest::Property'
|
40
43
|
})
|
41
44
|
|
42
45
|
exprexted_sql = <<-SQL
|
43
|
-
CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid(
|
46
|
+
CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid(1, 4) NOT NULL PRIMARY KEY, "name" character varying(255))
|
44
47
|
SQL
|
45
48
|
|
46
49
|
assert_sql exprexted_sql do
|
@@ -52,48 +55,103 @@ class FilterTest < ActiveSupport::TestCase
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
58
|
+
test 'adding primary key as a typed_uuid in a migration with a version' do
|
59
|
+
ActiveRecord::Base.register_uuid_types({
|
60
|
+
'UUIDTest::Lot' => {version: 1, enum: 512}
|
61
|
+
})
|
62
|
+
|
63
|
+
exprexted_sql = <<-SQL
|
64
|
+
CREATE TABLE "lots" ("id" uuid DEFAULT typed_uuid(512, 1) NOT NULL PRIMARY KEY, "name" character varying(255))
|
65
|
+
SQL
|
66
|
+
|
67
|
+
assert_sql exprexted_sql do
|
68
|
+
ActiveRecord::Migration.suppress_messages do
|
69
|
+
ActiveRecord::Migration.create_table :lots, id: :typed_uuid do |t|
|
70
|
+
t.string "name", limit: 255
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
55
76
|
test 'typed_uuid' do
|
56
77
|
assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512))
|
57
|
-
assert_equal
|
58
|
-
assert_equal
|
59
|
-
assert_equal
|
60
|
-
|
78
|
+
assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512, 1))
|
79
|
+
assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512, 3, name: "test"))
|
80
|
+
assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512, 5, name: "test"))
|
81
|
+
assert_equal UUIDTest::Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
|
82
|
+
assert_equal UUIDTest::Building, ::ActiveRecord::Base.class_from_uuid(Building.typed_uuid)
|
83
|
+
assert_equal UUIDTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(SkyScraper.typed_uuid)
|
84
|
+
|
61
85
|
assert_raises ArgumentError do
|
62
86
|
::ActiveRecord::Base.class_from_uuid(SingleFamilyHome.typed_uuid)
|
63
87
|
end
|
64
88
|
end
|
65
|
-
|
66
|
-
test '
|
89
|
+
|
90
|
+
test 'typed_uuid v1' do
|
91
|
+
time = Time.at(1606612254, 370979, :usec)
|
92
|
+
Time.stubs(:now).returns(time)
|
93
|
+
|
94
|
+
uuid = TypedUUID.uuid(592, 1)
|
95
|
+
|
96
|
+
assert_equal 1, TypedUUID.version(uuid)
|
97
|
+
assert_equal 592, TypedUUID.enum(uuid)
|
98
|
+
|
99
|
+
assert_equal time, TypedUUID.timestamp(uuid)
|
100
|
+
assert_equal time.to_i, TypedUUID.timestamp(uuid).to_i
|
101
|
+
assert_equal time.nsec, TypedUUID.timestamp(uuid).nsec
|
102
|
+
|
103
|
+
assert_equal Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
|
104
|
+
end
|
105
|
+
|
106
|
+
test 'class_from uuid generated bye PostgresQL' do
|
67
107
|
listing = Listing.create
|
68
108
|
building = Building.create
|
69
109
|
skyscraper = SkyScraper.create
|
70
|
-
|
71
|
-
assert_equal
|
72
|
-
assert_equal
|
73
|
-
assert_equal
|
110
|
+
|
111
|
+
assert_equal UUIDTest::Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
|
112
|
+
assert_equal UUIDTest::Building, ::ActiveRecord::Base.class_from_uuid(building.id)
|
113
|
+
assert_equal UUIDTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(skyscraper.id)
|
74
114
|
|
75
115
|
assert_raises ArgumentError do
|
76
116
|
SingleFamilyHome.create
|
77
117
|
end
|
78
118
|
end
|
79
|
-
|
80
|
-
test '
|
81
|
-
|
82
|
-
|
83
|
-
assert_equal
|
84
|
-
end
|
85
|
-
|
86
|
-
test '
|
87
|
-
assert_equal 0, ::ActiveRecord::Base.
|
88
|
-
assert_equal 0, ::ActiveRecord::Base.
|
89
|
-
|
90
|
-
assert_equal
|
91
|
-
end
|
92
|
-
|
93
|
-
test '
|
94
|
-
assert_equal
|
95
|
-
assert_equal
|
96
|
-
assert_equal
|
97
|
-
end
|
98
|
-
|
99
|
-
|
119
|
+
|
120
|
+
test 'class_from v1 uuid generated bye PostgresQL' do
|
121
|
+
listing = Listing.create
|
122
|
+
|
123
|
+
assert_equal Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
|
124
|
+
end
|
125
|
+
|
126
|
+
test 'uuid_enum from table_name' do
|
127
|
+
assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_table_name(:listings)
|
128
|
+
assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_table_name('listings')
|
129
|
+
|
130
|
+
assert_equal 592, ::ActiveRecord::Base.uuid_enum_from_table_name(:buildings)
|
131
|
+
end
|
132
|
+
|
133
|
+
test 'uuid_version from table_name' do
|
134
|
+
assert_equal 1, ::ActiveRecord::Base.uuid_version_from_table_name(:listings)
|
135
|
+
assert_equal 1, ::ActiveRecord::Base.uuid_version_from_table_name('listings')
|
136
|
+
assert_equal 4, ::ActiveRecord::Base.uuid_version_from_table_name(:buildings)
|
137
|
+
end
|
138
|
+
|
139
|
+
test 'uuid_enum from class' do
|
140
|
+
assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_class(Listing)
|
141
|
+
assert_equal 592, ::ActiveRecord::Base.uuid_enum_from_class(Building)
|
142
|
+
assert_equal 1_952, ::ActiveRecord::Base.uuid_enum_from_class(SkyScraper)
|
143
|
+
end
|
144
|
+
|
145
|
+
test 'uuid_version from class' do
|
146
|
+
assert_equal 1, ::ActiveRecord::Base.uuid_version_from_class(Listing)
|
147
|
+
assert_equal 4, ::ActiveRecord::Base.uuid_version_from_class(Building)
|
148
|
+
assert_equal 4, ::ActiveRecord::Base.uuid_version_from_class(SkyScraper)
|
149
|
+
end
|
150
|
+
|
151
|
+
test 'class from uuid_enum' do
|
152
|
+
assert_equal UUIDTest::Listing, ::ActiveRecord::Base.class_from_uuid_enum(0)
|
153
|
+
assert_equal UUIDTest::Building, ::ActiveRecord::Base.class_from_uuid_enum(592)
|
154
|
+
assert_equal UUIDTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid_enum(1_952)
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
data/typed_uuid.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
15
|
s.require_paths = ["lib"]
|
16
16
|
|
17
|
-
# Developoment
|
17
|
+
# Developoment
|
18
18
|
s.add_development_dependency 'rake'
|
19
19
|
s.add_development_dependency 'bundler'
|
20
20
|
s.add_development_dependency 'minitest'
|
@@ -24,8 +24,8 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_development_dependency 'byebug'
|
25
25
|
s.add_development_dependency 'activesupport', '>= 6.0.0'
|
26
26
|
s.add_development_dependency 'rails', '>= 6.0.0'
|
27
|
-
|
27
|
+
s.add_development_dependency 'pg'
|
28
|
+
|
28
29
|
# Runtime
|
29
|
-
s.add_runtime_dependency 'pg'
|
30
30
|
s.add_runtime_dependency 'activerecord', '>= 6.0.0'
|
31
|
-
end
|
31
|
+
end
|
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
|
+
version: '4.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -143,7 +143,7 @@ dependencies:
|
|
143
143
|
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
145
|
version: '0'
|
146
|
-
type: :
|
146
|
+
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
@@ -171,6 +171,7 @@ executables: []
|
|
171
171
|
extensions: []
|
172
172
|
extra_rdoc_files: []
|
173
173
|
files:
|
174
|
+
- ".github/workflows/ci.yml"
|
174
175
|
- ".gitignore"
|
175
176
|
- ".tm_properties"
|
176
177
|
- Gemfile
|
@@ -190,7 +191,7 @@ files:
|
|
190
191
|
homepage: https://github.com/malomalo/typed_uuid
|
191
192
|
licenses: []
|
192
193
|
metadata: {}
|
193
|
-
post_install_message:
|
194
|
+
post_install_message:
|
194
195
|
rdoc_options: []
|
195
196
|
require_paths:
|
196
197
|
- lib
|
@@ -205,8 +206,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
206
|
- !ruby/object:Gem::Version
|
206
207
|
version: '0'
|
207
208
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
209
|
-
signing_key:
|
209
|
+
rubygems_version: 3.2.3
|
210
|
+
signing_key:
|
210
211
|
specification_version: 4
|
211
212
|
summary: Typed UUIDs for ActiveRecord
|
212
213
|
test_files:
|