typed_uuid 2.1 → 3.2
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 +17 -6
- data/db/migrate/20191222234546_add_typed_uuid_function.rb +16 -14
- data/lib/typed_uuid.rb +11 -0
- data/lib/typed_uuid/active_record.rb +83 -42
- data/lib/typed_uuid/psql_column_methods.rb +1 -1
- data/lib/typed_uuid/railtie.rb +3 -2
- data/lib/typed_uuid/version.rb +1 -1
- data/test/test_helper.rb +16 -9
- data/test/typed_uuid_test.rb +42 -6
- data/typed_uuid.gemspec +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9b97eecee8303b2863faf3982c4080d347c9dabc4b0fca45c2c5dc7c74b6172
|
4
|
+
data.tar.gz: 72d70bc0bc247987141c6ee4710a5ef34257693179b1d526dcc6fbf427e414f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 204697bd0dc2c7dd64b62dc7b7955cb5d83242c5966bc447c84d79d343f3b0825cc8bd1bb450525a3acc3405a61a1740b06f127a9df6f5f5830f9a063a75f18a
|
7
|
+
data.tar.gz: cba9d2425930e678e4c44bfc163392fb749c249f5e2930b62998f22fae5ad10cdaa9e8df5df013b6155eac8c1c242056e4ff67b65dfb63140f82f545b24d38d4
|
data/README.md
CHANGED
@@ -33,24 +33,27 @@ Add this to your Gemfile:
|
|
33
33
|
`gem 'typed_uuid'`
|
34
34
|
|
35
35
|
Once bundled you can add an initializer to Rails to register your types as shown
|
36
|
-
below. This maps the
|
36
|
+
below. This maps the __Model Classes__ to an integer between 0 and 65,535.
|
37
37
|
|
38
38
|
```ruby
|
39
39
|
# config/initializers/uuid_types.rb
|
40
40
|
|
41
41
|
ActiveRecord::Base.register_uuid_types({
|
42
|
-
|
43
|
-
|
42
|
+
Listing: 0,
|
43
|
+
Building: 512,
|
44
|
+
'Building::SkyScrpaer' => 65_535
|
44
45
|
})
|
45
46
|
|
46
47
|
# Or:
|
47
48
|
|
48
49
|
ActiveRecord::Base.register_uuid_types({
|
49
|
-
0
|
50
|
-
|
50
|
+
0 => :Listing,
|
51
|
+
512 => :Building,
|
52
|
+
65_535 => 'Building::SkyScrpaer'
|
51
53
|
})
|
52
54
|
```
|
53
55
|
|
56
|
+
|
54
57
|
## Usage
|
55
58
|
|
56
59
|
In your migrations simply replace `id: :uuid` with `id: :typed_uuid` when creating
|
@@ -64,4 +67,12 @@ class CreateProperties < ActiveRecord::Migration[5.2]
|
|
64
67
|
end
|
65
68
|
end
|
66
69
|
end
|
67
|
-
```
|
70
|
+
```
|
71
|
+
|
72
|
+
## STI Models
|
73
|
+
When using STI Model Rails will generate the UUID to be inserted. This UUID will
|
74
|
+
be calculated of the STI Model class and not the base class.
|
75
|
+
|
76
|
+
In the migration you can still used `id: :typed_uuid`, this will use the base
|
77
|
+
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.
|
@@ -1,18 +1,20 @@
|
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
execute <<-SQL
|
7
|
+
CREATE OR REPLACE FUNCTION typed_uuid(t bytea) RETURNS uuid AS $$
|
8
|
+
DECLARE
|
9
|
+
bytes bytea := gen_random_bytes(16);
|
10
|
+
uuid bytea;
|
11
|
+
BEGIN
|
12
|
+
bytes := set_byte(bytes, 6, (get_byte(bytes, 4) # get_byte(bytes, 8)) # get_byte(t, 0));
|
13
|
+
bytes := set_byte(bytes, 7, (get_byte(bytes, 5) # get_byte(bytes, 9)) # get_byte(t, 1));
|
14
|
+
RETURN encode( bytes, 'hex') :: uuid;
|
15
|
+
END;
|
16
|
+
$$ LANGUAGE plpgsql;
|
17
|
+
SQL
|
18
|
+
end
|
17
19
|
end
|
18
|
-
end
|
20
|
+
end
|
data/lib/typed_uuid.rb
CHANGED
@@ -2,6 +2,17 @@ module TypedUUID
|
|
2
2
|
autoload :ActiveRecord, 'typed_uuid/active_record'
|
3
3
|
autoload :PsqlColumnMethods, 'typed_uuid/psql_column_methods'
|
4
4
|
autoload :PsqlSchemaDumper, 'typed_uuid/psql_schema_dumper'
|
5
|
+
|
6
|
+
def self.uuid(enum)
|
7
|
+
uuid = SecureRandom.random_bytes(16).unpack("NnnnnN")
|
8
|
+
uuid[2] = (uuid[1] ^ uuid[3]) ^ enum
|
9
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % uuid
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enum(uuid)
|
13
|
+
uuid = uuid.gsub('-', '')
|
14
|
+
(uuid[8..11].to_i(16) ^ uuid[16..19].to_i(16)) ^ uuid[12..15].to_i(16)
|
15
|
+
end
|
5
16
|
end
|
6
17
|
|
7
18
|
require 'typed_uuid/railtie' if defined? Rails
|
@@ -1,67 +1,108 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
1
3
|
module TypedUUID::ActiveRecord
|
4
|
+
extend ActiveSupport::Concern
|
2
5
|
|
3
6
|
UUID_TYPE_CONFLICT_MESSAGE = \
|
4
|
-
"You tried to define an UUID type %{int} for \"%{
|
7
|
+
"You tried to define an UUID type %{int} for \"%{class_name}\", but " \
|
5
8
|
" %{int} is already defined as the type for %{other}"
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute(:defined_uuid_types, instance_writer: false, default: {})
|
12
|
+
class_attribute(:class_to_uuid_type_cache, instance_writer: false, default: Hash.new { |hash, klass|
|
13
|
+
hash[klass] = defined_uuid_types.key(klass.name)
|
14
|
+
})
|
15
|
+
class_attribute(:uuid_type_to_class_cache, instance_writer: false, default: {})
|
10
16
|
end
|
11
17
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
int: int,
|
18
|
-
table: table,
|
19
|
-
other: defined_uuid_types[int]
|
20
|
-
}
|
21
|
-
else
|
22
|
-
defined_uuid_types[int] = table.to_s
|
18
|
+
def _create_record
|
19
|
+
klass = self.class
|
20
|
+
if !klass.descends_from_active_record? && klass.typed?
|
21
|
+
pk = klass.primary_key
|
22
|
+
write_attribute(pk, klass.typed_uuid) if pk && read_attribute(pk).nil?
|
23
23
|
end
|
24
|
+
|
25
|
+
super
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
if
|
29
|
-
|
28
|
+
class_methods do
|
29
|
+
def register_uuid_type(class_name, int)
|
30
|
+
if int < 0 || int > 65_535
|
31
|
+
raise ArgumentError, "UUID type must be between 0 and 65,535"
|
32
|
+
elsif defined_uuid_types.has_key?(int)
|
33
|
+
raise ArgumentError, UUID_TYPE_CONFLICT_MESSAGE % {
|
34
|
+
int: int,
|
35
|
+
class_name: class_name,
|
36
|
+
other: defined_uuid_types[int]
|
37
|
+
}
|
30
38
|
else
|
31
|
-
|
39
|
+
defined_uuid_types[int] = class_name.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_uuid_types(mapping)
|
44
|
+
mapping.each do |k, v|
|
45
|
+
if k.is_a?(Integer)
|
46
|
+
register_uuid_type(v, k)
|
47
|
+
else
|
48
|
+
register_uuid_type(k, v)
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
34
|
-
end
|
35
52
|
|
36
|
-
|
37
|
-
|
38
|
-
if type.nil?
|
39
|
-
raise ArgumentError, "UUID Type for \"#{table}\" not defined"
|
53
|
+
def typed?
|
54
|
+
!!class_to_uuid_type_cache[self.base_class]
|
40
55
|
end
|
41
56
|
|
42
|
-
|
43
|
-
|
57
|
+
def typed_uuid
|
58
|
+
TypedUUID.uuid(uuid_type_from_class(self))
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
61
|
+
def uuid_type_from_table_name(table)
|
62
|
+
uuid_type_from_class(class_from_table_name(table))
|
63
|
+
end
|
64
|
+
|
65
|
+
def uuid_type_from_class(klass)
|
66
|
+
type = class_to_uuid_type_cache[klass]
|
50
67
|
|
51
|
-
|
68
|
+
if type.nil?
|
69
|
+
raise ArgumentError, "UUID Type for \"#{klass.name}\" not defined"
|
70
|
+
end
|
71
|
+
|
72
|
+
type
|
73
|
+
end
|
74
|
+
|
75
|
+
def class_from_uuid_type(type)
|
76
|
+
klass = if uuid_type_to_class_cache.has_key?(type)
|
77
|
+
uuid_type_to_class_cache[type]
|
78
|
+
else
|
79
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
80
|
+
|
81
|
+
::ActiveRecord::Base.descendants.each do |klass|
|
82
|
+
next if klass.table_name.nil?
|
83
|
+
|
84
|
+
uuid_type_to_class_cache[defined_uuid_types.key(klass.name)] = klass
|
85
|
+
end
|
86
|
+
|
87
|
+
uuid_type_to_class_cache[type]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def class_from_table_name(table)
|
92
|
+
table = table.to_s
|
93
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
94
|
+
|
95
|
+
::ActiveRecord::Base.descendants.find do |klass|
|
52
96
|
next unless ( klass.superclass == ::ActiveRecord::Base || klass.superclass.abstract_class? )
|
53
97
|
next if klass.table_name.nil?
|
54
|
-
|
55
|
-
|
98
|
+
|
99
|
+
klass.table_name == table
|
56
100
|
end
|
57
|
-
|
58
|
-
uuid_type_cache[type]
|
59
101
|
end
|
60
|
-
end
|
61
102
|
|
62
|
-
|
63
|
-
|
64
|
-
|
103
|
+
def class_from_uuid(uuid)
|
104
|
+
class_from_uuid_type(TypedUUID.enum(uuid))
|
105
|
+
end
|
65
106
|
end
|
66
107
|
|
67
108
|
end
|
@@ -4,7 +4,7 @@ module TypedUUID::PsqlColumnMethods
|
|
4
4
|
if type == :typed_uuid
|
5
5
|
klass_enum = ::ActiveRecord::Base.uuid_type_from_table_name(self.name)
|
6
6
|
options[:id] = :uuid
|
7
|
-
options[:default] ||= -> { "typed_uuid('\\x#{klass_enum.to_s(16).
|
7
|
+
options[:default] ||= -> { "typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}')" }
|
8
8
|
super(name, :uuid, **options)
|
9
9
|
else
|
10
10
|
super
|
data/lib/typed_uuid/railtie.rb
CHANGED
@@ -4,12 +4,13 @@ class TypedUUID::Railtie < Rails::Railtie
|
|
4
4
|
ActiveRecord::Tasks::DatabaseTasks.migrations_paths << File.expand_path('../../../db/migrate', __FILE__)
|
5
5
|
|
6
6
|
ActiveSupport.on_load(:active_record) do
|
7
|
-
ActiveRecord::Base.
|
7
|
+
ActiveRecord::Base.include TypedUUID::ActiveRecord
|
8
8
|
end
|
9
9
|
|
10
|
-
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
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/postgresql/schema_dumper'
|
13
14
|
ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(TypedUUID::PsqlSchemaDumper)
|
14
15
|
end
|
15
16
|
|
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'
|
@@ -50,7 +52,12 @@ class ActiveSupport::TestCase
|
|
50
52
|
end
|
51
53
|
|
52
54
|
set_callback(:setup, :before) do
|
55
|
+
Rails.stubs(:application).returns(stub(config: stub(eager_load: true)))
|
53
56
|
if !self.class.class_variable_defined?(:@@suite_setup_run)
|
57
|
+
ActiveRecord::Base.defined_uuid_types.clear
|
58
|
+
ActiveRecord::Base.uuid_type_to_class_cache.clear
|
59
|
+
ActiveRecord::Base.class_to_uuid_type_cache.clear
|
60
|
+
|
54
61
|
configuration = {
|
55
62
|
adapter: "postgresql",
|
56
63
|
database: "uuid-types-test",
|
@@ -75,14 +82,14 @@ class ActiveSupport::TestCase
|
|
75
82
|
self.class.class_variable_set(:@@suite_setup_run, true)
|
76
83
|
end
|
77
84
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
85
|
+
def debug
|
86
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
87
|
+
$debugging = true
|
88
|
+
yield
|
89
|
+
ensure
|
90
|
+
ActiveRecord::Base.logger = nil
|
91
|
+
$debugging = false
|
92
|
+
end
|
86
93
|
|
87
94
|
def capture_sql
|
88
95
|
# ActiveRecord::Base.connection.materialize_transactions
|
data/test/typed_uuid_test.rb
CHANGED
@@ -4,8 +4,9 @@ class FilterTest < ActiveSupport::TestCase
|
|
4
4
|
|
5
5
|
schema do
|
6
6
|
ActiveRecord::Base.register_uuid_types({
|
7
|
-
|
8
|
-
|
7
|
+
'FilterTest::Listing' => 0,
|
8
|
+
'FilterTest::Building' => 592,
|
9
|
+
'FilterTest::SkyScraper' => 1_952
|
9
10
|
})
|
10
11
|
|
11
12
|
create_table :listings, id: :typed_uuid do |t|
|
@@ -14,6 +15,7 @@ class FilterTest < ActiveSupport::TestCase
|
|
14
15
|
|
15
16
|
create_table :buildings, id: :typed_uuid do |t|
|
16
17
|
t.string "name", limit: 255
|
18
|
+
t.string "type", limit: 255
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
@@ -22,14 +24,23 @@ class FilterTest < ActiveSupport::TestCase
|
|
22
24
|
|
23
25
|
class Building < ActiveRecord::Base
|
24
26
|
end
|
27
|
+
|
28
|
+
class SkyScraper < Building
|
29
|
+
end
|
25
30
|
|
31
|
+
class SingleFamilyHome < Building
|
32
|
+
end
|
33
|
+
|
34
|
+
class Property < ActiveRecord::Base
|
35
|
+
end
|
36
|
+
|
26
37
|
test 'adding primary key as a typed_uuid in a migration' do
|
27
38
|
ActiveRecord::Base.register_uuid_types({
|
28
|
-
|
39
|
+
1 => 'FilterTest::Property'
|
29
40
|
})
|
30
41
|
|
31
42
|
exprexted_sql = <<-SQL
|
32
|
-
CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid('\\
|
43
|
+
CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid('\\x0001') NOT NULL PRIMARY KEY, "name" character varying(255))
|
33
44
|
SQL
|
34
45
|
|
35
46
|
assert_sql exprexted_sql do
|
@@ -41,23 +52,48 @@ class FilterTest < ActiveSupport::TestCase
|
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
55
|
+
test 'typed_uuid' do
|
56
|
+
assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512))
|
57
|
+
assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
|
58
|
+
assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(Building.typed_uuid)
|
59
|
+
assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(SkyScraper.typed_uuid)
|
60
|
+
|
61
|
+
assert_raises ArgumentError do
|
62
|
+
::ActiveRecord::Base.class_from_uuid(SingleFamilyHome.typed_uuid)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
44
66
|
test 'class_from uuid' do
|
45
67
|
listing = Listing.create
|
46
68
|
building = Building.create
|
69
|
+
skyscraper = SkyScraper.create
|
47
70
|
|
48
71
|
assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
|
49
72
|
assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(building.id)
|
73
|
+
assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(skyscraper.id)
|
74
|
+
|
75
|
+
assert_raises ArgumentError do
|
76
|
+
SingleFamilyHome.create
|
77
|
+
end
|
50
78
|
end
|
51
79
|
|
52
80
|
test 'uuid_type from table_name' do
|
53
81
|
assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name(:listings)
|
54
82
|
assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name('listings')
|
55
|
-
assert_equal
|
83
|
+
assert_equal 592, ::ActiveRecord::Base.uuid_type_from_table_name(:buildings)
|
84
|
+
end
|
85
|
+
|
86
|
+
test 'uuid_type from class' do
|
87
|
+
assert_equal 0, ::ActiveRecord::Base.uuid_type_from_class(Listing)
|
88
|
+
assert_equal 0, ::ActiveRecord::Base.uuid_type_from_class(Listing)
|
89
|
+
assert_equal 592, ::ActiveRecord::Base.uuid_type_from_class(Building)
|
90
|
+
assert_equal 1_952, ::ActiveRecord::Base.uuid_type_from_class(SkyScraper)
|
56
91
|
end
|
57
92
|
|
58
93
|
test 'class from uuid_type' do
|
59
94
|
assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid_type(0)
|
60
|
-
assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid_type(
|
95
|
+
assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid_type(592)
|
96
|
+
assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid_type(1_952)
|
61
97
|
end
|
62
98
|
|
63
99
|
end
|
data/typed_uuid.gemspec
CHANGED
@@ -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
|
+
s.add_development_dependency 'pg'
|
27
28
|
|
28
29
|
# Runtime
|
29
|
-
s.add_runtime_dependency 'pg'
|
30
30
|
s.add_runtime_dependency 'activerecord', '>= 6.0.0'
|
31
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: '2
|
4
|
+
version: '3.2'
|
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: 2020-11-04 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:
|
@@ -190,7 +190,7 @@ files:
|
|
190
190
|
homepage: https://github.com/malomalo/typed_uuid
|
191
191
|
licenses: []
|
192
192
|
metadata: {}
|
193
|
-
post_install_message:
|
193
|
+
post_install_message:
|
194
194
|
rdoc_options: []
|
195
195
|
require_paths:
|
196
196
|
- lib
|
@@ -205,8 +205,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
205
|
- !ruby/object:Gem::Version
|
206
206
|
version: '0'
|
207
207
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
209
|
-
signing_key:
|
208
|
+
rubygems_version: 3.1.4
|
209
|
+
signing_key:
|
210
210
|
specification_version: 4
|
211
211
|
summary: Typed UUIDs for ActiveRecord
|
212
212
|
test_files:
|