typed_uuid 3.1 → 4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '095cb1b693a3a64314143fc603c593a940fc9e62f6c52541d351896322a8b62a'
4
- data.tar.gz: 5e9e510f3a93f8eac7f7ae6cc1b06b6665cc9176335f5f08678367c8c0348ad5
3
+ metadata.gz: eb375ca568f121a2a996b9afd41123e18b820621e4417b2bce05024364eef414
4
+ data.tar.gz: 15d885d11a1dbcb19ca60733312ad0b21bcf77a0f9ce165ce8b25fa24f43cecf
5
5
  SHA512:
6
- metadata.gz: 2c0d7fbf4d5f52d337328fad41631c7999cd6c9e2fd7726c2552ade74cc5e52bb98f48f446a4063e711efa3a546e70d9f86bf9a0290d7ccb5ada6d57df5e554f
7
- data.tar.gz: 22cd61995e15a923b614eb9c66181a9bb6f60607bd0bd33f60e1f5831aaf13219a6da1f3dfbd1bf0e63480e4fd2c7f490552b2625e38eb4944692128e7851c02
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 7th & 8th bytes in the
16
- UUID are enum XORed with the result of XORing bytes 5 & 6 with bytes 9 & 10.
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-TTTT-ZZZZ-xxxxxxxxxxxx`
16
+ `xxxxxxxx-YYYY-xxxx-xxxx-xxxxZZZZTTTT`
19
17
 
20
18
  Where:
21
19
 
22
- - TTTT is the Type ENUM 0bNNNN_NNNN_NNNN_NNNN (0 - 65,535) XORed with (YYYY xor ZZZZ)
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 9 & 10 and XORing again with bytes 5 & 6 of the Typed UUID
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
- Building: 512,
44
- 'Building::SkyScrpaer' => 65_535
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
- 65_535 => 'Building::SkyScrpaer'
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
- enable_extension 'pgcrypto'
3
+ if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
4
+ enable_extension 'pgcrypto'
4
5
 
5
- execute <<-SQL
6
- CREATE OR REPLACE FUNCTION typed_uuid(t bytea) RETURNS uuid AS $$
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 := gen_random_bytes(16);
9
- uuid bytea;
10
+ bytes bytea;
11
+ type bytea;
10
12
  BEGIN
11
- bytes := set_byte(bytes, 6, (get_byte(bytes, 4) # get_byte(bytes, 8)) # get_byte(t, 0));
12
- bytes := set_byte(bytes, 7, (get_byte(bytes, 5) # get_byte(bytes, 9)) # get_byte(t, 1));
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
- $$ LANGUAGE plpgsql;
16
- SQL
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
- 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)
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
- hash[klass] = defined_uuid_types.key(klass.name)
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(:uuid_type_to_class_cache, instance_writer: false, default: {})
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, int)
30
- if int < 0 || int > 65_535
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?(int)
42
+ elsif defined_uuid_types.has_key?(enum)
33
43
  raise ArgumentError, UUID_TYPE_CONFLICT_MESSAGE % {
34
- int: int,
44
+ int: enum,
35
45
  class_name: class_name,
36
- other: defined_uuid_types[int]
46
+ other: defined_uuid_types[enum]
37
47
  }
38
48
  else
39
- defined_uuid_types[int] = class_name.to_s
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(uuid_type_from_class(self))
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 uuid_type_from_table_name(table)
62
- uuid_type_from_class(class_from_table_name(table))
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 uuid_type_from_class(klass)
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 class_from_uuid_type(type)
76
- klass = if uuid_type_to_class_cache.has_key?(type)
77
- uuid_type_to_class_cache[type]
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
- uuid_type_to_class_cache[defined_uuid_types.key(klass.name)] = klass
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
- uuid_type_to_class_cache[type]
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
- class_from_uuid_type(TypedUUID.enum(uuid))
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
- klass_enum = ::ActiveRecord::Base.uuid_type_from_table_name(self.name)
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('\\x#{klass_enum.to_s(16).rjust(4, '0')}')" }
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(t bytea) RETURNS uuid AS $$
10
- DECLARE
11
- bytes bytea := gen_random_bytes(16);
12
- uuid bytea;
13
- BEGIN
14
- bytes := set_byte(bytes, 6, (get_byte(bytes, 4) # get_byte(bytes, 8)) # get_byte(t, 0));
15
- bytes := set_byte(bytes, 7, (get_byte(bytes, 5) # get_byte(bytes, 9)) # get_byte(t, 1));
16
- RETURN encode( bytes, 'hex') :: uuid;
17
- END;
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
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module TypedUUID
2
- VERSION = '3.1'
2
+ VERSION = '4.0'
3
3
  end
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.uuid_type_to_class_cache.clear
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
@@ -1,46 +1,49 @@
1
1
  require 'test_helper'
2
2
 
3
- class FilterTest < ActiveSupport::TestCase
3
+ class UUIDTest < ActiveSupport::TestCase
4
4
 
5
5
  schema do
6
6
  ActiveRecord::Base.register_uuid_types({
7
- 'FilterTest::Listing' => 0,
8
- 'FilterTest::Building' => 592,
9
- 'FilterTest::SkyScraper' => 1_952
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 => 'FilterTest::Property'
42
+ 1 => 'UUIDTest::Property'
40
43
  })
41
44
 
42
45
  exprexted_sql = <<-SQL
43
- CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid('\\x0001') NOT NULL PRIMARY KEY, "name" character varying(255))
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 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
-
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 'class_from uuid' do
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 FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
72
- assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(building.id)
73
- assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(skyscraper.id)
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 'uuid_type from table_name' do
81
- assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name(:listings)
82
- assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name('listings')
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)
91
- end
92
-
93
- test 'class from uuid_type' do
94
- assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid_type(0)
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)
97
- end
98
-
99
- end
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: '3.1'
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: 2020-01-01 00:00:00.000000000 Z
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: :runtime
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.0.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: