opaque_id 1.1.0 → 1.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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +50 -0
- data/CODE_OF_CONDUCT.md +11 -11
- data/README.md +82 -0
- data/docs/.gitignore +5 -0
- data/docs/404.html +25 -0
- data/docs/Gemfile +31 -0
- data/docs/Gemfile.lock +335 -0
- data/docs/_config.yml +162 -0
- data/docs/_data/navigation.yml +132 -0
- data/docs/_includes/footer_custom.html +8 -0
- data/docs/_includes/head_custom.html +67 -0
- data/docs/algorithms.md +409 -0
- data/docs/alphabets.md +521 -0
- data/docs/api-reference.md +594 -0
- data/docs/assets/css/custom.scss +798 -0
- data/docs/assets/images/favicon.svg +17 -0
- data/docs/assets/images/og-image.svg +65 -0
- data/docs/configuration.md +548 -0
- data/docs/development.md +567 -0
- data/docs/getting-started.md +256 -0
- data/docs/index.md +132 -0
- data/docs/installation.md +377 -0
- data/docs/performance.md +488 -0
- data/docs/robots.txt +11 -0
- data/docs/security.md +598 -0
- data/docs/usage.md +414 -0
- data/docs/use-cases.md +569 -0
- data/lib/generators/opaque_id/install_generator.rb +38 -23
- data/lib/opaque_id/version.rb +1 -1
- data/tasks/0003-prd-documentation-site.md +191 -0
- data/tasks/tasks-0003-prd-documentation-site.md +84 -0
- metadata +27 -2
- data/sig/opaque_id.rbs +0 -4
data/docs/usage.md
ADDED
@@ -0,0 +1,414 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
title: Usage
|
4
|
+
nav_order: 4
|
5
|
+
description: "Complete usage guide with standalone and ActiveRecord examples"
|
6
|
+
permalink: /usage/
|
7
|
+
---
|
8
|
+
|
9
|
+
# Usage
|
10
|
+
|
11
|
+
This guide covers all usage patterns for OpaqueId, from basic standalone generation to advanced ActiveRecord integration.
|
12
|
+
|
13
|
+
## Standalone ID Generation
|
14
|
+
|
15
|
+
OpaqueId can be used independently of ActiveRecord for generating secure, random IDs.
|
16
|
+
|
17
|
+
### Basic Usage
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# Generate a default opaque ID (21 characters, alphanumeric)
|
21
|
+
id = OpaqueId.generate
|
22
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
23
|
+
|
24
|
+
# Generate multiple IDs
|
25
|
+
ids = 5.times.map { OpaqueId.generate }
|
26
|
+
# => ["V1StGXR8_Z5jdHi6B-myT", "K8jH2mN9_pL3qR7sT1v", ...]
|
27
|
+
```
|
28
|
+
|
29
|
+
### Custom Parameters
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# Custom length
|
33
|
+
id = OpaqueId.generate(size: 10)
|
34
|
+
# => "V1StGXR8_Z5"
|
35
|
+
|
36
|
+
# Custom alphabet
|
37
|
+
id = OpaqueId.generate(alphabet: OpaqueId::STANDARD_ALPHABET)
|
38
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
39
|
+
|
40
|
+
# Both custom length and alphabet
|
41
|
+
id = OpaqueId.generate(size: 15, alphabet: OpaqueId::STANDARD_ALPHABET)
|
42
|
+
# => "V1StGXR8_Z5jdHi"
|
43
|
+
```
|
44
|
+
|
45
|
+
### Built-in Alphabets
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# Alphanumeric alphabet (default) - A-Z, a-z, 0-9
|
49
|
+
id = OpaqueId.generate(alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
|
50
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
51
|
+
|
52
|
+
# Standard alphabet - A-Z, a-z, 0-9, -, _
|
53
|
+
id = OpaqueId.generate(alphabet: OpaqueId::STANDARD_ALPHABET)
|
54
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
55
|
+
```
|
56
|
+
|
57
|
+
### Custom Alphabets
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# Numeric only
|
61
|
+
numeric_alphabet = "0123456789"
|
62
|
+
id = OpaqueId.generate(size: 8, alphabet: numeric_alphabet)
|
63
|
+
# => "12345678"
|
64
|
+
|
65
|
+
# Hexadecimal
|
66
|
+
hex_alphabet = "0123456789abcdef"
|
67
|
+
id = OpaqueId.generate(size: 16, alphabet: hex_alphabet)
|
68
|
+
# => "a1b2c3d4e5f67890"
|
69
|
+
|
70
|
+
# URL-safe characters
|
71
|
+
url_safe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
72
|
+
id = OpaqueId.generate(size: 12, alphabet: url_safe)
|
73
|
+
# => "V1StGXR8_Z5j"
|
74
|
+
```
|
75
|
+
|
76
|
+
## ActiveRecord Integration
|
77
|
+
|
78
|
+
OpaqueId provides seamless integration with ActiveRecord models through the `OpaqueId::Model` concern.
|
79
|
+
|
80
|
+
### Basic Model Setup
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class User < ApplicationRecord
|
84
|
+
include OpaqueId::Model
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Automatic ID Generation
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# Create a new user - opaque_id is automatically generated
|
92
|
+
user = User.create!(name: "John Doe", email: "john@example.com")
|
93
|
+
puts user.opaque_id
|
94
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
95
|
+
|
96
|
+
# The ID is generated before the record is saved
|
97
|
+
user = User.new(name: "Jane Smith")
|
98
|
+
puts user.opaque_id
|
99
|
+
# => nil (not generated yet)
|
100
|
+
|
101
|
+
user.save!
|
102
|
+
puts user.opaque_id
|
103
|
+
# => "K8jH2mN9_pL3qR7sT1v" (generated on save)
|
104
|
+
```
|
105
|
+
|
106
|
+
### Finder Methods
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# Find by opaque_id (returns nil if not found)
|
110
|
+
user = User.find_by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
|
111
|
+
|
112
|
+
# Find by opaque_id (raises exception if not found)
|
113
|
+
user = User.find_by_opaque_id!("V1StGXR8_Z5jdHi6B-myT")
|
114
|
+
# => ActiveRecord::RecordNotFound if not found
|
115
|
+
|
116
|
+
# Use in scopes
|
117
|
+
class User < ApplicationRecord
|
118
|
+
include OpaqueId::Model
|
119
|
+
|
120
|
+
scope :by_opaque_id, ->(id) { where(opaque_id: id) }
|
121
|
+
end
|
122
|
+
|
123
|
+
users = User.by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
|
124
|
+
```
|
125
|
+
|
126
|
+
### Custom Column Names
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class User < ApplicationRecord
|
130
|
+
include OpaqueId::Model
|
131
|
+
|
132
|
+
# Use a different column name
|
133
|
+
self.opaque_id_column = :public_id
|
134
|
+
end
|
135
|
+
|
136
|
+
# Now the methods use the custom column name
|
137
|
+
user = User.create!(name: "John Doe")
|
138
|
+
puts user.public_id
|
139
|
+
# => "V1StGXR8_Z5jdHi6B-myT"
|
140
|
+
|
141
|
+
user = User.find_by_public_id("V1StGXR8_Z5jdHi6B-myT")
|
142
|
+
```
|
143
|
+
|
144
|
+
## Rails Generator
|
145
|
+
|
146
|
+
The Rails generator provides a convenient way to set up OpaqueId for your models.
|
147
|
+
|
148
|
+
### Basic Generator Usage
|
149
|
+
|
150
|
+
```bash
|
151
|
+
# Generate setup for a User model
|
152
|
+
rails generate opaque_id:install User
|
153
|
+
```
|
154
|
+
|
155
|
+
This creates:
|
156
|
+
|
157
|
+
- A migration to add the `opaque_id` column
|
158
|
+
- A unique index on the `opaque_id` column
|
159
|
+
- Includes the `OpaqueId::Model` concern in your model
|
160
|
+
|
161
|
+
### Custom Column Names
|
162
|
+
|
163
|
+
```bash
|
164
|
+
# Use a custom column name
|
165
|
+
rails generate opaque_id:install User --column-name=public_id
|
166
|
+
```
|
167
|
+
|
168
|
+
This will:
|
169
|
+
|
170
|
+
- Create a `public_id` column instead of `opaque_id`
|
171
|
+
- Add `self.opaque_id_column = :public_id` to your model
|
172
|
+
|
173
|
+
### Multiple Models
|
174
|
+
|
175
|
+
```bash
|
176
|
+
# Set up multiple models
|
177
|
+
rails generate opaque_id:install User
|
178
|
+
rails generate opaque_id:install Post --column-name=slug
|
179
|
+
rails generate opaque_id:install Comment
|
180
|
+
```
|
181
|
+
|
182
|
+
## Real-World Examples
|
183
|
+
|
184
|
+
### E-commerce Application
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class Order < ApplicationRecord
|
188
|
+
include OpaqueId::Model
|
189
|
+
|
190
|
+
# Use shorter IDs for orders
|
191
|
+
self.opaque_id_length = 12
|
192
|
+
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
|
193
|
+
end
|
194
|
+
|
195
|
+
# Create an order
|
196
|
+
order = Order.create!(user_id: 1, total: 99.99)
|
197
|
+
puts order.opaque_id
|
198
|
+
# => "V1StGXR8_Z5j"
|
199
|
+
|
200
|
+
# Use in URLs
|
201
|
+
order_url(order.opaque_id)
|
202
|
+
# => "/orders/V1StGXR8_Z5j"
|
203
|
+
```
|
204
|
+
|
205
|
+
### API Development
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class ApiKey < ApplicationRecord
|
209
|
+
include OpaqueId::Model
|
210
|
+
|
211
|
+
# Use longer IDs for API keys
|
212
|
+
self.opaque_id_length = 32
|
213
|
+
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
|
214
|
+
end
|
215
|
+
|
216
|
+
# Generate API key
|
217
|
+
api_key = ApiKey.create!(user_id: 1, name: "Mobile App")
|
218
|
+
puts api_key.opaque_id
|
219
|
+
# => "V1StGXR8_Z5jdHi6B-myT1234567890"
|
220
|
+
|
221
|
+
# Use in API requests
|
222
|
+
# Authorization: Bearer V1StGXR8_Z5jdHi6B-myT1234567890
|
223
|
+
```
|
224
|
+
|
225
|
+
### Content Management
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
class Article < ApplicationRecord
|
229
|
+
include OpaqueId::Model
|
230
|
+
|
231
|
+
# Use URL-safe characters for slugs
|
232
|
+
self.opaque_id_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
233
|
+
self.opaque_id_length = 8
|
234
|
+
end
|
235
|
+
|
236
|
+
# Create an article
|
237
|
+
article = Article.create!(title: "My Article", content: "Content here")
|
238
|
+
puts article.opaque_id
|
239
|
+
# => "V1StGXR8"
|
240
|
+
|
241
|
+
# Use in URLs
|
242
|
+
article_url(article.opaque_id)
|
243
|
+
# => "/articles/V1StGXR8"
|
244
|
+
```
|
245
|
+
|
246
|
+
### User Management
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
class User < ApplicationRecord
|
250
|
+
include OpaqueId::Model
|
251
|
+
|
252
|
+
# Require letter start for user IDs
|
253
|
+
self.opaque_id_require_letter_start = true
|
254
|
+
end
|
255
|
+
|
256
|
+
# Create a user
|
257
|
+
user = User.create!(name: "John Doe", email: "john@example.com")
|
258
|
+
puts user.opaque_id
|
259
|
+
# => "V1StGXR8_Z5jdHi6B-myT" (starts with letter)
|
260
|
+
|
261
|
+
# Use in user profiles
|
262
|
+
user_url(user.opaque_id)
|
263
|
+
# => "/users/V1StGXR8_Z5jdHi6B-myT"
|
264
|
+
```
|
265
|
+
|
266
|
+
## Advanced Usage Patterns
|
267
|
+
|
268
|
+
### Batch Operations
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# Generate multiple IDs at once
|
272
|
+
ids = 100.times.map { OpaqueId.generate }
|
273
|
+
# => ["V1StGXR8_Z5jdHi6B-myT", "K8jH2mN9_pL3qR7sT1v", ...]
|
274
|
+
|
275
|
+
# Use in bulk operations
|
276
|
+
users_data = ids.map.with_index do |id, index|
|
277
|
+
{ opaque_id: id, name: "User #{index + 1}" }
|
278
|
+
end
|
279
|
+
|
280
|
+
User.insert_all(users_data)
|
281
|
+
```
|
282
|
+
|
283
|
+
### Conditional ID Generation
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
class User < ApplicationRecord
|
287
|
+
include OpaqueId::Model
|
288
|
+
|
289
|
+
private
|
290
|
+
|
291
|
+
def set_opaque_id
|
292
|
+
# Only generate ID if not already set
|
293
|
+
return if opaque_id.present?
|
294
|
+
|
295
|
+
# Custom generation logic
|
296
|
+
self.opaque_id = generate_custom_id
|
297
|
+
end
|
298
|
+
|
299
|
+
def generate_custom_id
|
300
|
+
# Generate ID with custom logic
|
301
|
+
base_id = OpaqueId.generate(size: 15)
|
302
|
+
"usr_#{base_id}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
### Error Handling
|
308
|
+
|
309
|
+
```ruby
|
310
|
+
class User < ApplicationRecord
|
311
|
+
include OpaqueId::Model
|
312
|
+
|
313
|
+
# Handle generation failures
|
314
|
+
rescue_from OpaqueId::GenerationError do |exception|
|
315
|
+
Rails.logger.error "Failed to generate opaque ID: #{exception.message}"
|
316
|
+
# Fallback to a different generation method
|
317
|
+
self.opaque_id = generate_fallback_id
|
318
|
+
end
|
319
|
+
|
320
|
+
private
|
321
|
+
|
322
|
+
def generate_fallback_id
|
323
|
+
# Fallback generation method
|
324
|
+
"fallback_#{SecureRandom.hex(10)}"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
## Performance Considerations
|
330
|
+
|
331
|
+
### Batch Generation
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
# Efficient batch generation
|
335
|
+
def generate_batch_ids(count, size: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
|
336
|
+
count.times.map { OpaqueId.generate(size: size, alphabet: alphabet) }
|
337
|
+
end
|
338
|
+
|
339
|
+
# Generate 1000 IDs
|
340
|
+
ids = generate_batch_ids(1000)
|
341
|
+
```
|
342
|
+
|
343
|
+
### Caching Generated IDs
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
class User < ApplicationRecord
|
347
|
+
include OpaqueId::Model
|
348
|
+
|
349
|
+
# Cache the generated ID
|
350
|
+
def opaque_id
|
351
|
+
@opaque_id ||= super
|
352
|
+
end
|
353
|
+
end
|
354
|
+
```
|
355
|
+
|
356
|
+
## Best Practices
|
357
|
+
|
358
|
+
### 1. Choose Appropriate Length
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
# Short IDs for public URLs
|
362
|
+
self.opaque_id_length = 8
|
363
|
+
|
364
|
+
# Medium IDs for general use
|
365
|
+
self.opaque_id_length = 21
|
366
|
+
|
367
|
+
# Long IDs for sensitive data
|
368
|
+
self.opaque_id_length = 32
|
369
|
+
```
|
370
|
+
|
371
|
+
### 2. Select Suitable Alphabets
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
# URL-safe for public URLs
|
375
|
+
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
|
376
|
+
|
377
|
+
# Fastest generation
|
378
|
+
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
|
379
|
+
|
380
|
+
# Custom for specific needs
|
381
|
+
self.opaque_id_alphabet = "0123456789ABCDEF"
|
382
|
+
```
|
383
|
+
|
384
|
+
### 3. Handle Collisions Gracefully
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
class User < ApplicationRecord
|
388
|
+
include OpaqueId::Model
|
389
|
+
|
390
|
+
# Increase retry attempts for high-volume applications
|
391
|
+
self.opaque_id_max_retry = 10
|
392
|
+
end
|
393
|
+
```
|
394
|
+
|
395
|
+
### 4. Use Appropriate Finder Methods
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
# Use find_by_opaque_id for optional lookups
|
399
|
+
user = User.find_by_opaque_id(params[:id])
|
400
|
+
|
401
|
+
# Use find_by_opaque_id! for required lookups
|
402
|
+
user = User.find_by_opaque_id!(params[:id])
|
403
|
+
```
|
404
|
+
|
405
|
+
## Next Steps
|
406
|
+
|
407
|
+
Now that you understand the usage patterns:
|
408
|
+
|
409
|
+
1. **Explore [Configuration](configuration.md)** for advanced setup
|
410
|
+
2. **Check out [Use Cases](use-cases.md)** for more real-world scenarios
|
411
|
+
3. **Review [Performance](performance.md)** for optimization tips
|
412
|
+
4. **Read [API Reference](api-reference.md)** for complete documentation
|
413
|
+
5. **Learn about [Alphabets](alphabets.md)** for custom character sets
|
414
|
+
6. **Understand [Algorithms](algorithms.md)** for technical details
|