opaque_id 1.3.0 → 1.6.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.
data/docs/development.md CHANGED
@@ -10,6 +10,9 @@ permalink: /development/
10
10
 
11
11
  This guide covers development setup, guidelines, and contribution information for OpaqueId. Whether you're contributing to the project or developing applications that use OpaqueId, this guide will help you get started.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## Prerequisites
14
17
 
15
18
  Before you begin development, ensure you have the following installed:
@@ -10,6 +10,9 @@ permalink: /getting-started/
10
10
 
11
11
  This guide will help you get up and running with OpaqueId in your Rails application. We'll cover installation, basic setup, and common usage patterns.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## Prerequisites
14
17
 
15
18
  Before you begin, ensure you have:
data/docs/index.md CHANGED
@@ -12,7 +12,10 @@ permalink: /
12
12
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
13
13
  [![Gem Downloads](https://img.shields.io/badge/gem%20downloads-opaque_id-blue)](https://rubygems.org/gems/opaque_id)
14
14
 
15
- A Ruby gem for generating cryptographically secure, collision-free opaque IDs for ActiveRecord models. OpaqueId provides a drop-in replacement for `nanoid.rb` using Ruby's built-in `SecureRandom` methods with optimized algorithms for unbiased distribution.
15
+ A simple Ruby gem for generating secure, opaque IDs for ActiveRecord models. OpaqueId provides a drop-in replacement for `nanoid.rb` using Ruby's built-in `SecureRandom` methods, with slug-like IDs as the default for optimal URL safety and user experience.
16
+
17
+ - TOC
18
+ {:toc}
16
19
 
17
20
  ## Features
18
21
 
@@ -54,10 +57,10 @@ end
54
57
 
55
58
  # Create a user - opaque_id is automatically generated
56
59
  user = User.create!(name: "John Doe")
57
- puts user.opaque_id # => "V1StGXR8_Z5jdHi6B-myT"
60
+ puts user.opaque_id # => "izkpm55j334u8x9y2"
58
61
 
59
62
  # Find by opaque_id
60
- user = User.find_by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
63
+ user = User.find_by_opaque_id("izkpm55j334u8x9y2")
61
64
  ```
62
65
 
63
66
  ## Why OpaqueId?
data/docs/installation.md CHANGED
@@ -10,6 +10,9 @@ permalink: /installation/
10
10
 
11
11
  This guide covers all installation methods for OpaqueId, from basic setup to advanced configurations.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## Requirements
14
17
 
15
18
  Before installing OpaqueId, ensure your environment meets these requirements:
data/docs/performance.md CHANGED
@@ -8,17 +8,20 @@ permalink: /performance/
8
8
 
9
9
  # Performance
10
10
 
11
- OpaqueId is designed for high performance with optimized algorithms and efficient memory usage. This guide covers benchmarks, optimization strategies, and scalability considerations.
11
+ OpaqueId is designed for efficient ID generation with optimized algorithms and memory usage. This guide covers performance characteristics, optimization strategies, and scalability considerations.
12
+
13
+ - TOC
14
+ {:toc}
12
15
 
13
16
  ## Performance Characteristics
14
17
 
15
18
  ### Generation Speed
16
19
 
17
- OpaqueId is designed for high performance with optimized algorithms for different alphabet sizes and ID lengths.
20
+ OpaqueId is designed for efficient ID generation with optimized algorithms for different alphabet sizes and ID lengths.
18
21
 
19
22
  #### Algorithm Performance
20
23
 
21
- - **Fast Path (64-character alphabets)**: Uses bitwise operations for maximum speed with no rejection sampling overhead
24
+ - **Fast Path (64-character alphabets)**: Uses bitwise operations for efficient generation with no rejection sampling overhead
22
25
  - **Unbiased Path (Other alphabets)**: Uses rejection sampling for unbiased distribution with slight performance overhead
23
26
  - **Performance scales linearly** with ID length and batch size
24
27
 
data/docs/security.md CHANGED
@@ -10,6 +10,9 @@ permalink: /security/
10
10
 
11
11
  OpaqueId is designed with security as a primary concern, providing cryptographically secure ID generation with protection against various attack vectors. This guide covers security considerations, best practices, and threat model analysis.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## Cryptographic Security
14
17
 
15
18
  ### SecureRandom Foundation
data/docs/usage.md CHANGED
@@ -10,6 +10,9 @@ permalink: /usage/
10
10
 
11
11
  This guide covers all usage patterns for OpaqueId, from basic standalone generation to advanced ActiveRecord integration.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## Standalone ID Generation
14
17
 
15
18
  OpaqueId can be used independently of ActiveRecord for generating secure, random IDs.
@@ -17,9 +20,9 @@ OpaqueId can be used independently of ActiveRecord for generating secure, random
17
20
  ### Basic Usage
18
21
 
19
22
  ```ruby
20
- # Generate a default opaque ID (21 characters, alphanumeric)
23
+ # Generate a default opaque ID (18 characters, slug-like)
21
24
  id = OpaqueId.generate
22
- # => "V1StGXR8_Z5jdHi6B-myT"
25
+ # => "izkpm55j334u8x9y2"
23
26
 
24
27
  # Generate multiple IDs
25
28
  ids = 5.times.map { OpaqueId.generate }
@@ -31,15 +34,15 @@ ids = 5.times.map { OpaqueId.generate }
31
34
  ```ruby
32
35
  # Custom length
33
36
  id = OpaqueId.generate(size: 10)
34
- # => "V1StGXR8_Z5"
37
+ # => "izkpm55j334u"
35
38
 
36
39
  # Custom alphabet
37
40
  id = OpaqueId.generate(alphabet: OpaqueId::STANDARD_ALPHABET)
38
- # => "V1StGXR8_Z5jdHi6B-myT"
41
+ # => "izkpm55j334u8x9y2"
39
42
 
40
43
  # Both custom length and alphabet
41
44
  id = OpaqueId.generate(size: 15, alphabet: OpaqueId::STANDARD_ALPHABET)
42
- # => "V1StGXR8_Z5jdHi"
45
+ # => "izkpm55j334u8x9y"
43
46
  ```
44
47
 
45
48
  ### Built-in Alphabets
@@ -47,11 +50,11 @@ id = OpaqueId.generate(size: 15, alphabet: OpaqueId::STANDARD_ALPHABET)
47
50
  ```ruby
48
51
  # Alphanumeric alphabet (default) - A-Z, a-z, 0-9
49
52
  id = OpaqueId.generate(alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
50
- # => "V1StGXR8_Z5jdHi6B-myT"
53
+ # => "izkpm55j334u8x9y2"
51
54
 
52
55
  # Standard alphabet - A-Z, a-z, 0-9, -, _
53
56
  id = OpaqueId.generate(alphabet: OpaqueId::STANDARD_ALPHABET)
54
- # => "V1StGXR8_Z5jdHi6B-myT"
57
+ # => "izkpm55j334u8x9y2"
55
58
  ```
56
59
 
57
60
  ### Custom Alphabets
@@ -70,7 +73,7 @@ id = OpaqueId.generate(size: 16, alphabet: hex_alphabet)
70
73
  # URL-safe characters
71
74
  url_safe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
72
75
  id = OpaqueId.generate(size: 12, alphabet: url_safe)
73
- # => "V1StGXR8_Z5j"
76
+ # => "izkpm55j334u8x"
74
77
  ```
75
78
 
76
79
  ## ActiveRecord Integration
@@ -91,7 +94,7 @@ end
91
94
  # Create a new user - opaque_id is automatically generated
92
95
  user = User.create!(name: "John Doe", email: "john@example.com")
93
96
  puts user.opaque_id
94
- # => "V1StGXR8_Z5jdHi6B-myT"
97
+ # => "izkpm55j334u8x9y2"
95
98
 
96
99
  # The ID is generated before the record is saved
97
100
  user = User.new(name: "Jane Smith")
@@ -107,10 +110,10 @@ puts user.opaque_id
107
110
 
108
111
  ```ruby
109
112
  # Find by opaque_id (returns nil if not found)
110
- user = User.find_by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
113
+ user = User.find_by_opaque_id("izkpm55j334u8x9y2")
111
114
 
112
115
  # Find by opaque_id (raises exception if not found)
113
- user = User.find_by_opaque_id!("V1StGXR8_Z5jdHi6B-myT")
116
+ user = User.find_by_opaque_id!("izkpm55j334u8x9y2")
114
117
  # => ActiveRecord::RecordNotFound if not found
115
118
 
116
119
  # Use in scopes
@@ -120,7 +123,7 @@ class User < ApplicationRecord
120
123
  scope :by_opaque_id, ->(id) { where(opaque_id: id) }
121
124
  end
122
125
 
123
- users = User.by_opaque_id("V1StGXR8_Z5jdHi6B-myT")
126
+ users = User.by_opaque_id("izkpm55j334u8x9y2")
124
127
  ```
125
128
 
126
129
  ### Custom Column Names
@@ -136,7 +139,7 @@ end
136
139
  # Now the methods use the custom column name
137
140
  user = User.create!(name: "John Doe")
138
141
  puts user.public_id
139
- # => "V1StGXR8_Z5jdHi6B-myT"
142
+ # => "izkpm55j334u8x9y2"
140
143
 
141
144
  user = User.find_by_public_id("V1StGXR8_Z5jdHi6B-myT")
142
145
  ```
@@ -195,7 +198,7 @@ end
195
198
  # Create an order
196
199
  order = Order.create!(user_id: 1, total: 99.99)
197
200
  puts order.opaque_id
198
- # => "V1StGXR8_Z5j"
201
+ # => "izkpm55j334u8x"
199
202
 
200
203
  # Use in URLs
201
204
  order_url(order.opaque_id)
@@ -256,7 +259,7 @@ end
256
259
  # Create a user
257
260
  user = User.create!(name: "John Doe", email: "john@example.com")
258
261
  puts user.opaque_id
259
- # => "V1StGXR8_Z5jdHi6B-myT" (starts with letter)
262
+ # => "izkpm55j334u8x9y2" (starts with letter)
260
263
 
261
264
  # Use in user profiles
262
265
  user_url(user.opaque_id)
data/docs/use-cases.md CHANGED
@@ -10,6 +10,9 @@ permalink: /use-cases/
10
10
 
11
11
  OpaqueId is designed for a wide range of applications where secure, unpredictable identifiers are needed. This guide covers real-world use cases with practical examples and implementation patterns.
12
12
 
13
+ - TOC
14
+ {:toc}
15
+
13
16
  ## E-Commerce Applications
14
17
 
15
18
  ### Order Management
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpaqueId
4
- VERSION = '1.3.0'
4
+ VERSION = '1.6.0'
5
5
  end
data/lib/opaque_id.rb CHANGED
@@ -9,6 +9,9 @@ module OpaqueId
9
9
  class GenerationError < Error; end
10
10
  class ConfigurationError < Error; end
11
11
 
12
+ # Slug-like alphabet for URL-safe, double-click selectable IDs (36 characters)
13
+ SLUG_LIKE_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
14
+
12
15
  # Standard URL-safe alphabet (64 characters)
13
16
  STANDARD_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
14
17
 
@@ -17,7 +20,7 @@ module OpaqueId
17
20
 
18
21
  class << self
19
22
  # Generate a cryptographically secure random ID
20
- def generate(size: 21, alphabet: ALPHANUMERIC_ALPHABET)
23
+ def generate(size: 18, alphabet: SLUG_LIKE_ALPHABET)
21
24
  raise ConfigurationError, 'Size must be positive' unless size.positive?
22
25
  raise ConfigurationError, 'Alphabet cannot be empty' if alphabet.nil? || alphabet.empty?
23
26
 
@@ -3,7 +3,8 @@
3
3
  "packages": {
4
4
  ".": {
5
5
  "package-name": "opaque_id",
6
- "version-file": "lib/opaque_id/version.rb"
6
+ "version-file": "lib/opaque_id/version.rb",
7
+ "tag-template": "v${version}"
7
8
  }
8
9
  },
9
10
  "changelog-sections": [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opaque_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Nyaggah
@@ -156,14 +156,6 @@ files:
156
156
  - lib/opaque_id/model.rb
157
157
  - lib/opaque_id/version.rb
158
158
  - release-please-config.json
159
- - tasks/0001-prd-opaque-id-gem.md
160
- - tasks/0002-prd-publishing-release-automation.md
161
- - tasks/0003-prd-documentation-site.md
162
- - tasks/references/opaque_gem_requirements.md
163
- - tasks/references/original_identifiable_concern_and_nanoid.md
164
- - tasks/tasks-0001-prd-opaque-id-gem.md
165
- - tasks/tasks-0002-prd-publishing-release-automation.md
166
- - tasks/tasks-0003-prd-documentation-site.md
167
159
  homepage: https://github.com/nyaggah/opaque_id
168
160
  licenses:
169
161
  - MIT
@@ -1,202 +0,0 @@
1
- # Product Requirements Document: OpaqueId Ruby Gem
2
-
3
- ## Introduction/Overview
4
-
5
- The OpaqueId gem is a Ruby library that generates cryptographically secure, collision-free opaque identifiers for ActiveRecord models. This gem replaces the existing `nanoid.rb` dependency by implementing the same functionality using Ruby's built-in `SecureRandom` methods, eliminating external dependencies while maintaining the same security and performance characteristics.
6
-
7
- The primary problem this gem solves is preventing the exposure of incremental database IDs in public URLs and APIs, which can reveal business metrics, enable enumeration attacks, and expose internal system architecture. Instead, it provides opaque, non-sequential identifiers that are URL-friendly and cryptographically secure.
8
-
9
- ## Goals
10
-
11
- 1. **Replace nanoid.rb dependency** - Implement equivalent functionality using Ruby's built-in SecureRandom
12
- 2. **Maintain security standards** - Provide cryptographically secure ID generation with unbiased distribution
13
- 3. **Ensure performance** - Achieve 1M+ IDs/sec for 64-character alphabets, 180K+ IDs/sec for 36-character alphabets
14
- 4. **Simplify integration** - Provide seamless ActiveRecord integration with minimal configuration
15
- 5. **Enable wide adoption** - Create comprehensive documentation accessible to all Rails developers
16
- 6. **Ensure reliability** - Implement robust collision detection and retry logic
17
- 7. **Maintain compatibility** - Support Rails 8.0+ and Ruby 3.2+ environments
18
-
19
- ## User Stories
20
-
21
- ### Primary Users: Rails Developers
22
-
23
- **As a Rails developer**, I want to generate opaque IDs for my models so that I can expose public identifiers without revealing database structure.
24
-
25
- **As a Rails developer**, I want to easily integrate opaque ID generation into my existing models so that I don't have to manually implement ID generation logic.
26
-
27
- **As a Rails developer**, I want to configure ID generation parameters (length, alphabet, column name) so that I can customize the behavior for different use cases.
28
-
29
- **As a Rails developer**, I want to find records by their opaque ID so that I can build public-facing APIs and URLs.
30
-
31
- **As a Rails developer**, I want the gem to handle collision detection automatically so that I don't have to worry about duplicate IDs.
32
-
33
- **As a Rails developer**, I want comprehensive documentation and examples so that I can quickly understand and implement the gem in my project.
34
-
35
- ### Secondary Users: Security-Conscious Teams
36
-
37
- **As a security-conscious developer**, I want cryptographically secure ID generation so that my public identifiers cannot be predicted or enumerated.
38
-
39
- **As a security-conscious developer**, I want unbiased character distribution so that my IDs have maximum entropy and cannot be analyzed for patterns.
40
-
41
- ## Functional Requirements
42
-
43
- ### Core ID Generation
44
-
45
- 1. The system must generate cryptographically secure random IDs using Ruby's `SecureRandom`
46
- 2. The system must implement rejection sampling algorithm to ensure unbiased character distribution
47
- 3. The system must provide optimized fast path for 64-character alphabets using bitwise operations
48
- 4. The system must support custom alphabet configurations
49
- 5. The system must support custom ID length configurations
50
- 6. The system must provide two predefined alphabets: ALPHANUMERIC_ALPHABET (36 chars) and STANDARD_ALPHABET (64 chars)
51
-
52
- ### ActiveRecord Integration
53
-
54
- 7. The system must provide `OpaqueId::Model` concern for easy ActiveRecord integration
55
- 8. The system must automatically generate opaque IDs on model creation via `before_create` callback
56
- 9. The system must provide `find_by_opaque_id` and `find_by_opaque_id!` class methods
57
- 10. The system must support custom column names via `opaque_id_column` configuration
58
- 11. The system must implement collision detection with configurable retry attempts
59
- 12. The system must raise appropriate errors when collision resolution fails
60
-
61
- ### Rails Generator (Optional Convenience Tool)
62
-
63
- 13. The system must provide optional Rails generator `opaque_id:install` for creating migrations and updating models
64
- 14. The system must generate migration files that add opaque_id column with unique index
65
- 15. The system must automatically add `include OpaqueId::Model` to the corresponding model file
66
- 16. The system must support custom column names via generator options
67
- 17. The system must require explicit table name argument and show clear usage instructions when run without arguments
68
- 18. The system must work with any existing table (new or existing models)
69
- 19. The system must handle edge cases gracefully (missing model file, already included, different class names)
70
-
71
- ### Configuration Options
72
-
73
- 20. The system must support `opaque_id_column` configuration (default: `:opaque_id`)
74
- 21. The system must support `opaque_id_length` configuration (default: `18`)
75
- 22. The system must support `opaque_id_alphabet` configuration (default: `ALPHANUMERIC_ALPHABET`)
76
- 23. The system must support `opaque_id_require_letter_start` configuration (default: `true`)
77
- 24. The system must support `opaque_id_purge_chars` configuration (default: `nil`)
78
- 25. The system must support `opaque_id_max_retry` configuration (default: `1000`)
79
-
80
- ### Error Handling
81
-
82
- 26. The system must raise `OpaqueId::ConfigurationError` for invalid size or empty alphabet
83
- 27. The system must raise `OpaqueId::GenerationError` when collision resolution fails
84
- 28. The system must provide clear error messages for debugging
85
-
86
- ### Standalone Usage
87
-
88
- 29. The system must provide `OpaqueId.generate` method for standalone ID generation
89
- 30. The system must support all configuration options in standalone generation
90
- 31. The system must maintain the same security and performance characteristics in standalone mode
91
-
92
- ## Non-Goals (Out of Scope)
93
-
94
- 1. **Other ORM Support** - Will not support Mongoid, Sequel, or other ORMs in initial release
95
- 2. **Non-Rails Usage** - Will not provide standalone Ruby usage without ActiveRecord dependency
96
- 3. **Custom Algorithms** - Will not implement alternative ID generation algorithms beyond rejection sampling
97
- 4. **Database Migrations** - Will not provide automatic database migration for existing records
98
- 5. **ID Validation** - Will not provide built-in ID format validation (users can implement their own)
99
- 6. **Bulk Generation** - Will not provide optimized bulk ID generation methods
100
- 7. **ID Prefixes/Suffixes** - Will not support adding prefixes or suffixes to generated IDs
101
- 8. **Custom Random Sources** - Will not support custom random number generators beyond SecureRandom
102
- 9. **Interactive Generator Mode** - Will not provide interactive prompts for generator arguments
103
- 10. **Backward Compatibility** - Will not maintain compatibility with existing `public_id` implementations
104
-
105
- ## Design Considerations
106
-
107
- ### API Design
108
-
109
- - Follow Rails conventions for ActiveRecord concerns and generators
110
- - Use descriptive method names that clearly indicate functionality
111
- - Provide both safe (`find_by_opaque_id`) and unsafe (`find_by_opaque_id!`) lookup methods
112
- - Use class-level configuration options for easy customization
113
- - Follow Devise-style generator pattern for seamless integration
114
-
115
- ### Performance Optimization
116
-
117
- - Implement fast path for 64-character alphabets using bitwise operations (`byte & 63`)
118
- - Use rejection sampling with optimal mask calculation for unbiased distribution
119
- - Pre-allocate string capacity to avoid memory reallocation during generation
120
- - Batch random byte generation to minimize SecureRandom calls
121
-
122
- ### Security Considerations
123
-
124
- - Use only cryptographically secure random number generation
125
- - Implement proper rejection sampling to avoid modulo bias
126
- - Provide sufficient entropy through configurable alphabet sizes
127
- - Ensure IDs cannot be predicted or enumerated
128
-
129
- ## Technical Considerations
130
-
131
- ### Dependencies
132
-
133
- - **ActiveRecord**: >= 6.0 (targeting Rails 8.0+ compatibility)
134
- - **ActiveSupport**: >= 6.0 (for concern functionality)
135
- - **Ruby**: >= 3.2 (for modern Ruby features and performance)
136
-
137
- ### Testing Framework
138
-
139
- - Use **Minitest** instead of RSpec for consistency with Rails conventions
140
- - Implement comprehensive unit tests for all public methods
141
- - Include statistical tests for character distribution uniformity
142
- - Add performance benchmarks to ensure performance requirements are met
143
- - Test edge cases including collision scenarios and error conditions
144
-
145
- ### File Structure
146
-
147
- ```
148
- lib/
149
- ├── opaque_id.rb # Main module with generator
150
- ├── opaque_id/
151
- │ ├── version.rb # Version constant
152
- │ └── model.rb # ActiveRecord concern
153
- └── generators/
154
- └── opaque_id/
155
- ├── install_generator.rb # Migration generator
156
- └── templates/
157
- └── migration.rb.tt # Migration template
158
- ```
159
-
160
- ### Error Classes
161
-
162
- - `OpaqueId::Error` - Base error class
163
- - `OpaqueId::GenerationError` - ID generation failures
164
- - `OpaqueId::ConfigurationError` - Invalid configuration
165
-
166
- ## Success Metrics
167
-
168
- ### Performance Metrics
169
-
170
- - **Standard alphabet (64 chars)**: Achieve ~1.2M IDs/sec generation rate
171
- - **Alphanumeric alphabet (36 chars)**: Achieve ~180K IDs/sec generation rate
172
- - **Custom alphabet (20 chars)**: Achieve ~150K IDs/sec generation rate
173
-
174
- ### Quality Metrics
175
-
176
- - **Test Coverage**: Achieve 95%+ code coverage
177
- - **Documentation**: Provide comprehensive README with examples
178
- - **Error Handling**: All error conditions properly tested and documented
179
-
180
- ### Adoption Metrics
181
-
182
- - **Gem Downloads**: Target 1000+ downloads in first month
183
- - **GitHub Stars**: Target 50+ stars within 6 months
184
- - **Community Feedback**: Positive reception from Rails community
185
-
186
- ## Open Questions
187
-
188
- 1. **Version Strategy**: Should we follow semantic versioning strictly, or use a different versioning strategy for a utility gem?
189
-
190
- 2. **Backward Compatibility**: How should we handle potential breaking changes in future versions, especially regarding default configurations?
191
-
192
- 3. **Performance Testing**: Should we include automated performance regression testing in CI/CD, or rely on manual benchmarking?
193
-
194
- 4. **Documentation Hosting**: Should we create a dedicated documentation site, or rely on GitHub README and inline documentation?
195
-
196
- 5. **Community Contributions**: What level of community contribution should we expect, and how should we structure the project to encourage contributions?
197
-
198
- 6. **Integration Testing**: Should we test against multiple Rails versions in CI, or focus on the target Rails 8.0+ range?
199
-
200
- 7. **Security Auditing**: Should we implement any security auditing tools or processes for the random number generation?
201
-
202
- 8. **Migration Path**: How should we help users migrate from nanoid.rb to opaque_id, if at all?