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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0743664e3f9987059821b25c8c42f80b062bbeeb3ba563e795d4b9845920505
4
- data.tar.gz: ec150decbaced84ca8cbd26b06af761551969ee9cc74145291c552215ab2a94b
3
+ metadata.gz: c9b97eecee8303b2863faf3982c4080d347c9dabc4b0fca45c2c5dc7c74b6172
4
+ data.tar.gz: 72d70bc0bc247987141c6ee4710a5ef34257693179b1d526dcc6fbf427e414f5
5
5
  SHA512:
6
- metadata.gz: e3e66ca9962d22a9ccece4458708c132e776062dc5d10071b715816c4c5329a99c6bea6b172e0f33a55c274a761e0850c1cb4a957b31de2124f3942d60204aa3
7
- data.tar.gz: 502b8f036034be6a080aa0722b26918810589fc741666d2ab4f0c125d987895bc2ba19ab886b79ff13b97e38b50698808dbc28eb80d2b1d822c1dda055f89ad3
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 __table_names__ of the models to an integer between 0 and 255.
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
- listings: 0,
43
- buildings: 65_535
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 => :listings,
50
- 65_535 => :buildings
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
- 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 $$
7
- DECLARE
8
- bytes bytea := gen_random_bytes(16);
9
- uuid bytea;
10
- 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
- RETURN encode( bytes, 'hex') :: uuid;
14
- END;
15
- $$ LANGUAGE plpgsql;
16
- SQL
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
@@ -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 \"%{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
+ 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 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
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
- def register_uuid_types(mapping)
27
- mapping.each do |k, v|
28
- if k.is_a?(Integer)
29
- register_uuid_type(v, k)
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
- register_uuid_type(k, v)
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
- def uuid_type_from_table_name(table)
37
- type = defined_uuid_types.key(table.to_s)
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
- type
43
- end
57
+ def typed_uuid
58
+ TypedUUID.uuid(uuid_type_from_class(self))
59
+ end
44
60
 
45
- def class_from_uuid_type(type)
46
- if klass = uuid_type_cache[type]
47
- return klass
48
- else
49
- # Rails.application.eager_load! if !Rails.application.config.eager_load
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
- ::ActiveRecord::Base.descendants.select do |klass|
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
- uuid_type_cache[defined_uuid_types.key(klass.table_name)] = klass
98
+
99
+ klass.table_name == table
56
100
  end
57
-
58
- uuid_type_cache[type]
59
101
  end
60
- end
61
102
 
62
- def class_from_uuid(uuid)
63
- uuid = uuid.gsub('-', '')
64
- class_from_uuid_type((uuid[8..11].to_i(16) ^ uuid[16..19].to_i(16)) ^ uuid[12..15].to_i(16))
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).ljust(4, '0')}')" }
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
@@ -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.extend TypedUUID::ActiveRecord
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
 
@@ -1,3 +1,3 @@
1
1
  module TypedUUID
2
- VERSION = '2.1'
2
+ VERSION = '3.2'
3
3
  end
@@ -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
- # def debug
79
- # ActiveRecord::Base.logger = Logger.new(STDOUT)
80
- # $debugging = true
81
- # yield
82
- # ensure
83
- # ActiveRecord::Base.logger = nil
84
- # $debugging = false
85
- # 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
86
93
 
87
94
  def capture_sql
88
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: 65_535
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
- properties: 1
39
+ 1 => 'FilterTest::Property'
29
40
  })
30
41
 
31
42
  exprexted_sql = <<-SQL
32
- CREATE TABLE "properties" ("id" uuid DEFAULT typed_uuid('\\x1000') NOT NULL PRIMARY KEY, "name" character varying(255))
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 65_535, ::ActiveRecord::Base.uuid_type_from_table_name(:buildings)
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(65_535)
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
@@ -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.1'
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: 2019-12-24 00:00:00.000000000 Z
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: :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
@@ -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.0.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: