typed_uuid 2.1 → 3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|