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 +4 -4
- data/CHANGELOG.md +63 -0
- data/README.md +54 -4
- data/lib/rails_uuid_pk/migration_helpers.rb +29 -10
- data/lib/rails_uuid_pk/railtie.rb +28 -40
- data/lib/rails_uuid_pk/type.rb +12 -5
- data/lib/rails_uuid_pk/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44071099c05c09c2bd83cf2c82eac2cd7794990d288d68d8ef775e2ecaa410e2
|
|
4
|
+
data.tar.gz: ba6ad1a7f1a00c2d5201122215c9fcfbd39142088775b24234f2a01c9e25dc05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
[](https://rubygems.org/gems/rails-uuid-pk)
|
|
7
8
|
[](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.
|
|
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
|
|
10
|
-
#
|
|
11
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
data/lib/rails_uuid_pk/type.rb
CHANGED
|
@@ -2,12 +2,12 @@ module RailsUuidPk
|
|
|
2
2
|
module Type
|
|
3
3
|
class Uuid < ActiveRecord::Type::String
|
|
4
4
|
def type
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
if
|
|
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
|