rails-uuid-pk 0.1.0 → 0.3.0

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: 7e1ef0fefbc7527b8c728500ad6280dd8f3fd8ab1aaf3c7fe708b952c8bae326
4
- data.tar.gz: db36f10be02bbac2fe2befb4993489a3b9ad346e4b798a0af1a885c89bd883f4
3
+ metadata.gz: d9552cdab44f5a5a6a4b699e12dc31ee13917ff74f0752f32e823dffed24e2e0
4
+ data.tar.gz: 6ddd7862945d8b13939be92990241539d93eaf754d6c47f2733d66eb2e29597e
5
5
  SHA512:
6
- metadata.gz: d16161f270f32d71e76cb02339954bf3abd378f1d6a652ebfde0f5ab85e44147a1216969dc5b9e47f1a25bc89a37487bdc63dd983d67941c4de0f53c325b699e
7
- data.tar.gz: f4720b5cbd0d71bbdb78fd2157b09bbe6a886264f9b4460274a00cef3141ba18b6fa0c5afaf7b66354bffb2dc49ea92165f6729fe4c1c5c43f8b84985ca61232
6
+ metadata.gz: 72fb9c050567562c0d01793d801d7633d45b26851734cb069b2e70ee74d8bf3387e515f50cb771fdbb46f8ad9b4cb767058739bad45050134ba710a8f6290201
7
+ data.tar.gz: ed3b396026fe6edc6485720d6d80af4d5fd11138652fea133515fcb8d931d9d38cf981553a46689121201922ee41b6fad5829c42c06783109b930b06acf41fda
data/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v1.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2026-01-09
9
+
10
+ ### Added
11
+ - Migration helpers that automatically detect and set UUID foreign key types
12
+ - Smart `references` method that inspects database schema to determine primary key types
13
+ - Automatic UUID type assignment for polymorphic associations when using global UUID primary keys
14
+ - Comprehensive test coverage for migration helper functionality
15
+
16
+ ### Changed
17
+ - Eliminated need for manual editing of Action Text and Active Storage migrations
18
+ - Updated install generator message to reflect automatic foreign key type handling
19
+ - Updated README.md to document the improved zero-config experience
20
+ - Changed "Zero config after install" status from "Yes (mostly)" to "Yes"
21
+
22
+ ### Removed
23
+ - Manual migration editing requirement for Action Text & Active Storage compatibility
24
+
25
+ ## [0.2.0] - 2026-01-07
26
+
27
+ ### Added
28
+ - SQLite schema dumping support for UUID primary keys using Ruby format
29
+ - Custom UUID type implementation for ActiveRecord type casting and validation
30
+ - SQLite3 adapter extension for native UUID database type definitions
31
+
32
+ ### Changed
33
+ - Switched SQLite schema format from SQL to Ruby for improved compatibility and standard Rails behavior
34
+ - Updated test suite to expect Ruby schema format for SQLite databases
35
+
8
36
  ## [0.1.0] - 2026-01-07
9
37
 
10
38
  ### Added
data/README.md CHANGED
@@ -13,6 +13,8 @@ Works great with **PostgreSQL 18+** and **SQLite 3.51+** — zero extra extensio
13
13
  - Automatically sets `:uuid` as default primary key type
14
14
  - Adds reliable `before_create` callback for UUIDv7 generation
15
15
  - Works perfectly on **both PostgreSQL 18+** and **SQLite** (and older PostgreSQL versions too)
16
+ - **PostgreSQL**: Uses native `UUID` column types with full database support
17
+ - **SQLite**: Uses `VARCHAR(36)` with custom ActiveRecord type handling
16
18
  - Zero database extensions needed
17
19
  - Minimal and maintainable — no monkey-patching hell
18
20
 
@@ -21,7 +23,7 @@ Works great with **PostgreSQL 18+** and **SQLite 3.51+** — zero extra extensio
21
23
  Add to your `Gemfile`:
22
24
 
23
25
  ```ruby
24
- gem "rails-uuid-pk", "~> 0.1"
26
+ gem "rails-uuid-pk", "~> 0.3"
25
27
  ```
26
28
 
27
29
  Then run:
