typed_uuid 2.3 → 4.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42ecd88d5a86ad89fe9a8c0cd8ec275bfded2d66ce7a7bd240f43184d0fe65ab
4
- data.tar.gz: 2b32e8528b4d5dc750c54b8818ccb0fcbb99ea4d5276ece385d8af8f2f70c259
3
+ metadata.gz: 581721d0683d1e3bf4bfa2c4bc29d4a7f5079854c85d98d8672e2f9bec6934e9
4
+ data.tar.gz: cb660ea624b3be55d02084ca8175035ade7774604a7428cb4555fa00e95460e6
5
5
  SHA512:
6
- metadata.gz: 2bacb4da4092347b9a483711e7e4530979bf2b135d03bc77f03599851463c21041dbef6b566c0ba6d151c1d2c516a4ca98caa150592888f2a3c85ec941dd060d
7
- data.tar.gz: 7e4dd50dbd81e87934f04daf4bf94ed89f7bac9f024e23886bc3c401d626137de664aa8a8e087d02cb57f1193309159a5c6a97ac5b9258ab326c787669f427dc
6
+ metadata.gz: 25835d74e2cf54a76f4be490f95fa98e5b0cbde8ddec6bafdd990a17d9a900fa7033f15f63a4be23609ae1164d686132a0472bec2e168d93f19e6204af1ab315
7
+ data.tar.gz: 8dcd77488ddd9f45bba84db7faec4b3e1f37c4258df3dc6273bee7eef1b620799cd725d56338759dc1d488aece3c32102a7364232e03183fa123cef481aa0543
data/README.md CHANGED
@@ -7,24 +7,36 @@ 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 4: A random UUID where the first 112 bits are random. The following
39
+ 16 bits are the UUID type.
28
40
 
29
41
  ## Install
30
42
 
@@ -33,24 +45,28 @@ Add this to your Gemfile:
33
45
  `gem 'typed_uuid'`
34
46
 
35
47
  Once bundled you can add an initializer to Rails to register your types as shown
36
- below. This maps the __table_names__ of the models to an integer between 0 and 255.
48
+ below. This maps the __Model Classes__ to an integer between 0 and 65,535.
37
49
 
38
50
  ```ruby
39
51
  # config/initializers/uuid_types.rb
40
52
 
41
53
  ActiveRecord::Base.register_uuid_types({
42
- listings: 0,
43
- buildings: 65_535
54
+ Listing: 0,
55
+ Address: {enum: 5},
56
+ Building: {enum: 512, version: 1},
57
+ 'Building::SkyScrpaer' => 8_191
44
58
  })
45
59
 
46
60
  # Or:
47
61
 
48
62
  ActiveRecord::Base.register_uuid_types({
49
- 0 => :listings,
50
- 65_535 => :buildings
63
+ 0 => :Listing,
64
+ 512 => :Building,
65
+ 8_191 => 'Building::SkyScrpaer'
51
66
  })
52
67
  ```
53
68
 
69
+
54
70
  ## Usage
55
71
 
56
72
  In your migrations simply replace `id: :uuid` with `id: :typed_uuid` when creating
@@ -64,4 +80,38 @@ class CreateProperties < ActiveRecord::Migration[5.2]
64
80
  end
65
81
  end
66
82
  end
67
- ```
83
+ ```
84
+
85
+ To add a typed UUID to an existing table:
86
+
87
+ ```ruby
88
+ class UpdateProperties < ActiveRecord::Migration[6.1]
89
+ def change
90
+ klass_enum = ::ActiveRecord::Base.uuid_type_from_table_name(:properties)
91
+
92
+ # Add the column
93
+ add_column :properties, :typed_uuid, :uuid, default: -> { "typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}')" }
94
+
95
+ # Update existing properties with a new typed UUID
96
+ execute "UPDATE properties SET id = typed_uuid('\\x#{klass_enum.to_s(16).rjust(4, '0')}');"
97
+
98
+ # Add null constraint since we'll swap these out for the primary key
99
+ change_column_null :properties, :typed_uuid, false
100
+
101
+ # TODO: Here you will want to update any reference to the old primary key
102
+ # with the new typed_uuid that will be the new primary key.
103
+
104
+ # Replace the old primary key with the typed_uuid
105
+ execute "ALTER TABLE properties DROP CONSTRAINT properties_pkey;"
106
+ rename_column :properties, :typed_uuid, :id
107
+ execute "ALTER TABLE properties ADD PRIMARY KEY (id);"
108
+ end
109
+ ```
110
+
111
+ ## STI Models
112
+ When using STI Model Rails will generate the UUID to be inserted. This UUID will
113
+ be calculated of the STI Model class and not the base class.
114
+
115
+ In the migration you can still used `id: :typed_uuid`, this will use the base
116
+ class to calculated the default type for the UUID. You could also set the
117
+ `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
@@ -3,16 +3,50 @@ module TypedUUID
3
3
  autoload :PsqlColumnMethods, 'typed_uuid/psql_column_methods'
4
4
  autoload :PsqlSchemaDumper, 'typed_uuid/psql_schema_dumper'
5
5
 
6
- def self.uuid(enum)
7
- uuid = SecureRandom.random_bytes(16).unpack("NnnnnN")
8
- uuid[2] = (uuid[1] ^ uuid[3]) ^ enum
9
- "%08x-%04x-%04x-%04x-%04x%08x" % uuid
6
+ def self.uuid(enum, version = 4, **options)
7
+ if enum < 0 || enum > 8_191
8
+ raise ArgumentError, "UUID type must be between 0 and 8,191"
9
+ end
10
+
11
+ if version == 1
12
+ timestamp_uuid(enum, **options)
13
+ elsif version == 4
14
+ random_uuid(enum, **options)
15
+ end
16
+ end
17
+
18
+ def self.random_uuid(enum)
19
+ uuid = SecureRandom.random_bytes(16).unpack("nnnnnnnn")
20
+
21
+ uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 4)
22
+ "%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
23
+ end
24
+
25
+ def self.timestamp_uuid(enum, timestamp: nil, sequence: nil)
26
+ timestamp ||= Time.now
27
+
28
+ uuid = [timestamp.to_i * 1_000_000 + timestamp.usec].pack('Q>')[1..-1]
29
+ uuid << (sequence&.pack('Q>') || SecureRandom.random_bytes(10))
30
+
31
+ uuid = uuid.unpack("nnnnnnnn")
32
+ uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 1)
33
+ "%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
10
34
  end
11
35
 
12
36
  def self.enum(uuid)
13
37
  uuid = uuid.gsub('-', '')
14
- (uuid[8..11].to_i(16) ^ uuid[16..19].to_i(16)) ^ uuid[12..15].to_i(16)
38
+ ((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) >> 3
39
+ end
40
+
41
+ def self.version(uuid)
42
+ uuid = uuid.gsub('-', '')
43
+ ((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) & 0b0000000000000111
44
+ end
45
+
46
+ def self.timestamp(uuid)
47
+ uuid = uuid.gsub('-', '')
48
+ Time.at(*uuid[0..13].to_i(16).divmod(1_000_000), :usec)
15
49
  end
16
50
  end
17
51
 
18
- require 'typed_uuid/railtie' if defined? Rails
52
+ require 'typed_uuid/railtie' if defined? Rails
@@ -1,79 +1,132 @@
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 \"%{table}\", but " \
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
- def self.extended(base) # :nodoc:
8
- base.class_attribute(:defined_uuid_types, instance_writer: false, default: {})
9
- base.class_attribute(:uuid_type_cache, instance_writer: false, default: {})
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
+ if type = defined_uuid_types.find { |k, v| v[:class] == klass.name }
14
+ hash[klass] = type[1]
15
+ end
16
+ })
17
+ class_attribute(:uuid_enum_to_class_cache, instance_writer: false, default: {})
10
18
  end
11
19
 
12
- def register_uuid_type(table, int)
13
- if int < 0 || int > 65_535
14
- raise ArgumentError, "UUID type must be between 0 and 65,535"
15
- elsif defined_uuid_types.has_key?(int)
16
- raise ArgumentError, UUID_TYPE_CONFLICT_MESSAGE % {
17
- int: int,
18
- table: table,
19
- other: defined_uuid_types[int]
20
- }
21
- else
22
- defined_uuid_types[int] = table.to_s
20
+ def _create_record
21
+ klass = self.class
22
+ if !klass.descends_from_active_record? && klass.typed?
23
+ pk = klass.primary_key
24
+ write_attribute(pk, klass.typed_uuid) if pk && read_attribute(pk).nil?
23
25
  end
26
+
27
+ super
24
28
  end
25
29
 
26
- def register_uuid_types(mapping)
27
- mapping.each do |k, v|
28
- if k.is_a?(Integer)
29
- register_uuid_type(v, k)
30
+ class_methods do
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
30
35
  else
31
- register_uuid_type(k, v)
36
+ enum = enum_or_options
37
+ version = 4
38
+ end
39
+
40
+ if enum < 0 || enum > 8_191
41
+ raise ArgumentError, "UUID type must be between 0 and 65,535"
42
+ elsif defined_uuid_types.has_key?(enum)
43
+ raise ArgumentError, UUID_TYPE_CONFLICT_MESSAGE % {
44
+ int: enum,
45
+ class_name: class_name,
46
+ other: defined_uuid_types[enum]
47
+ }
48
+ else
49
+ defined_uuid_types[enum] = { class: class_name.to_s, version: version, enum: enum }
32
50
  end
33
51
  end
34
- end
35
-
36
- def typed_uuid
37
- TypedUUID.uuid(uuid_type_from_class(self))
38
- end
39
52
 
40
- def uuid_type_from_table_name(table)
41
- type = defined_uuid_types.key(table.to_s)
42
- if type.nil?
43
- raise ArgumentError, "UUID Type for \"#{table}\" not defined"
53
+ def register_uuid_types(mapping)
54
+ mapping.each do |k, v|
55
+ if k.is_a?(Integer)
56
+ register_uuid_type(v, k)
57
+ else
58
+ register_uuid_type(k, v)
59
+ end
60
+ end
44
61
  end
45
-
46
- type
47
- end
48
62
 
49
- def uuid_type_from_class(klass)
50
- type = defined_uuid_types.key(klass.table_name)
51
- if type.nil?
52
- raise ArgumentError, "UUID Type for \"#{table}\" not defined"
63
+ def typed?
64
+ !!class_to_uuid_type_cache[self.base_class]
53
65
  end
54
66
 
55
- type
56
- end
67
+ def typed_uuid(**options)
68
+ TypedUUID.uuid(uuid_enum_from_class(self), uuid_version_from_class(self), **options)
69
+ end
70
+
71
+ def uuid_enum_from_table_name(table)
72
+ uuid_enum_from_class(class_from_table_name(table))
73
+ end
74
+
75
+ def uuid_version_from_table_name(table)
76
+ uuid_version_from_class(class_from_table_name(table))
77
+ end
57
78
 
58
- def class_from_uuid_type(type)
59
- if klass = uuid_type_cache[type]
60
- return klass
61
- else
62
- Rails.application.eager_load! if !Rails.application.config.eager_load
79
+ def uuid_enum_from_class(klass)
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
87
+
88
+ def uuid_version_from_class(klass)
89
+ type = class_to_uuid_type_cache[klass]
90
+ if type.nil?
91
+ raise ArgumentError, "UUID Type for \"#{klass.name}\" not defined"
92
+ end
63
93
 
64
- ::ActiveRecord::Base.descendants.select do |klass|
94
+ type[:version]
95
+ end
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]
100
+ else
101
+ Rails.application.eager_load! if !Rails.application.config.eager_load
102
+
103
+ ::ActiveRecord::Base.descendants.each do |klass|
104
+ next if klass.table_name.nil?
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
109
+ end
110
+
111
+ uuid_enum_to_class_cache[enum]
112
+ end
113
+ end
114
+
115
+ def class_from_table_name(table)
116
+ table = table.to_s
117
+ Rails.application.eager_load! if !Rails.application.config.eager_load
118
+
119
+ ::ActiveRecord::Base.descendants.find do |klass|
65
120
  next unless ( klass.superclass == ::ActiveRecord::Base || klass.superclass.abstract_class? )
66
121
  next if klass.table_name.nil?
67
-
68
- uuid_type_cache[defined_uuid_types.key(klass.table_name)] = klass
122
+
123
+ klass.table_name == table
69
124
  end
70
-
71
- uuid_type_cache[type]
72
125
  end
73
- end
74
126
 
75
- def class_from_uuid(uuid)
76
- class_from_uuid_type(TypedUUID.enum(uuid))
127
+ def class_from_uuid(uuid)
128
+ class_from_uuid_enum(TypedUUID.enum(uuid))
129
+ end
77
130
  end
78
131
 
79
132
  end
@@ -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
 
@@ -4,7 +4,7 @@ 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.extend TypedUUID::ActiveRecord
7
+ ActiveRecord::Base.include TypedUUID::ActiveRecord
8
8
  end
9
9
 
10
10
  require 'active_record/connection_adapters/postgresql/schema_definitions'
@@ -1,3 +1,3 @@
1
1
  module TypedUUID
2
- VERSION = '2.3'
2
+ VERSION = '4.0.rc2'
3
3
  end
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'
@@ -52,6 +54,10 @@ class ActiveSupport::TestCase
52
54
  set_callback(:setup, :before) do
53
55
  Rails.stubs(:application).returns(stub(config: stub(eager_load: true)))
54
56
  if !self.class.class_variable_defined?(:@@suite_setup_run)
57
+ ActiveRecord::Base.defined_uuid_types.clear
58
+ ActiveRecord::Base.uuid_enum_to_class_cache.clear
59
+ ActiveRecord::Base.class_to_uuid_type_cache.clear
60
+
55
61
  configuration = {
56
62
  adapter: "postgresql",
57
63
  database: "uuid-types-test",
@@ -76,14 +82,14 @@ class ActiveSupport::TestCase
76
82
  self.class.class_variable_set(:@@suite_setup_run, true)
77
83
  end
78
84
 
79
- # def debug
80
- # ActiveRecord::Base.logger = Logger.new(STDOUT)
81
- # $debugging = true
82
- # yield
83
- # ensure
84
- # ActiveRecord::Base.logger = nil
85
- # $debugging = false
86
- # end
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
87
93
 
88
94
  def capture_sql
89
95
  # ActiveRecord::Base.connection.materialize_transactions
@@ -4,8 +4,9 @@ class FilterTest < ActiveSupport::TestCase
4
4
 
5
5
  schema do
6
6
  ActiveRecord::Base.register_uuid_types({
7
- listings: 0,
8
- buildings: 592
7
+ 'FilterTest::Listing' => {enum: 0, version: 1},
8
+ 'FilterTest::Building' => 592,
9
+ 'FilterTest::SkyScraper' => {enum: 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,26 @@ 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
+
37
+ class Lot < ActiveRecord::Base
38
+ end
39
+
26
40
  test 'adding primary key as a typed_uuid in a migration' do
27
41
  ActiveRecord::Base.register_uuid_types({
28
- properties: 1
42
+ 1 => 'FilterTest::Property'
29
43
  })
30
44
 
31
45
  exprexted_sql = <<-SQL
32
- 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))
33
47
  SQL
34
48
 
35
49
  assert_sql exprexted_sql do
@@ -41,29 +55,100 @@ class FilterTest < ActiveSupport::TestCase
41
55
  end
42
56
  end
43
57
 
58
+ test 'adding primary key as a typed_uuid in a migration with a version' do
59
+ ActiveRecord::Base.register_uuid_types({
60
+ 'FilterTest::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
+
44
76
  test 'typed_uuid' do
45
77
  assert_equal 512, TypedUUID.enum(TypedUUID.uuid(512))
46
- assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
47
- assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(Building.typed_uuid)
78
+ assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
79
+ assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(Building.typed_uuid)
80
+ assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(SkyScraper.typed_uuid)
81
+
82
+ assert_raises ArgumentError do
83
+ ::ActiveRecord::Base.class_from_uuid(SingleFamilyHome.typed_uuid)
84
+ end
48
85
  end
49
86
 
50
- test 'class_from uuid' do
87
+ test 'typed_uuid v1' do
88
+ time = Time.at(1606612254, 370979, :usec)
89
+ Time.stubs(:now).returns(time)
90
+
91
+ uuid = TypedUUID.uuid(592, 1)
92
+
93
+ assert_equal 1, TypedUUID.version(uuid)
94
+ assert_equal 592, TypedUUID.enum(uuid)
95
+
96
+ assert_equal time, TypedUUID.timestamp(uuid)
97
+ assert_equal time.to_i, TypedUUID.timestamp(uuid).to_i
98
+ assert_equal time.nsec, TypedUUID.timestamp(uuid).nsec
99
+
100
+ assert_equal Listing, ::ActiveRecord::Base.class_from_uuid(Listing.typed_uuid)
101
+ end
102
+
103
+ test 'class_from uuid generated bye PostgresQL' do
51
104
  listing = Listing.create
52
105
  building = Building.create
106
+ skyscraper = SkyScraper.create
53
107
 
54
108
  assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
55
109
  assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid(building.id)
110
+ assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid(skyscraper.id)
111
+
112
+ assert_raises ArgumentError do
113
+ SingleFamilyHome.create
114
+ end
56
115
  end
57
116
 
58
- test 'uuid_type from table_name' do
59
- assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name(:listings)
60
- assert_equal 0, ::ActiveRecord::Base.uuid_type_from_table_name('listings')
61
- assert_equal 592, ::ActiveRecord::Base.uuid_type_from_table_name(:buildings)
117
+ test 'class_from v1 uuid generated bye PostgresQL' do
118
+ listing = Listing.create
119
+
120
+ assert_equal Listing, ::ActiveRecord::Base.class_from_uuid(listing.id)
121
+ end
122
+
123
+ test 'uuid_enum from table_name' do
124
+ assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_table_name(:listings)
125
+ assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_table_name('listings')
126
+
127
+ assert_equal 592, ::ActiveRecord::Base.uuid_enum_from_table_name(:buildings)
128
+ end
129
+
130
+ test 'uuid_version from table_name' do
131
+ assert_equal 1, ::ActiveRecord::Base.uuid_version_from_table_name(:listings)
132
+ assert_equal 1, ::ActiveRecord::Base.uuid_version_from_table_name('listings')
133
+ assert_equal 4, ::ActiveRecord::Base.uuid_version_from_table_name(:buildings)
134
+ end
135
+
136
+ test 'uuid_enum from class' do
137
+ assert_equal 0, ::ActiveRecord::Base.uuid_enum_from_class(Listing)
138
+ assert_equal 592, ::ActiveRecord::Base.uuid_enum_from_class(Building)
139
+ assert_equal 1_952, ::ActiveRecord::Base.uuid_enum_from_class(SkyScraper)
140
+ end
141
+
142
+ test 'uuid_version from class' do
143
+ assert_equal 1, ::ActiveRecord::Base.uuid_version_from_class(Listing)
144
+ assert_equal 4, ::ActiveRecord::Base.uuid_version_from_class(Building)
145
+ assert_equal 4, ::ActiveRecord::Base.uuid_version_from_class(SkyScraper)
62
146
  end
63
147
 
64
- test 'class from uuid_type' do
65
- assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid_type(0)
66
- assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid_type(592)
148
+ test 'class from uuid_enum' do
149
+ assert_equal FilterTest::Listing, ::ActiveRecord::Base.class_from_uuid_enum(0)
150
+ assert_equal FilterTest::Building, ::ActiveRecord::Base.class_from_uuid_enum(592)
151
+ assert_equal FilterTest::SkyScraper, ::ActiveRecord::Base.class_from_uuid_enum(1_952)
67
152
  end
68
153
 
69
154
  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.3'
4
+ version: 4.0.rc2
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: 2019-12-25 00:00:00.000000000 Z
11
+ date: 2021-04-21 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:
@@ -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
@@ -201,12 +201,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
201
201
  version: '0'
202
202
  required_rubygems_version: !ruby/object:Gem::Requirement
203
203
  requirements:
204
- - - ">="
204
+ - - ">"
205
205
  - !ruby/object:Gem::Version
206
- version: '0'
206
+ version: 1.3.1
207
207
  requirements: []
208
- rubygems_version: 3.0.3
209
- signing_key:
208
+ rubygems_version: 3.2.3
209
+ signing_key:
210
210
  specification_version: 4
211
211
  summary: Typed UUIDs for ActiveRecord
212
212
  test_files: