rails-uuid-pk 0.5.0 → 0.7.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: 615c351b860e9cf6b5cc663f8f1a8d4c5781bb6f7f12d658440f42a3173d8fc0
4
- data.tar.gz: 5973fbaed1fb21337bd3e3377c59f7ba410dd50d8837eb444e0c15ead96ddaaf
3
+ metadata.gz: 44071099c05c09c2bd83cf2c82eac2cd7794990d288d68d8ef775e2ecaa410e2
4
+ data.tar.gz: ba6ad1a7f1a00c2d5201122215c9fcfbd39142088775b24234f2a01c9e25dc05
5
5
  SHA512:
6
- metadata.gz: b9a9adf355b72109ed26fa08586983581aa0b4005bbbbc504782a6f5297198b4852f6b664138b24976f2405c464adeba017d177143b15f95761b2901f2aee336
7
- data.tar.gz: bde5e20373b9d07a9013194f3b90faa01cdad6a63de4598b1d1925c0b80694c8416256290f4410d4c4821c1e7ff51e5bcb9281753bcc12f0ec31fa56c06790be
6
+ metadata.gz: caf06da62bc4a35bd6eb8fc578e298d3a1652e89a1ea0bf74118aeb325b780e3f0dc8b7a1b37828967a6ca9287d164631ac4355a2817c9c483a4e4934396adef
7
+ data.tar.gz: 0b6a0d2920550d3d0eb385f4f42dcafd55877a6a603d6347d1d13b6a9171ddd4a74c8f14439125666455e91f2481d57b688bf8e6a18e9dde439c7daaa3918db3
data/CHANGELOG.md CHANGED
@@ -5,6 +5,69 @@ 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.7.0] - 2026-01-12
9
+
10
+ ### Added
11
+ - **SECURITY.md**: Comprehensive security documentation covering UUIDv7-specific security considerations
12
+ - Cryptographic security analysis with timestamp exposure details
13
+ - Database security implications and foreign key considerations
14
+ - Performance-security trade-offs analysis
15
+ - Security vulnerability reporting process
16
+ - Side-channel attack vectors and mitigation strategies
17
+ - Compliance considerations (GDPR, HIPAA, etc.)
18
+ - Security testing recommendations and monitoring guidelines
19
+ - **ARCHITECTURE.md**: Comprehensive architecture documentation
20
+ - Core design principles and architectural decisions
21
+ - App-level vs database-level UUID generation analysis
22
+ - Database compatibility rationale and trade-offs
23
+ - Migration performance implications and caching strategies
24
+ - Database replication and backup considerations
25
+ - ORM and query builder integration details
26
+ - Error handling and resilience patterns
27
+ - Future evolution and extensibility points
28
+ - **PERFORMANCE.md**: Comprehensive performance documentation in dedicated file
29
+ - UUID generation throughput metrics and cryptographic security details
30
+ - Database-specific performance characteristics (PostgreSQL, MySQL, SQLite)
31
+ - Index performance analysis comparing 36-byte UUIDs vs 4-byte integers
32
+ - Scaling recommendations for tables of different sizes (<1M, 1M-10M, >10M records)
33
+ - UUIDv7 vs UUIDv4 performance trade-offs with detailed comparison tables
34
+ - Index fragmentation and cache locality analysis
35
+ - Production monitoring and optimization guidelines
36
+ - **README.md**: Streamlined with concise performance overview and link to PERFORMANCE.md
37
+ - **Comprehensive UUIDv7 Correctness Testing**: Added extensive test suite validating UUIDv7 compliance
38
+ - RFC 9562 version and variant bits validation
39
+ - Timestamp monotonicity and collision resistance testing
40
+ - Format consistency and edge case handling
41
+ - Statistical randomness quality analysis
42
+ - Cross-database compatibility verification
43
+
44
+ ### Changed
45
+ - **Schema Dumper Compatibility**: Replaced fragile `caller` detection with Rails version-aware schema type handling
46
+ - Rails 8.1+: Uses `:uuid` type in schema dumps for native UUID support
47
+ - Rails 8.0.x: Uses `:string` type to avoid "Unknown type 'uuid'" errors
48
+ - Future-proof design that adapts to Rails version changes
49
+ - Added comprehensive test coverage for schema dumping behavior
50
+
51
+ ### Fixed
52
+ - **Schema Dumping Fragility**: Eliminated dependency on Rails internal `caller` stack inspection
53
+ - **Rails Version Compatibility**: Robust handling of UUID types across different Rails versions
54
+
55
+ ### Security
56
+ - Enhanced security posture with professional security documentation
57
+ - Clear vulnerability disclosure process for responsible reporting
58
+ - UUID-specific security guidance for enterprise adoption
59
+
60
+ ## [0.6.0] - 2026-01-12
61
+
62
+ ### Added
63
+ - **Support for `add_reference` and `add_belongs_to`**: Migration helpers now automatically handle foreign key types for these methods as well.
64
+ - **Performance Caching**: Added primary key lookup caching during migrations to improve performance.
65
+
66
+ ### Changed
67
+ - **Improved Migration Helpers**: Enhanced robustness of foreign key type detection by handling more default types (`:bigint`, `:integer`, `nil`).
68
+ - **Refactored Railtie**: Unified UUID type registration for SQLite and MySQL, improving code maintainability.
69
+ - **Better Initialization**: Improved timing of adapter extensions using `ActiveSupport.on_load`.
70
+
8
71
  ## [0.5.0] - 2026-01-10