@@ -57,8 +59,6 @@ User.create!(name: "Alice") # ← id is automatically a proper UUIDv7
57
59
 
58
60
  ### Action Text & Active Storage
59
61
 
60
- **These Rails engines do NOT automatically respect the `primary_key_type: :uuid` setting** when generating their install migrations.
61
-
62
62
  When you run:
63
63
 
64
64
  ```bash
@@ -66,37 +66,25 @@ rails action_text:install
66
66
  rails active_storage:install
67
67
  ```
68
68
 
69
- You **MUST** manually edit the generated migration and change:
70
-
71
- ```ruby
72
- t.references :record, null: false, polymorphic: true, index: false
73
- # to
74
- t.references :record, null: false, polymorphic: true, index: false, type: :uuid
75
- ```
76
-
77
- Same applies to `active_storage_attachments.record_id`.
78
-
79
- **Without this change** you will get:
69
+ The generated migrations will **automatically use the correct foreign key types** when referencing tables with UUID primary keys. No manual editing required!
80
70
 
81
- - `PG::DatatypeMismatch` errors
82
- - Duplicate key violations on uniqueness indexes
83
- - Association failures
71
+ This works because rails-uuid-pk includes smart migration helpers that detect the primary key type of referenced tables and automatically set `type: :uuid` for foreign keys.
84
72
 
85
- This is a **long-standing Rails limitation** (still present in Rails 8+).
86
- The gem shows a big warning during installation — but double-check every time you install these engines.
73
+ ### Polymorphic associations
87
74
 
