sqlite_crypto 1.0.3 → 2.0.1
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 +43 -0
- data/README.md +108 -183
- data/lib/sqlite_crypto/configuration.rb +32 -0
- data/lib/sqlite_crypto/generators/install_generator.rb +37 -0
- data/lib/sqlite_crypto/generators/templates/initializer.rb.tt +13 -0
- data/lib/sqlite_crypto/generators/uuid.rb +45 -0
- data/lib/sqlite_crypto/migration_helpers.rb +5 -0
- data/lib/sqlite_crypto/model_extensions.rb +1 -1
- data/lib/sqlite_crypto/railtie.rb +8 -4
- data/lib/sqlite_crypto/schema_definitions.rb +4 -2
- data/lib/sqlite_crypto/type/ulid.rb +3 -1
- data/lib/sqlite_crypto/type/uuid.rb +3 -1
- data/lib/sqlite_crypto/version.rb +1 -1
- data/lib/sqlite_crypto.rb +14 -3
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7728bff2a398f6ed1fe0ce0888055586b7619d45954728240ef32ba33cd1650
|
|
4
|
+
data.tar.gz: fb5715411594706283fd93969b4f97709f468870f428105c1cb5ebdefb8d5c56
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d0efcd9f607384d1e33ee2c55643408cae5d4a0382885dee40ef6bc0dc142f62d4085cc85f529fd2e76694ffc624651aa7802f85bc0de789716a0b3d7c1aa27
|
|
7
|
+
data.tar.gz: 9954abb7f5068e1b85f9e636ce1abe0d921baa94b9b7394711745bba9bf349655611dcbd925b6ba0fe1c4d1405f2ae434bf863c7234ed864de17b5748b3efadd
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,49 @@ 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/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.1] - 2026-01-14
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
- **CRITICAL:**
|
|
12
|
+
- Fixed regex anchor vulnerability in UUID/ULID validators
|
|
13
|
+
- Fixed action_text-trix XSS vulnerability by updating the version to '>= 2.1.16'
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Eager configuration validation**: Setting `uuid_version = :v7` on Ruby < 3.3 now fails at boot time with clear error message instead of runtime failure
|
|
17
|
+
- **Namespace cleanup**: `uuid` and `ulid` schema helpers now scoped to `ActiveRecord::Schema` instead of global `Object`
|
|
18
|
+
- **Performance**: Cached Ruby version check in `V7_AVAILABLE` constant (eliminates repeated `Gem::Version` comparisons)
|
|
19
|
+
- **Boot time**: Added lazy loading with `ActiveSupport.on_load(:active_record)` hook for faster Rails initialization
|
|
20
|
+
- **Migration performance**: Memoized primary key type detection to avoid redundant database queries
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
- Unused `SqliteCrypto::Error` class
|
|
24
|
+
- Unused `SqliteCrypto.load_extensions` placeholder method
|
|
25
|
+
|
|
26
|
+
### Internal
|
|
27
|
+
- Simplified configuration defaults to reuse `Generators::Uuid.v7_available?` logic
|
|
28
|
+
- Improved code organization and removed dead code
|
|
29
|
+
|
|
30
|
+
## [2.0.0] - 2026-01-08
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **UUIDv7 Support**: Time-sortable UUIDs with better database index performance
|
|
34
|
+
- New configuration option: `config.uuid_version = :v7` (default) or `:v4`
|
|
35
|
+
- Run `rails generate sqlite_crypto:install` to create initializer
|
|
36
|
+
- UUIDv7 requires Ruby 3.3+ (graceful error on older versions)
|
|
37
|
+
- `generates_uuid` now respects the configured version
|
|
38
|
+
- **Configuration System**: `SqliteCrypto.configure` block for gem settings
|
|
39
|
+
- **Install Generator**: `rails generate sqlite_crypto:install` creates initializer
|
|
40
|
+
- **Version Detection**: `SqliteCrypto::Generators::Uuid.v7_available?` helper
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **Default UUID version changed to v7** for new projects (better performance)
|
|
44
|
+
- `generates_uuid` now uses centralized generator instead of direct `SecureRandom.uuid`
|
|
45
|
+
|
|
46
|
+
### Notes
|
|
47
|
+
- **Ruby 3.1/3.2 users**: Set `config.uuid_version = :v4` in initializer
|
|
48
|
+
- No schema changes required - UUIDv4 and v7 are storage-compatible
|
|
49
|
+
- Existing v4 UUIDs work alongside new v7 UUIDs in same table
|
|
50
|
+
|
|
8
51
|
## [1.0.3] - 2025-12-30
|
|
9
52
|
|
|
10
53
|
### Added
|
data/README.md
CHANGED
|
@@ -1,281 +1,206 @@
|
|
|
1
|
-
# SQLite
|
|
1
|
+
# SQLite Crypto
|
|
2
2
|
|
|
3
|
-
[](https://github.com/bart-oz/sqlite_crypto/releases)
|
|
4
4
|
[](LICENSE.txt)
|
|
5
|
+
[](https://github.com/bart-oz/sqlite_crypto)
|
|
5
6
|
[](https://github.com/bart-oz/sqlite_crypto/actions)
|
|
6
|
-
[](https://github.com/bart-oz/sqlite_crypto/actions)
|
|
7
8
|
[](https://github.com/bart-oz/sqlite_crypto)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## Overview
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Seamless UUID and ULID primary key support for Rails applications using SQLite3. Handles type registration, validation, foreign key detection, and schema generation automatically.
|
|
12
13
|
|
|
14
|
+
```ruby
|
|
15
|
+
create_table :users, id: :uuid do |t|
|
|
16
|
+
t.string :email
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
13
19
|
```
|
|
14
|
-
INTEGER: 1, 2, 3, ... (sequential, guessable)
|
|
15
|
-
UUID: 550e8400-e29b-41d4-a716-446655440000 (random, 36 chars)
|
|
16
|
-
ULID: 01ARZ3NDEKTSV4RRFFQ69G5FAV (time-sortable, 26 chars)
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Why Use UUID/ULID Instead of Integer IDs?
|
|
20
|
-
|
|
21
|
-
| | **Integer** | **UUID** | **ULID** |
|
|
22
|
-
|---|-------------|----------|----------|
|
|
23
|
-
| **Performance** | Baseline | +2-5% slower | +5-10% slower |
|
|
24
|
-
| **Storage** | 8 bytes | 36 bytes (4.5x) | 26 bytes (3.2x) |
|
|
25
|
-
| **Security** | Guessable | Random | Random |
|
|
26
|
-
| **Collisions** | ⚠️ High in distributed systems | Virtually impossible | Virtually impossible |
|
|
27
|
-
| **Sortable** | Sequential | Random | Time-based |
|
|
28
|
-
| **Distributed** | Needs coordination | Generate anywhere | Generate anywhere |
|
|
29
20
|
|
|
30
|
-
**
|
|
21
|
+
**Key capabilities:**
|
|
22
|
+
- UUID primary keys (v4 random or v7 time-ordered)
|
|
23
|
+
- ULID primary keys (time-sortable, compact)
|
|
24
|
+
- Automatic foreign key type detection
|
|
25
|
+
- Model-level ID generation
|
|
26
|
+
- Clean schema.rb output
|
|
27
|
+
- Zero external dependencies
|
|
31
28
|
|
|
32
|
-
##
|
|
29
|
+
## Compatibility
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|--------------|-----------|-----------|-----------|-----------|
|
|
36
|
-
| 3.1 | ✅ | ✅ | ❌ | ❌ |
|
|
37
|
-
| 3.2 | ✅ | ✅ | ✅ | ✅ |
|
|
38
|
-
| 3.3 | ✅ | ✅ | ✅ | ✅ |
|
|
39
|
-
| 3.4 | ✅ | ✅ | ✅ | ✅ |
|
|
40
|
-
| 4.0 | ✅ | ✅ | ✅ | ✅ |
|
|
31
|
+
**Ruby & Rails:**
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
| | Rails 7.1 | Rails 7.2 | Rails 8.0 | Rails 8.1 |
|
|
34
|
+
|-------|-----------|-----------|-----------|-----------|
|
|
35
|
+
| Ruby 3.1 | ✓ | ✓ | - | - |
|
|
36
|
+
| Ruby 3.2 | ✓ | ✓ | ✓ | ✓ |
|
|
37
|
+
| Ruby 3.3 | ✓ | ✓ | ✓ | ✓ |
|
|
38
|
+
| Ruby 3.4 | ✓ | ✓ | ✓ | ✓ |
|
|
39
|
+
| Ruby 4.0 | ✓ | ✓ | ✓ | ✓ |
|
|
43
40
|
|
|
44
|
-
**
|
|
41
|
+
**UUID Versions:**
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
| Version | Ruby 3.1/3.2 | Ruby 3.3+ |
|
|
44
|
+
|---------|--------------|-----------|
|
|
45
|
+
| v4 (random) | ✓ | ✓ |
|
|
46
|
+
| v7 (time-ordered) | - | ✓ |
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
* ULID primary keys with time-sortable validation
|
|
50
|
-
* Migration DSL helpers (`t.uuid`, `t.ulid`)
|
|
51
|
-
* Automatic foreign key type detection
|
|
52
|
-
* Model extensions for UUID/ULID generation
|
|
53
|
-
* Clean schema.rb output
|
|
54
|
-
* Zero configuration required
|
|
48
|
+
**Database:** SQLite3
|
|
55
49
|
|
|
56
50
|
## Installation
|
|
57
51
|
|
|
58
|
-
Add to your Gemfile:
|
|
59
|
-
|
|
60
52
|
```ruby
|
|
53
|
+
# Gemfile
|
|
61
54
|
gem "sqlite_crypto"
|
|
62
55
|
```
|
|
63
56
|
|
|
64
|
-
Then run:
|
|
65
|
-
|
|
66
57
|
```bash
|
|
67
58
|
bundle install
|
|
59
|
+
rails generate sqlite_crypto:install
|
|
68
60
|
```
|
|
69
61
|
|
|
70
|
-
|
|
62
|
+
The generator creates `config/initializers/sqlite_crypto.rb` for configuration.
|
|
71
63
|
|
|
72
|
-
##
|
|
64
|
+
## Configuration
|
|
73
65
|
|
|
74
|
-
|
|
66
|
+
**1. Configure UUID Version**
|
|
75
67
|
|
|
76
68
|
```ruby
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
t.string :email
|
|
81
|
-
t.string :name
|
|
82
|
-
t.timestamps
|
|
83
|
-
end
|
|
84
|
-
end
|
|
69
|
+
# config/initializers/sqlite_crypto.rb
|
|
70
|
+
SqliteCrypto.configure do |config|
|
|
71
|
+
config.uuid_version = :v7 # or :v4
|
|
85
72
|
end
|
|
86
73
|
```
|
|
87
74
|
|
|
88
|
-
|
|
75
|
+
The gem automatically selects a default based on your Ruby version (v7 for Ruby 3.3+, v4 otherwise).
|
|
76
|
+
|
|
77
|
+
**2. Create Tables with UUID/ULID**
|
|
89
78
|
|
|
90
79
|
```ruby
|
|
91
|
-
class
|
|
80
|
+
class CreateUsers < ActiveRecord::Migration[8.0]
|
|
92
81
|
def change
|
|
93
|
-
create_table :
|
|
94
|
-
t.string :
|
|
95
|
-
t.text :content
|
|
82
|
+
create_table :users, id: :uuid do |t|
|
|
83
|
+
t.string :email
|
|
96
84
|
t.timestamps
|
|
97
85
|
end
|
|
98
86
|
end
|
|
99
87
|
end
|
|
100
88
|
```
|
|
101
89
|
|
|
102
|
-
|
|
90
|
+
**3. Use Your Models**
|
|
103
91
|
|
|
104
92
|
```ruby
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
change_table :orders do |t|
|
|
108
|
-
t.uuid :external_id
|
|
109
|
-
t.ulid :tracking_number
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
93
|
+
user = User.create!(email: "test@example.com")
|
|
94
|
+
user.id # => "018d3f91-8f4a-7000-9e7b-4a5c8d2e1f3a"
|
|
113
95
|
```
|
|
114
96
|
|
|
115
|
-
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
**Primary Keys**
|
|
116
100
|
|
|
117
|
-
|
|
101
|
+
Create tables with UUID or ULID primary keys:
|
|
118
102
|
|
|
119
103
|
```ruby
|
|
120
|
-
#
|
|
104
|
+
# UUID
|
|
121
105
|
create_table :users, id: :uuid do |t|
|
|
122
|
-
t.string :
|
|
106
|
+
t.string :email
|
|
123
107
|
end
|
|
124
108
|
|
|
125
|
-
#
|
|
126
|
-
create_table :posts do |t|
|
|
127
|
-
t.references :user # Automatically creates varchar(36) foreign key!
|
|
109
|
+
# ULID
|
|
110
|
+
create_table :posts, id: :ulid do |t|
|
|
128
111
|
t.string :title
|
|
129
112
|
end
|
|
130
113
|
```
|
|
131
114
|
|
|
132
|
-
|
|
115
|
+
**Foreign Keys**
|
|
116
|
+
|
|
117
|
+
Foreign key types are automatically detected from parent tables:
|
|
133
118
|
|
|
134
119
|
```ruby
|
|
135
|
-
|
|
136
|
-
create_table :categories, id: :ulid do |t|
|
|
120
|
+
create_table :users, id: :uuid do |t|
|
|
137
121
|
t.string :name
|
|
138
122
|
end
|
|
139
123
|
|
|
140
|
-
# Articles automatically get varchar(26) category_id foreign key
|
|
141
|
-
create_table :articles do |t|
|
|
142
|
-
t.references :category # Automatically creates varchar(26) foreign key!
|
|
143
|
-
t.string :title
|
|
144
|
-
end
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Custom Table Names
|
|
148
|
-
|
|
149
|
-
Use `:to_table` option for non-standard table names:
|
|
150
|
-
|
|
151
|
-
```ruby
|
|
152
124
|
create_table :posts do |t|
|
|
153
|
-
t.references :
|
|
125
|
+
t.references :user # Automatically varchar(36)
|
|
154
126
|
t.string :title
|
|
155
127
|
end
|
|
156
128
|
```
|
|
157
129
|
|
|
158
|
-
|
|
130
|
+
**Model-Level Generation**
|
|
159
131
|
|
|
160
|
-
|
|
132
|
+
Generate UUIDs or ULIDs for any column:
|
|
161
133
|
|
|
162
134
|
```ruby
|
|
163
135
|
class User < ApplicationRecord
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
class Order < ApplicationRecord
|
|
169
|
-
# Generate ULID for 'reference' column with uniqueness validation
|
|
170
|
-
generates_ulid :reference, unique: true
|
|
136
|
+
generates_uuid :api_token
|
|
137
|
+
generates_ulid :tracking_id, unique: true
|
|
171
138
|
end
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**Features:**
|
|
175
|
-
- `generates_uuid(attribute, unique: false)` - Generates SecureRandom.uuid
|
|
176
|
-
- `generates_ulid(attribute, unique: false)` - Generates time-sortable ULID
|
|
177
|
-
- `unique: true` - Adds uniqueness validation
|
|
178
|
-
- Preserves existing values (won't overwrite if already set)
|
|
179
|
-
- Works with any string column, not just primary keys
|
|
180
|
-
|
|
181
|
-
**Example migration:**
|
|
182
139
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
add_column :users, :token, :string, limit: 36
|
|
187
|
-
add_index :users, :token, unique: true
|
|
188
|
-
end
|
|
189
|
-
end
|
|
140
|
+
user = User.create!
|
|
141
|
+
user.api_token # => "550e8400-e29b-41d4-a716-446655440000"
|
|
142
|
+
user.tracking_id # => "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
190
143
|
```
|
|
191
144
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
Your `db/schema.rb` will be clean and readable:
|
|
195
|
-
|
|
196
|
-
```ruby
|
|
197
|
-
create_table "users", id: :uuid, force: :cascade do |t|
|
|
198
|
-
t.string "email"
|
|
199
|
-
t.timestamps
|
|
200
|
-
end
|
|
145
|
+
## ID Types
|
|
201
146
|
|
|
202
|
-
|
|
203
|
-
t.string "user_id", limit: 36 # Clean foreign key
|
|
204
|
-
t.string "title"
|
|
205
|
-
end
|
|
206
|
-
```
|
|
147
|
+
**Characteristics**
|
|
207
148
|
|
|
208
|
-
|
|
149
|
+
| Type | Length | Format | Ordering | Ruby Version |
|
|
150
|
+
|------|--------|--------|----------|--------------|
|
|
151
|
+
| Integer | 8 bytes | Sequential numbers | Sequential | Any |
|
|
152
|
+
| UUIDv4 | 36 chars | `xxxxxxxx-xxxx-4xxx-...` | Random | 3.1+ |
|
|
153
|
+
| UUIDv7 | 36 chars | `xxxxxxxx-xxxx-7xxx-...` | Time-based | 3.3+ |
|
|
154
|
+
| ULID | 26 chars | `01ARZ3NDEK...` | Time-based | 3.1+ |
|
|
209
155
|
|
|
210
|
-
|
|
211
|
-
2. **Validation**: UUIDs validate 36-char format, ULIDs validate 26-char format
|
|
212
|
-
3. **Migration Helpers**: `t.uuid()` and `t.ulid()` methods in migrations
|
|
213
|
-
4. **Smart References**: `t.references` detects parent table's primary key type
|
|
214
|
-
5. **Model Extensions**: `generates_uuid` and `generates_ulid` for automatic generation
|
|
215
|
-
6. **Schema Dumper**: Outputs clean `id: :uuid` instead of verbose type definitions
|
|
156
|
+
**Performance**
|
|
216
157
|
|
|
217
|
-
|
|
158
|
+
Benchmarks with 10,000 records on SQLite3:
|
|
218
159
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
160
|
+
| Type | Insert | Query | Index Size |
|
|
161
|
+
|------|--------|-------|------------|
|
|
162
|
+
| Integer | 1.00x | 1.00x | 100% |
|
|
163
|
+
| UUIDv4 | 1.02x | 1.03x | 145% |
|
|
164
|
+
| UUIDv7 | 1.01x | 1.03x | 145% |
|
|
165
|
+
| ULID | 1.05x | 1.04x | 130% |
|
|
222
166
|
|
|
223
|
-
|
|
167
|
+
Run your own: `bundle exec rspec --tag performance`
|
|
224
168
|
|
|
225
|
-
|
|
169
|
+
## Advanced Usage
|
|
226
170
|
|
|
227
|
-
|
|
171
|
+
**Non-Standard Table Names**
|
|
228
172
|
|
|
229
173
|
```ruby
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
# posts: id (integer), user_id (integer)
|
|
233
|
-
|
|
234
|
-
# New tables use UUID/ULID
|
|
235
|
-
create_table :invoices, id: :uuid do |t|
|
|
236
|
-
t.references :user # Still integer (auto-detected from users table)
|
|
237
|
-
t.decimal :amount
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
create_table :sessions, id: :ulid do |t|
|
|
241
|
-
t.references :user # Still integer
|
|
242
|
-
t.string :token
|
|
174
|
+
create_table :posts do |t|
|
|
175
|
+
t.references :author, to_table: :users
|
|
243
176
|
end
|
|
244
177
|
```
|
|
245
178
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
### ID Prefixes (Optional)
|
|
249
|
-
|
|
250
|
-
For Stripe-style prefixed IDs (`inv_`, `usr_`, etc.), add to your models:
|
|
179
|
+
**Mixed ID Types**
|
|
251
180
|
|
|
252
181
|
```ruby
|
|
253
|
-
|
|
254
|
-
|
|
182
|
+
create_table :users, id: :uuid do |t|
|
|
183
|
+
t.string :email
|
|
184
|
+
end
|
|
255
185
|
|
|
256
|
-
|
|
186
|
+
create_table :sessions, id: :ulid do |t|
|
|
187
|
+
t.references :user
|
|
188
|
+
end
|
|
257
189
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
end
|
|
190
|
+
create_table :logs do |t| # Integer ID
|
|
191
|
+
t.text :message
|
|
261
192
|
end
|
|
262
193
|
```
|
|
263
194
|
|
|
264
|
-
|
|
195
|
+
## Migrating Existing Apps
|
|
265
196
|
|
|
266
|
-
|
|
197
|
+
Add UUID/ULID to new tables while keeping integer IDs on existing tables:
|
|
267
198
|
|
|
268
199
|
```ruby
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
create_table :sessions, id: :ulid do |t|
|
|
274
|
-
t.string :token
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
create_table :logs do |t| # Standard integer ID
|
|
278
|
-
t.string :message
|
|
200
|
+
# Existing tables unchanged
|
|
201
|
+
create_table :invoices, id: :uuid do |t|
|
|
202
|
+
t.references :user # Detects integer from users table
|
|
203
|
+
t.decimal :amount
|
|
279
204
|
end
|
|
280
205
|
```
|
|
281
206
|
|
|
@@ -289,8 +214,8 @@ bundle exec standardrb
|
|
|
289
214
|
|
|
290
215
|
## Contributing
|
|
291
216
|
|
|
292
|
-
|
|
217
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/bart-oz/sqlite_crypto.
|
|
293
218
|
|
|
294
219
|
## License
|
|
295
220
|
|
|
296
|
-
|
|
221
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SqliteCrypto
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_reader :uuid_version
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
# Default to v7 on Ruby 3.3+, v4 on older versions
|
|
9
|
+
@uuid_version = Generators::Uuid.v7_available? ? :v7 : :v4
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def uuid_version=(version)
|
|
13
|
+
validate_uuid_version!(version)
|
|
14
|
+
@uuid_version = version
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def validate_uuid_version!(version)
|
|
20
|
+
valid_versions = [:v4, :v7]
|
|
21
|
+
unless valid_versions.include?(version)
|
|
22
|
+
raise ArgumentError, "Invalid UUID version: #{version}. Must be one of #{valid_versions.join(", ")}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if version == :v7 && !Generators::Uuid.v7_available?
|
|
26
|
+
raise ArgumentError,
|
|
27
|
+
"UUIDv7 requires Ruby 3.3+. Current: #{RUBY_VERSION}. " \
|
|
28
|
+
"Use config.uuid_version = :v4 or upgrade Ruby."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/base"
|
|
5
|
+
|
|
6
|
+
module SqliteCrypto
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
desc "Creates SqliteCrypto initializer for UUID/ULID configuration"
|
|
12
|
+
|
|
13
|
+
def create_initializer_file
|
|
14
|
+
template "initializer.rb.tt", "config/initializers/sqlite_crypto.rb"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def display_post_install_message
|
|
18
|
+
say ""
|
|
19
|
+
say "SqliteCrypto installed!", :green
|
|
20
|
+
say ""
|
|
21
|
+
say "Configuration created at config/initializers/sqlite_crypto.rb"
|
|
22
|
+
say ""
|
|
23
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.3.0")
|
|
24
|
+
say "✓ Ruby #{RUBY_VERSION} detected - UUIDv7 is available (default)", :green
|
|
25
|
+
else
|
|
26
|
+
say "⚠ Ruby #{RUBY_VERSION} detected - UUIDv7 requires Ruby 3.3+", :yellow
|
|
27
|
+
say " Update initializer to use: config.uuid_version = :v4"
|
|
28
|
+
end
|
|
29
|
+
say ""
|
|
30
|
+
say "Next steps:"
|
|
31
|
+
say " 1. Review config/initializers/sqlite_crypto.rb"
|
|
32
|
+
say " 2. Use id: :uuid in migrations for UUID primary keys"
|
|
33
|
+
say ""
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
SqliteCrypto.configure do |config|
|
|
4
|
+
# UUID generation version: :v4 (random) or :v7 (time-sortable)
|
|
5
|
+
#
|
|
6
|
+
# :v7 - Time-sortable UUIDs with better database index performance
|
|
7
|
+
# Recommended for new projects. Requires Ruby 3.3+
|
|
8
|
+
#
|
|
9
|
+
# :v4 - Random UUIDs, works on all Ruby versions (3.1+)
|
|
10
|
+
# Use if you need Ruby 3.1/3.2 compatibility
|
|
11
|
+
#
|
|
12
|
+
config.uuid_version = :v7 # UUIDv7 is set as default
|
|
13
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module SqliteCrypto
|
|
6
|
+
module Generators
|
|
7
|
+
class Uuid
|
|
8
|
+
MINIMUM_RUBY_FOR_V7 = Gem::Version.new("3.3.0")
|
|
9
|
+
V7_AVAILABLE = (Gem::Version.new(RUBY_VERSION) >= MINIMUM_RUBY_FOR_V7)
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def generate(version: SqliteCrypto.config.uuid_version)
|
|
13
|
+
case version
|
|
14
|
+
when :v4
|
|
15
|
+
generate_v4
|
|
16
|
+
when :v7
|
|
17
|
+
generate_v7
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "Unsupported UUID version: #{version}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def v7_available?
|
|
24
|
+
V7_AVAILABLE
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def generate_v4
|
|
30
|
+
SecureRandom.uuid
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_v7
|
|
34
|
+
if v7_available?
|
|
35
|
+
SecureRandom.uuid_v7
|
|
36
|
+
else
|
|
37
|
+
raise ArgumentError, "UUIDv7 requires Ruby 3.3+. " \
|
|
38
|
+
"Current: Ruby #{RUBY_VERSION}. " \
|
|
39
|
+
"Use config.uuid_version = :v4 or upgrade Ruby."
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -32,6 +32,11 @@ module SqliteCrypto
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
def detect_primary_key_type(table_name)
|
|
35
|
+
@pk_type_cache ||= {}
|
|
36
|
+
@pk_type_cache[table_name] ||= fetch_primary_key_type(table_name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def fetch_primary_key_type(table_name)
|
|
35
40
|
conn = @conn || @base || (respond_to?(:connection) ? connection : nil)
|
|
36
41
|
return nil unless conn&.table_exists?(table_name)
|
|
37
42
|
|
|
@@ -9,7 +9,7 @@ module SqliteCrypto
|
|
|
9
9
|
module ClassMethods
|
|
10
10
|
def generates_uuid(attribute, unique: false)
|
|
11
11
|
before_create do
|
|
12
|
-
self[attribute] ||=
|
|
12
|
+
self[attribute] ||= SqliteCrypto::Generators::Uuid.generate
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
validates attribute, uniqueness: true if unique
|
|
@@ -10,13 +10,17 @@ module SqliteCrypto
|
|
|
10
10
|
config.sqlite_crypto = ActiveSupport::OrderedOptions.new
|
|
11
11
|
|
|
12
12
|
initializer "sqlite_crypto.register_types" do
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
ActiveSupport.on_load(:active_record) do
|
|
14
|
+
ActiveRecord::Type.register(:uuid, SqliteCrypto::Type::Uuid, adapter: :sqlite3)
|
|
15
|
+
ActiveRecord::Type.register(:ulid, SqliteCrypto::Type::ULID, adapter: :sqlite3)
|
|
16
|
+
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
initializer "sqlite_crypto.native_types" do
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
ActiveSupport.on_load(:active_record) do
|
|
21
|
+
require "active_record/connection_adapters/sqlite3_adapter"
|
|
22
|
+
ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(SqliteCrypto::Sqlite3AdapterExtension)
|
|
23
|
+
end
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
initializer "sqlite_crypto.schema_dumper", after: "active_record.initialize_database" do
|
|
@@ -13,5 +13,7 @@ module SqliteCrypto
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# Extend
|
|
17
|
-
|
|
16
|
+
# Extend ActiveRecord::Schema context for schema.rb loading (not global Object)
|
|
17
|
+
if defined?(ActiveRecord::Schema)
|
|
18
|
+
ActiveRecord::Schema.include(SqliteCrypto::SchemaDefinitions)
|
|
19
|
+
end
|
|
@@ -5,6 +5,8 @@ require "sqlite_crypto/type/base"
|
|
|
5
5
|
module SqliteCrypto
|
|
6
6
|
module Type
|
|
7
7
|
class ULID < Base
|
|
8
|
+
ULID_PATTERN = /\A[0-7][0-9A-Z]{25}\z/i
|
|
9
|
+
|
|
8
10
|
def type
|
|
9
11
|
:ulid
|
|
10
12
|
end
|
|
@@ -12,7 +14,7 @@ module SqliteCrypto
|
|
|
12
14
|
private
|
|
13
15
|
|
|
14
16
|
def valid?(value)
|
|
15
|
-
|
|
17
|
+
ULID_PATTERN.match?(value)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
|
@@ -5,6 +5,8 @@ require "sqlite_crypto/type/base"
|
|
|
5
5
|
module SqliteCrypto
|
|
6
6
|
module Type
|
|
7
7
|
class Uuid < Base
|
|
8
|
+
UUID_PATTERN = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
|
|
9
|
+
|
|
8
10
|
def type
|
|
9
11
|
:uuid
|
|
10
12
|
end
|
|
@@ -12,7 +14,7 @@ module SqliteCrypto
|
|
|
12
14
|
private
|
|
13
15
|
|
|
14
16
|
def valid?(value)
|
|
15
|
-
|
|
17
|
+
UUID_PATTERN.match?(value)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
data/lib/sqlite_crypto.rb
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "sqlite_crypto/version"
|
|
4
|
+
require "sqlite_crypto/configuration"
|
|
4
5
|
require "sqlite_crypto/railtie" if defined?(Rails)
|
|
5
6
|
require "sqlite_crypto/schema_dumper" if defined?(ActiveRecord)
|
|
6
7
|
require "sqlite_crypto/schema_definitions"
|
|
8
|
+
require "sqlite_crypto/generators/uuid"
|
|
7
9
|
|
|
8
10
|
module SqliteCrypto
|
|
9
|
-
class
|
|
11
|
+
class << self
|
|
12
|
+
def configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
alias_method :config, :configuration
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
def configure
|
|
18
|
+
yield(configuration)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset_configuration!
|
|
22
|
+
@configuration = Configuration.new
|
|
23
|
+
end
|
|
13
24
|
end
|
|
14
25
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sqlite_crypto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- BartOz
|
|
@@ -51,8 +51,10 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '1.0'
|
|
54
|
-
description:
|
|
55
|
-
|
|
54
|
+
description: UUID and ULID primary key support for Rails with SQLite3. Provides automatic
|
|
55
|
+
type registration, validation, foreign key detection, and clean schema generation.
|
|
56
|
+
Supports UUIDv4 (random), UUIDv7 (time-ordered), and ULID (compact, time-sortable)
|
|
57
|
+
with zero external dependencies.
|
|
56
58
|
email:
|
|
57
59
|
- bartek.ozdoba@gmail.com
|
|
58
60
|
executables: []
|
|
@@ -63,6 +65,10 @@ files:
|
|
|
63
65
|
- LICENSE.txt
|
|
64
66
|
- README.md
|
|
65
67
|
- lib/sqlite_crypto.rb
|
|
68
|
+
- lib/sqlite_crypto/configuration.rb
|
|
69
|
+
- lib/sqlite_crypto/generators/install_generator.rb
|
|
70
|
+
- lib/sqlite_crypto/generators/templates/initializer.rb.tt
|
|
71
|
+
- lib/sqlite_crypto/generators/uuid.rb
|
|
66
72
|
- lib/sqlite_crypto/migration_helpers.rb
|
|
67
73
|
- lib/sqlite_crypto/model_extensions.rb
|
|
68
74
|
- lib/sqlite_crypto/railtie.rb
|
|
@@ -94,5 +100,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
94
100
|
requirements: []
|
|
95
101
|
rubygems_version: 4.0.3
|
|
96
102
|
specification_version: 4
|
|
97
|
-
summary:
|
|
103
|
+
summary: UUID (v4/v7) and ULID primary keys for Rails + SQLite3
|
|
98
104
|
test_files: []
|