9
72
 
10
73
  ### Changed
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # rails-uuid-pk
2
2
 
3
- **Dead-simple UUIDv7 primary keys for modern Rails apps**
4
- Works great with **PostgreSQL 18+**, **MySQL 8.0+**, and **SQLite 3.51+** — zero extra extensions required.
3
+ **Dead-simple UUIDv7 primary keys for modern Rails apps**
4
+
5
+ Automatically use UUID v7 for **all primary keys** in Rails applications. Works with PostgreSQL, MySQL, and SQLite — **zero configuration required**. Just add the gem and you're done!
5
6
 
6
7
  [![Gem Version](https://img.shields.io/gem/v/rails-uuid-pk.svg?style=flat-square)](https://rubygems.org/gems/rails-uuid-pk)
7
8
  [![Ruby](https://img.shields.io/badge/ruby-≥3.3-red.svg?style=flat-square)](https://www.ruby-lang.org)
@@ -24,7 +25,7 @@ Works great with **PostgreSQL 18+**, **MySQL 8.0+**, and **SQLite 3.51+** — ze
24
25
  Add to your `Gemfile`:
25
26
 
26
27
  ```ruby
27
- gem "rails-uuid-pk", "~> 0.5"
28
+ gem "rails-uuid-pk", "~> 0.6"
28
29
  ```
29
30
 
30
31
  Then run:
@@ -89,6 +90,24 @@ end
89
90
  | Zero config after install | Yes | Migration helpers automatically handle foreign key types |
90
91
  | Works with Rails 7.1 – 8+ | Yes | Tested conceptually up to Rails 8.1+ |
91
92
 
93
+ ## Performance Overview
94
+
95
+ **Generation**: ~10,000 UUIDs/second with cryptographic security and monotonic ordering
96
+
97
+ | Database | Storage | Index Performance | Notes |
98
+ |----------|---------|-------------------|--------|
99
+ | **PostgreSQL** | Native UUID (16B) | Excellent | Optimal performance |
100
+ | **MySQL** | VARCHAR(36) (36B) | Good | 2.25x storage overhead |
101
+ | **SQLite** | VARCHAR(36) (36B) | Good | Good for development |
102
+
103
+ **Key Advantages**:
104
+ - **UUIDv7 outperforms UUIDv4** in most scenarios due to monotonic ordering
105
+ - **Better index locality** than random UUIDs with reduced fragmentation
106
+ - **Efficient range queries** for time-based data access
107
+ - **Production-ready scaling** with proper indexing and monitoring
108
+
109
+ For comprehensive performance analysis, scaling strategies, and optimization guides, see [PERFORMANCE.md](PERFORMANCE.md).
110
+
92
111
  ## Why not use native PostgreSQL `uuidv7()`?
93
112
 
94
113
  While PostgreSQL 18+ has excellent native `uuidv7()` support, the **fallback approach** was chosen for maximum compatibility:
@@ -104,12 +123,39 @@ You can still add native PostgreSQL defaults manually if you want maximum perfor
104
123
 
105
124
  ### Devcontainer Setup
106
125
 
107
- This project includes a devcontainer configuration for VS Code. To get started:
126
+ This project includes a devcontainer configuration for VS Code (highly recommended, as it automatically sets up Ruby 3.3, Rails, PostgreSQL, MySQL, and SQLite in an isolated environment). To get started:
108
127
 
109
128
  1. Open the project in VS Code
110
129
  2. When prompted, click "Reopen in Container" (or run `Dev Containers: Reopen in Container` from the command palette)
111
130
  3. The devcontainer will set up Ruby 3.3, Rails, and all dependencies automatically
112
131
 
132
+ #### Devcontainer CLI
133
+
134
+ For terminal-based development or automation, you can use the Devcontainer CLI. The devcontainer will be built and started automatically when you run the exec commands.
135
+
136
+ ##### Installation
137
+
138
+ - **MacOS**: `brew install devcontainer`
139
+ - **Other systems**: `npm install -g @devcontainers/cli`
140
+
141
+ ##### Usage
142
+
143
+ Run commands inside the devcontainer:
144
+
145
+ ```bash
146
+ # Install dependencies
147
+ devcontainer exec --workspace-folder . bundle install
148
+
149
+ # Run tests
150
+ devcontainer exec --workspace-folder . ./bin/test
151
+
152
+ # Run code quality checks
153
+ devcontainer exec --workspace-folder . ./bin/rubocop
154
+
155
+ # Interactive shell
156
+ devcontainer exec --workspace-folder . bash
157
+ ```
158
+
113
159
  ### Running Tests
114
160
 
115
161
  The project includes a comprehensive test suite that runs against SQLite, PostgreSQL, and MySQL.
@@ -155,6 +201,10 @@ For database testing, ensure the respective databases are running and accessible
155
201
 
156
202
  Bug reports and pull requests are welcome on GitHub at https://github.com/seouri/rails-uuid-pk.
157
203
 
204
+ Please see our [Security Policy](SECURITY.md) for information about reporting security vulnerabilities.
205
+
206
+ For detailed architecture documentation, design decisions, and technical rationale, see [ARCHITECTURE.md](ARCHITECTURE.md).
207
+
158
208
  ## License
159
209
 
160
210
  The gem is available as open source under the terms of the [MIT License](MIT-LICENSE).
@@ -6,32 +6,51 @@ module RailsUuidPk
6
6
  ref_table = options.delete(:to_table) || ref_name.to_s.pluralize
7
7
 
8
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
9
+ # In Rails, default type is often passed as :integer or :bigint in the options hash
10
+ # depending on the Rails version and whether it is a migration or a table definition.
11
+ # We want to override it to :uuid if the target table uses UUID primary keys.
12
+ current_type = options[:type]
13
+ if (current_type.nil? || current_type == :integer || current_type == :bigint) &&
14
+ (uuid_primary_key?(ref_table) || (options[:polymorphic] && application_uses_uuid_primary_keys?))
12
15
  options[:type] = :uuid
13
16
  end
14
17
 
15
- super
18
+ super(*args, **options)
19
+ end
20
+
21
+ def add_reference(table_name, ref_name, **options)
22
+ ref_table = options.delete(:to_table) || ref_name.to_s.pluralize
23
+
24
+ current_type = options[:type]
25
+ if (current_type.nil? || current_type == :integer || current_type == :bigint) &&
26
+ (uuid_primary_key?(ref_table) || (options[:polymorphic] && application_uses_uuid_primary_keys?))
27
+ options[:type] = :uuid
28
+ end
29
+
30
+ super(table_name, ref_name, **options)
16
31
  end
17
32
 
18
33
  def application_uses_uuid_primary_keys?
19
34
  # Check if the application is configured to use UUID primary keys globally
20
- Rails.application.config.generators.options[:active_record]&.[](:primary_key_type) == :uuid
35
+ defined?(Rails) && Rails.application.config.generators.options[:active_record]&.[](:primary_key_type) == :uuid
21
36
  end
22
37
 
23
38
  alias_method :belongs_to, :references
39
+ alias_method :add_belongs_to, :add_reference
24
40
 
25
41
  private
26
42
 
27
43
  def uuid_primary_key?(table_name)
28
- conn = @conn || @base || (respond_to?(:connection) ? connection : nil)
29
- return false unless conn&.table_exists?(table_name)
44
+ # Cache results for the duration of the migration process to improve performance
45
+ @uuid_pk_cache ||= {}
46
+ return @uuid_pk_cache[table_name] if @uuid_pk_cache.key?(table_name)
30
47
 
31
- pk_column = find_primary_key_column(table_name, conn)
32
- return false unless pk_column
48
+ conn = @conn || @base || (respond_to?(:connection) ? connection : self)
49
+ # Ensure we have a connection-like object that can check for table existence
50
+ return false unless conn.respond_to?(:table_exists?) && conn.table_exists?(table_name)
33
51
 
34
- pk_column.sql_type.downcase.match?(/\A(uuid|varchar\(36\))\z/)
52
+ pk_column = find_primary_key_column(table_name, conn)
53
+ @uuid_pk_cache[table_name] = !!(pk_column && pk_column.sql_type.downcase.match?(/\A(uuid|varchar\(36\))\z/))
35
54
  end
36
55
 
37
56
  def find_primary_key_column(table_name, conn)
@@ -8,54 +8,31 @@ module RailsUuidPk
8
8
 
9
9
  initializer "rails-uuid-pk.configure_type_map", after: "active_record.initialize_database" do
10
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
- elsif ActiveRecord::Base.connection.adapter_name == "MySQL"
25
- # Register the UUID type with ActiveRecord for MySQL
26
- ActiveRecord::Type.register(:uuid, RailsUuidPk::Type::Uuid, adapter: :mysql2)
27
-
28
- # Map varchar SQL type to our custom UUID type (since that's how UUID columns are stored in MySQL)
29
- ActiveRecord::Base.connection.send(:type_map).register_type(/varchar/i) do |sql_type|
30
- RailsUuidPk::Type::Uuid.new
31
- end
32
-
33
- # Also map "uuid" SQL type to our custom UUID type for direct lookups
34
- ActiveRecord::Base.connection.send(:type_map).register_type "uuid" do |sql_type|
35
- RailsUuidPk::Type::Uuid.new
36
- end
11
+ adapter_name = ActiveRecord::Base.connection.adapter_name
12
+ if %w[SQLite MySQL].include?(adapter_name)
13
+ RailsUuidPk::Railtie.register_uuid_type(adapter_name.downcase.to_sym)
37
14
  end
38
15
  end
39
16
  end
40
17
 
41
18
  initializer "rails-uuid-pk.native_types" do
42
- require "active_record/connection_adapters/sqlite3_adapter"
43
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(RailsUuidPk::Sqlite3AdapterExtension)
44
- end
45
-
46
- initializer "rails-uuid-pk.mysql_native_types" do
47
- begin
48
- require "active_record/connection_adapters/mysql2_adapter"
49
- ActiveRecord::ConnectionAdapters::Mysql2Adapter.prepend(RailsUuidPk::Mysql2AdapterExtension)
50
- rescue LoadError
51
- # MySQL adapter not available, skip MySQL-specific initialization
19
+ ActiveSupport.on_load(:active_record) do
20
+ case ActiveRecord::Base.connection.adapter_name
21
+ when "SQLite"
22
+ require "active_record/connection_adapters/sqlite3_adapter"
23
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(RailsUuidPk::Sqlite3AdapterExtension)
24
+ when "MySQL"
25
+ begin
26
+ require "active_record/connection_adapters/mysql2_adapter"
27
+ ActiveRecord::ConnectionAdapters::Mysql2Adapter.prepend(RailsUuidPk::Mysql2AdapterExtension)
28
+ rescue LoadError
29
+ # MySQL adapter not available
30
+ end
31
+ end
52
32
  end
53
33
  end
54
34
 
55
-
56
-
57
35
  initializer "rails-uuid-pk.schema_format" do |app|
58
- # Ensure schema_format is set to :ruby for SQLite (default in Rails)
59
36
  app.config.active_record.schema_format ||= :ruby
60
37
  end
61
38
 
@@ -69,10 +46,21 @@ module RailsUuidPk
69
46
  ActiveSupport.on_load(:active_record) do
70
47
  require "rails_uuid_pk/migration_helpers"
71
48
 
72
- # Include migration helpers for all database adapters
73
49
  ActiveRecord::ConnectionAdapters::TableDefinition.prepend(RailsUuidPk::MigrationHelpers::References)
74
50
  ActiveRecord::ConnectionAdapters::Table.prepend(RailsUuidPk::MigrationHelpers::References)
51
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(RailsUuidPk::MigrationHelpers::References)
75
52
  end
76
53
  end
54
+
55
+ def self.register_uuid_type(adapter)
56
+ ActiveRecord::Type.register(:uuid, RailsUuidPk::Type::Uuid, adapter: adapter)
57
+
58
+ # Get the connection-specific type map
59
+ type_map = ActiveRecord::Base.connection.send(:type_map)
60
+ # Map varchar(36) or varchar SQL type to our custom UUID type
61
+ type_map.register_type(/varchar/i) { RailsUuidPk::Type::Uuid.new }
62
+ # Also map "uuid" SQL type for direct lookups
63
+ type_map.register_type("uuid") { RailsUuidPk::Type::Uuid.new }
64
+ end
77
65
  end
78
66
  end
@@ -2,12 +2,12 @@ module RailsUuidPk
2
2
  module Type
3
3
  class Uuid < ActiveRecord::Type::String
4
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
5
+ # Rails 8.1+ supports UUID types in schema dumping
6
+ # Earlier versions need :string to avoid "Unknown type 'uuid'" errors
7
+ if rails_supports_uuid_in_schema?
10
8
  :uuid
9
+ else
10
+ :string
11
11
  end
12
12
  end
13
13
 
@@ -42,6 +42,13 @@ module RailsUuidPk
42
42
  def valid?(value)
43
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
44
  end
45
+
46
+ def rails_supports_uuid_in_schema?
47
+ # Rails 8.1+ supports UUID types in schema dumping
48
+ # Earlier versions (8.0.x) need :string to avoid "Unknown type 'uuid'" errors
49
+ rails_version = Gem::Version.new(Rails::VERSION::STRING)
50
+ rails_version >= Gem::Version.new("8.1.0")
51
+ end
45
52
  end
46
53
  end
47
54
  end
@@ -1,3 +1,3 @@
1
1
  module RailsUuidPk
2
- VERSION = "0.5.0"
2
+ VERSION = "0.7.0"
3
3
  end
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.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joon Lee