88
- ### Other polymorphic associations
75
+ **Polymorphic associations** (like Action Text's `record` references) will automatically use the correct foreign key type based on the referenced table's primary key. If the parent model uses UUID primary keys, the foreign key will be UUID type.
89
76
 
90
- Any **custom polymorphic** association you create manually should also explicitly use `type: :uuid` if the parent models use UUID primary keys.
77
+ For **custom polymorphic associations**, the migration helpers will also automatically detect and set the correct type:
91
78
 
92
79
  ```ruby
93
- # Good
94
- has_many :comments, as: :commentable, foreign_key: { type: :uuid }
95
-
96
- # Risky (may cause type mismatch)
97
- has_many :comments, as: :commentable
80
+ # This will automatically use type: :uuid if the parent models have UUID primary keys
81
+ create_table :comments do |t|
82
+ t.references :commentable, polymorphic: true
83
+ end
98
84
  ```
99
85
 
86
+ No manual `type: :uuid` specification needed!
87
+
100
88
  ## Features / Trade-offs
101
89
 
102
90
  | Feature | Status | Notes |
@@ -105,7 +93,7 @@ has_many :comments, as: :commentable
105
93
  | PostgreSQL 18+ native `uuidv7()` | Not used | Fallback approach — more universal, no extensions needed |
106
94
  | SQLite support | Full | No native function → app-side generation works great |
107
95
  | Index locality / performance | Very good | UUIDv7 is monotonic → almost as good as sequential IDs |
108
- | Zero config after install | Yes (mostly) | Except Action Text / Active Storage migrations |
96
+ | Zero config after install | Yes | Migration helpers automatically handle foreign key types |
109
97
  | Works with Rails 7.1 – 8+ | Yes | Tested conceptually up to Rails 8.1+ |
110
98
 
111
99
  ## Why not use native PostgreSQL `uuidv7()`?
@@ -13,18 +13,15 @@ module RailsUuidPk
13
13
  def show_next_steps
14
14
  say "\nrails-uuid-pk was successfully installed!", :green
15
15
 
16
- say "\n⚠️ IMPORTANT - Action Text & Active Storage compatibility", :red
16
+ say "\n Action Text & Active Storage compatibility", :green
17
17
  say "─────────────────────────────────────────────────────────────"
18
- say "When you run any of these commands:"
18
+ say "Migration helpers now automatically handle foreign key types!"
19
+ say "When you run:"
19
20
  say " rails action_text:install"
20
21
  say " rails active_storage:install"
21
22
  say ""
22
- say "You **MUST** manually edit the generated migration and add:"
23
- say ""
24
- say " t.references :record, null: false, polymorphic: true, index: false, type: :uuid"
25
- say ""
26
- say "Without this change you'll get type mismatch errors (bigint vs uuid)."
27
- say "This is a Rails limitation - see Rails guides for details."
23
+ say "The generated migrations will automatically use the correct UUID types"
24
+ say "for foreign keys. No manual editing required!"
28
25
  say "─────────────────────────────────────────────────────────────\n"
29
26
 
30
27
  say "\nRecommended next steps:", :yellow
@@ -0,0 +1,45 @@
1
+ module RailsUuidPk
2
+ module MigrationHelpers
3
+ module References
4
+ def references(*args, **options)
5
+ ref_name = args.first
6
+ ref_table = options.delete(:to_table) || ref_name.to_s.pluralize
7
+
8
+ # Only set UUID type if not already explicitly set by user
9
+ # Rails sets type: :integer by default, so we override that
10
+ # But if user explicitly set a different type, we respect it
11
+ if (uuid_primary_key?(ref_table) || (options[:polymorphic] && application_uses_uuid_primary_keys?)) && options[:type] == :integer
12
+ options[:type] = :uuid
13
+ end
14
+
15
+ super
16
+ end
17
+
18
+ def application_uses_uuid_primary_keys?
19
+ # Check if the application is configured to use UUID primary keys globally
20
+ Rails.application.config.generators.options[:active_record]&.[](:primary_key_type) == :uuid
21
+ end
22
+
23
+ alias_method :belongs_to, :references
24
+
25
+ private
26
+
27
+ def uuid_primary_key?(table_name)
28
+ conn = @conn || @base || (respond_to?(:connection) ? connection : nil)
29
+ return false unless conn&.table_exists?(table_name)
30
+
31
+ pk_column = find_primary_key_column(table_name, conn)
32
+ return false unless pk_column
33
+
34
+ pk_column.sql_type.downcase.match?(/\A(uuid|varchar\(36\))\z/)
35
+ end
36
+
37
+ def find_primary_key_column(table_name, conn)
38
+ pk_name = conn.primary_key(table_name)
39
+ return nil unless pk_name
40
+
41
+ conn.columns(table_name).find { |c| c.name == pk_name }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -6,34 +6,50 @@ module RailsUuidPk
6
6
  end
7
7
  end
8
8
 
9
- initializer "rails-uuid-pk.configure_database" do |app|
10
- # For SQLite, use SQL schema format since schema.rb has issues with UUID types
11
- if app.config.database_configuration&.dig(Rails.env, "adapter") == "sqlite3"
12
- app.config.active_record.schema_format = :sql
9
+ initializer "rails-uuid-pk.configure_type_map", after: "active_record.initialize_database" do
10
+ ActiveSupport.on_load(:active_record) do
11
+ if ActiveRecord::Base.connection.adapter_name == "SQLite"
12
+ # Register the UUID type with ActiveRecord
13
+ ActiveRecord::Type.register(:uuid, RailsUuidPk::Type::Uuid, adapter: :sqlite3)
14
+
15
+ # Map varchar SQL type to our custom UUID type (since that's how UUID columns are stored in SQLite)
16
+ ActiveRecord::Base.connection.send(:type_map).register_type(/varchar/i) do |sql_type|
17
+ RailsUuidPk::Type::Uuid.new
18
+ end
19
+
20
+ # Also map "uuid" SQL type to our custom UUID type for direct lookups
21
+ ActiveRecord::Base.connection.send(:type_map).register_type "uuid" do |sql_type|
22
+ RailsUuidPk::Type::Uuid.new
23
+ end
24
+ end
13
25
  end
14
26
  end
15
27
 
28
+ initializer "rails-uuid-pk.native_types" do
29
+ require "active_record/connection_adapters/sqlite3_adapter"
30
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(RailsUuidPk::Sqlite3AdapterExtension)
31
+ end
32
+
33
+
34
+
35
+ initializer "rails-uuid-pk.schema_format" do |app|
36
+ # Ensure schema_format is set to :ruby for SQLite (default in Rails)
37
+ app.config.active_record.schema_format ||= :ruby
38
+ end
39
+
16
40
  initializer "rails-uuid-pk.include_concern" do
17
41
  ActiveSupport.on_load(:active_record) do
18
42
  ActiveRecord::Base.include RailsUuidPk::HasUuidv7PrimaryKey
19
43
  end
20
44
  end
21
45
 
22
- initializer "rails-uuid-pk.configure_type_map" do
46
+ initializer "rails-uuid-pk.migration_helpers" do
23
47
  ActiveSupport.on_load(:active_record) do
24
- if ActiveRecord::Base.connection.adapter_name == "SQLite"
25
- # Define a custom UUID type for SQLite that reports as :uuid
26
- uuid_type = Class.new(ActiveRecord::Type::String) do
27
- def type
28
- :uuid
29
- end
30
- end.new
31
-
32
- # Map 'uuid' SQL type to our custom UUID type
33
- ActiveRecord::Base.connection.send(:type_map).register_type "uuid" do |sql_type|
34
- uuid_type
35
- end
36
- end
48
+ require "rails_uuid_pk/migration_helpers"
49
+
50
+ # Include migration helpers for all database adapters
51
+ ActiveRecord::ConnectionAdapters::TableDefinition.prepend(RailsUuidPk::MigrationHelpers::References)
52
+ ActiveRecord::ConnectionAdapters::Table.prepend(RailsUuidPk::MigrationHelpers::References)
37
53
  end
38
54
  end
39
55
  end
@@ -0,0 +1,9 @@
1
+ module RailsUuidPk
2
+ module Sqlite3AdapterExtension
3
+ def native_database_types
4
+ super.merge(
5
+ uuid: { name: "varchar", limit: 36 }
6
+ )
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ module RailsUuidPk
2
+ module Type
3
+ class Uuid < ActiveRecord::Type::String
4
+ def type
5
+ # Return :string during schema dumping to avoid "Unknown type 'uuid'" errors
6
+ # Return :uuid for normal operation and tests
7
+ if caller.any? { |c| c.include?("schema_dumper") }
8
+ :string
9
+ else
10
+ :uuid
11
+ end
12
+ end
13
+
14
+ def deserialize(value)
15
+ return if value.nil?
16
+ cast(value)
17
+ end
18
+
19
+ def cast(value)
20
+ return if value.nil?
21
+ return value if value.is_a?(String) && valid?(value)
22
+
23
+ if value.respond_to?(:to_s)
24
+ str = value.to_s
25
+ return str if valid?(str)
26
+ end
27
+
28
+ # Allow invalid UUIDs to be stored (for backward compatibility and manual id assignment)
29
+ value.to_s
30
+ end
31
+
32
+ def serialize(value)
33
+ cast(value)
34
+ end
35
+
36
+ def changed_in_place?(raw_old_value, new_value)
37
+ cast(raw_old_value) != cast(new_value)
38
+ end
39
+
40
+ private
41
+
42
+ def valid?(value)
43
+ value.match?(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsUuidPk
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/rails_uuid_pk.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "rails_uuid_pk/version"
2
2
  require "rails_uuid_pk/concern"
3
+ require "rails_uuid_pk/type"
4
+ require "rails_uuid_pk/sqlite3_adapter_extension"
3
5
  require "rails_uuid_pk/railtie"
4
6
 
5
7
  module RailsUuidPk
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-uuid-pk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joon Lee
@@ -68,7 +68,10 @@ files:
68
68
  - lib/generators/rails_uuid_pk/install/templates/has_uuidv7_primary_key.rb
69
69
  - lib/rails_uuid_pk.rb
70
70
  - lib/rails_uuid_pk/concern.rb
71
+ - lib/rails_uuid_pk/migration_helpers.rb
71
72
  - lib/rails_uuid_pk/railtie.rb
73
+ - lib/rails_uuid_pk/sqlite3_adapter_extension.rb
74
+ - lib/rails_uuid_pk/type.rb
72
75
  - lib/rails_uuid_pk/version.rb
73
76
  - lib/tasks/rails_uuid_pk_tasks.rake
74
77
  homepage: https://github.com/seouri/rails-uuid-pk