familia 2.0.0.pre8 → 2.0.0.pre12
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/.github/workflows/ci.yml +13 -0
- data/.github/workflows/docs.yml +1 -1
- data/.gitignore +9 -9
- data/.rubocop.yml +19 -0
- data/.yardopts +22 -1
- data/CHANGELOG.md +247 -0
- data/CLAUDE.md +12 -59
- data/Gemfile.lock +1 -1
- data/README.md +62 -2
- data/changelog.d/README.md +77 -0
- data/docs/archive/.gitignore +2 -0
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +210 -0
- data/docs/archive/FAMILIA_TECHNICAL.md +823 -0
- data/docs/archive/FAMILIA_UPDATE.md +226 -0
- data/docs/archive/README.md +63 -0
- data/docs/guides/.gitignore +2 -0
- data/docs/{wiki → guides}/Home.md +1 -1
- data/docs/{wiki → guides}/Implementation-Guide.md +1 -1
- data/docs/{wiki → guides}/Relationships-Guide.md +103 -50
- data/docs/guides/relationships-methods.md +266 -0
- data/docs/migrating/.gitignore +2 -0
- data/docs/migrating/v2.0.0-pre.md +84 -0
- data/docs/migrating/v2.0.0-pre11.md +255 -0
- data/docs/migrating/v2.0.0-pre12.md +306 -0
- data/docs/migrating/v2.0.0-pre5.md +110 -0
- data/docs/migrating/v2.0.0-pre6.md +154 -0
- data/docs/migrating/v2.0.0-pre7.md +222 -0
- data/docs/overview.md +6 -7
- data/{examples/redis_command_validation_example.rb → docs/reference/auditing_database_commands.rb} +29 -32
- data/examples/{bit_encoding_integration.rb → permissions.rb} +30 -27
- data/examples/relationships.rb +205 -0
- data/examples/safe_dump.rb +281 -0
- data/familia.gemspec +4 -4
- data/lib/familia/base.rb +52 -0
- data/lib/familia/connection.rb +4 -21
- data/lib/familia/{encryption_request_cache.rb → encryption/request_cache.rb} +1 -1
- data/lib/familia/errors.rb +2 -0
- data/lib/familia/features/autoloader.rb +57 -0
- data/lib/familia/features/external_identifier.rb +310 -0
- data/lib/familia/features/object_identifier.rb +307 -0
- data/lib/familia/features/relationships/indexing.rb +160 -175
- data/lib/familia/features/relationships/membership.rb +16 -21
- data/lib/familia/features/relationships/tracking.rb +61 -21
- data/lib/familia/features/relationships.rb +15 -8
- data/lib/familia/features/safe_dump.rb +66 -72
- data/lib/familia/features.rb +93 -5
- data/lib/familia/horreum/subclass/definition.rb +49 -3
- data/lib/familia/horreum.rb +15 -24
- data/lib/familia/secure_identifier.rb +51 -75
- data/lib/familia/verifiable_identifier.rb +162 -0
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +1 -0
- data/setup.cfg +5 -0
- data/try/core/secure_identifier_try.rb +47 -18
- data/try/core/verifiable_identifier_try.rb +171 -0
- data/try/features/{external_identifiers/external_identifiers_try.rb → external_identifier/external_identifier_try.rb} +25 -28
- data/try/features/feature_improvements_try.rb +126 -0
- data/try/features/{object_identifiers/object_identifiers_integration_try.rb → object_identifier/object_identifier_integration_try.rb} +28 -30
- data/try/features/{object_identifiers/object_identifiers_try.rb → object_identifier/object_identifier_try.rb} +13 -13
- data/try/features/real_feature_integration_try.rb +7 -6
- data/try/features/relationships/relationships_api_changes_try.rb +339 -0
- data/try/features/relationships/relationships_try.rb +6 -5
- data/try/features/safe_dump/safe_dump_try.rb +8 -9
- data/try/helpers/test_helpers.rb +17 -17
- metadata +62 -41
- data/examples/relationships_basic.rb +0 -273
- data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +0 -120
- data/lib/familia/features/external_identifiers.rb +0 -111
- data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +0 -91
- data/lib/familia/features/object_identifiers.rb +0 -194
- /data/docs/{wiki → guides}/API-Reference.md +0 -0
- /data/docs/{wiki → guides}/Connection-Pooling-Guide.md +0 -0
- /data/docs/{wiki → guides}/Encrypted-Fields-Overview.md +0 -0
- /data/docs/{wiki → guides}/Expiration-Feature-Guide.md +0 -0
- /data/docs/{wiki → guides}/Feature-System-Guide.md +0 -0
- /data/docs/{wiki → guides}/Features-System-Developer-Guide.md +0 -0
- /data/docs/{wiki → guides}/Field-System-Guide.md +0 -0
- /data/docs/{wiki → guides}/Quantization-Feature-Guide.md +0 -0
- /data/docs/{wiki → guides}/Security-Model.md +0 -0
- /data/docs/{wiki → guides}/Transient-Fields-Guide.md +0 -0
@@ -0,0 +1,306 @@
|
|
1
|
+
# Migrating Guide: v2.0.0-pre12
|
2
|
+
|
3
|
+
This version introduces significant security improvements to Familia's identifier system, including verifiable identifiers with HMAC signatures, scoped identifier namespaces, and hardened external identifier derivation to prevent potential security vulnerabilities.
|
4
|
+
|
5
|
+
## VerifiableIdentifier Feature
|
6
|
+
|
7
|
+
### Overview
|
8
|
+
|
9
|
+
The new `Familia::VerifiableIdentifier` module allows applications to create and verify identifiers with embedded HMAC signatures. This enables stateless confirmation that an identifier was generated by your application, preventing forged IDs from malicious sources.
|
10
|
+
|
11
|
+
### Basic Usage
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class Customer < Familia::Horreum
|
15
|
+
feature :verifiable_identifier
|
16
|
+
|
17
|
+
# Required: Set the HMAC secret (do this once in your app initialization)
|
18
|
+
# Generate with: SecureRandom.hex(64)
|
19
|
+
ENV['VERIFIABLE_ID_HMAC_SECRET'] = 'your_64_character_hex_secret'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate a verifiable identifier
|
23
|
+
customer = Customer.new
|
24
|
+
verifiable_id = customer.generate_verifiable_id
|
25
|
+
# => "cust_1234567890abcdef_a1b2c3d4e5f6789..."
|
26
|
+
|
27
|
+
# Verify the identifier later (stateless verification)
|
28
|
+
if Customer.verified_identifier?(verifiable_id)
|
29
|
+
# Identifier is valid and was generated by this application
|
30
|
+
original_id = Customer.extract_identifier(verifiable_id)
|
31
|
+
customer = Customer.new(original_id)
|
32
|
+
else
|
33
|
+
# Identifier is forged or corrupted
|
34
|
+
raise SecurityError, "Invalid identifier"
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
### Scoped VerifiableIdentifier
|
39
|
+
|
40
|
+
The new `scope` parameter enables cryptographically isolated identifier namespaces for multi-tenant, multi-domain, or multi-environment applications.
|
41
|
+
|
42
|
+
#### Before (Global Scope)
|
43
|
+
```ruby
|
44
|
+
# All identifiers share the same cryptographic space
|
45
|
+
admin_id = admin.generate_verifiable_id
|
46
|
+
user_id = user.generate_verifiable_id
|
47
|
+
|
48
|
+
# Risk: Cross-contamination between different contexts
|
49
|
+
```
|
50
|
+
|
51
|
+
#### After (Scoped Namespaces)
|
52
|
+
```ruby
|
53
|
+
# Production environment
|
54
|
+
prod_customer_id = customer.generate_verifiable_id(scope: 'production')
|
55
|
+
prod_admin_id = admin.generate_verifiable_id(scope: 'production:admin')
|
56
|
+
|
57
|
+
# Development environment
|
58
|
+
dev_customer_id = customer.generate_verifiable_id(scope: 'development')
|
59
|
+
|
60
|
+
# Multi-tenant application
|
61
|
+
tenant_a_id = user.generate_verifiable_id(scope: "tenant:#{tenant_a.id}")
|
62
|
+
tenant_b_id = user.generate_verifiable_id(scope: "tenant:#{tenant_b.id}")
|
63
|
+
|
64
|
+
# Verification requires matching scope
|
65
|
+
Customer.verified_identifier?(prod_customer_id, scope: 'production') # => true
|
66
|
+
Customer.verified_identifier?(prod_customer_id, scope: 'development') # => false
|
67
|
+
```
|
68
|
+
|
69
|
+
**Scope Benefits:**
|
70
|
+
- **Multi-tenant isolation**: Tenant A cannot forge identifiers for Tenant B
|
71
|
+
- **Environment separation**: Production IDs cannot be used in development
|
72
|
+
- **Role-based security**: Admin scopes separate from user scopes
|
73
|
+
- **Full backward compatibility**: Existing code without scopes continues to work
|
74
|
+
|
75
|
+
### Key Management
|
76
|
+
|
77
|
+
#### Secure Secret Generation
|
78
|
+
```ruby
|
79
|
+
# Generate a cryptographically secure HMAC secret
|
80
|
+
require 'securerandom'
|
81
|
+
secret = SecureRandom.hex(64) # 512-bit secret
|
82
|
+
puts "VERIFIABLE_ID_HMAC_SECRET=#{secret}"
|
83
|
+
```
|
84
|
+
|
85
|
+
#### Environment Configuration
|
86
|
+
```ruby
|
87
|
+
# config/application.rb or equivalent
|
88
|
+
# Set this BEFORE any VerifiableIdentifier usage
|
89
|
+
ENV['VERIFIABLE_ID_HMAC_SECRET'] = Rails.application.credentials.verifiable_id_secret
|
90
|
+
|
91
|
+
# Or configure programmatically
|
92
|
+
Familia::VerifiableIdentifier.hmac_secret = your_secret_string
|
93
|
+
```
|
94
|
+
|
95
|
+
## ObjectIdentifier Feature Improvements
|
96
|
+
|
97
|
+
### Method Renaming
|
98
|
+
|
99
|
+
Method names have been updated for clarity and consistency:
|
100
|
+
|
101
|
+
#### Before
|
102
|
+
```ruby
|
103
|
+
customer = Customer.new
|
104
|
+
objid = customer.generate_objid # Unclear what this generates
|
105
|
+
extid = Customer.generate_extid(objid) # Less secure class method
|
106
|
+
```
|
107
|
+
|
108
|
+
#### After
|
109
|
+
```ruby
|
110
|
+
customer = Customer.new
|
111
|
+
objid = customer.generate_object_identifier # Clear: generates object ID
|
112
|
+
extid = customer.derive_external_identifier # Clear: derives from objid, instance method
|
113
|
+
```
|
114
|
+
|
115
|
+
**Migration:**
|
116
|
+
- Replace `generate_objid` → `generate_object_identifier`
|
117
|
+
- Replace `generate_external_identifier` → `derive_external_identifier`
|
118
|
+
- Remove usage of `generate_extid` (deprecated for security reasons)
|
119
|
+
|
120
|
+
### Provenance Tracking
|
121
|
+
|
122
|
+
ObjectIdentifier now tracks which generator was used for each identifier:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class Customer < Familia::Horreum
|
126
|
+
feature :object_identifier
|
127
|
+
|
128
|
+
# Configure generator type
|
129
|
+
object_identifier_generator :uuid_v7 # or :uuid_v4, :hex, custom proc
|
130
|
+
end
|
131
|
+
|
132
|
+
customer = Customer.new
|
133
|
+
objid = customer.generate_object_identifier
|
134
|
+
|
135
|
+
# Provenance information available
|
136
|
+
puts customer.object_identifier_generator_type # => :uuid_v7
|
137
|
+
puts customer.objid_format # => :uuid (normalized format)
|
138
|
+
```
|
139
|
+
|
140
|
+
**Benefits:**
|
141
|
+
- **Security auditing**: Know which generator created each identifier
|
142
|
+
- **Format normalization**: Eliminates ambiguity between UUID and hex formats
|
143
|
+
- **Migration support**: Track mixed generator usage during transitions
|
144
|
+
|
145
|
+
## ExternalIdentifier Security Hardening
|
146
|
+
|
147
|
+
### Provenance Validation
|
148
|
+
|
149
|
+
ExternalIdentifier now validates that objid values come from the ObjectIdentifier feature before deriving external identifiers.
|
150
|
+
|
151
|
+
#### Before (Potential Security Risk)
|
152
|
+
```ruby
|
153
|
+
# Could derive external IDs from any string, including malicious input
|
154
|
+
extid = customer.derive_external_identifier("malicious_input")
|
155
|
+
```
|
156
|
+
|
157
|
+
#### After (Hardened)
|
158
|
+
```ruby
|
159
|
+
customer = Customer.new
|
160
|
+
customer.generate_object_identifier # Must generate objid first
|
161
|
+
|
162
|
+
# Only works with validated objid from ObjectIdentifier feature
|
163
|
+
extid = customer.derive_external_identifier # Secure: uses validated objid
|
164
|
+
```
|
165
|
+
|
166
|
+
### Improved Security Model
|
167
|
+
|
168
|
+
External identifiers are now derived using the internal objid as a seed for a new random value, rather than directly deriving from objid.
|
169
|
+
|
170
|
+
#### Before
|
171
|
+
```ruby
|
172
|
+
# Direct derivation could leak information about objid
|
173
|
+
extid = hash(objid) # Information leakage risk
|
174
|
+
```
|
175
|
+
|
176
|
+
#### After
|
177
|
+
```ruby
|
178
|
+
# objid used as seed for new random value
|
179
|
+
extid = secure_hash(objid + additional_entropy) # No information leakage
|
180
|
+
```
|
181
|
+
|
182
|
+
### Error Handling Improvements
|
183
|
+
|
184
|
+
External identifier now raises clear errors for invalid usage:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class Customer < Familia::Horreum
|
188
|
+
feature :external_identifier # Missing: object_identifier dependency
|
189
|
+
end
|
190
|
+
|
191
|
+
customer = Customer.new
|
192
|
+
# Raises ExternalIdentifierError instead of returning nil
|
193
|
+
customer.derive_external_identifier
|
194
|
+
# => Familia::ExternalIdentifierError: Model does not have an objid field
|
195
|
+
```
|
196
|
+
|
197
|
+
## Migration Steps
|
198
|
+
|
199
|
+
### 1. Update Method Names
|
200
|
+
|
201
|
+
Replace deprecated method names in your codebase:
|
202
|
+
|
203
|
+
```bash
|
204
|
+
# Search and replace patterns:
|
205
|
+
grep -r "generate_objid" --include="*.rb" .
|
206
|
+
# Replace with: generate_object_identifier
|
207
|
+
|
208
|
+
grep -r "generate_external_identifier" --include="*.rb" .
|
209
|
+
# Replace with: derive_external_identifier
|
210
|
+
|
211
|
+
grep -r "generate_extid" --include="*.rb" .
|
212
|
+
# Remove usage - use derive_external_identifier instead
|
213
|
+
```
|
214
|
+
|
215
|
+
### 2. Add HMAC Secret for VerifiableIdentifier
|
216
|
+
|
217
|
+
If you plan to use VerifiableIdentifier:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
# Generate secret
|
221
|
+
require 'securerandom'
|
222
|
+
secret = SecureRandom.hex(64)
|
223
|
+
|
224
|
+
# Add to your environment configuration
|
225
|
+
# .env, Rails credentials, or similar
|
226
|
+
VERIFIABLE_ID_HMAC_SECRET=your_generated_secret
|
227
|
+
|
228
|
+
# Verify configuration
|
229
|
+
puts ENV['VERIFIABLE_ID_HMAC_SECRET']&.length # Should be 128 characters
|
230
|
+
```
|
231
|
+
|
232
|
+
### 3. Update ExternalIdentifier Usage
|
233
|
+
|
234
|
+
Ensure proper dependency chain:
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
class YourModel < Familia::Horreum
|
238
|
+
# Required: ObjectIdentifier must come before ExternalIdentifier
|
239
|
+
feature :object_identifier
|
240
|
+
feature :external_identifier
|
241
|
+
|
242
|
+
# Configure generator if needed
|
243
|
+
object_identifier_generator :uuid_v7
|
244
|
+
end
|
245
|
+
|
246
|
+
# Usage pattern
|
247
|
+
model = YourModel.new
|
248
|
+
model.generate_object_identifier # Generate objid first
|
249
|
+
extid = model.derive_external_identifier # Then derive external ID
|
250
|
+
```
|
251
|
+
|
252
|
+
### 4. Review Security-Sensitive Code
|
253
|
+
|
254
|
+
Audit any code that processes identifiers from external sources:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
# Before: Potentially unsafe
|
258
|
+
def process_identifier(external_id)
|
259
|
+
# Could process forged identifiers
|
260
|
+
model = Model.find_by_external_id(external_id)
|
261
|
+
end
|
262
|
+
|
263
|
+
# After: With verification
|
264
|
+
def process_identifier(verifiable_id)
|
265
|
+
# Verify identifier authenticity first
|
266
|
+
unless Model.verified_identifier?(verifiable_id)
|
267
|
+
raise SecurityError, "Invalid identifier"
|
268
|
+
end
|
269
|
+
|
270
|
+
original_id = Model.extract_identifier(verifiable_id)
|
271
|
+
model = Model.new(original_id)
|
272
|
+
end
|
273
|
+
```
|
274
|
+
|
275
|
+
## Breaking Changes
|
276
|
+
|
277
|
+
1. **`generate_extid` removed** - Use instance-level `derive_external_identifier` instead
|
278
|
+
2. **ExternalIdentifier validation** - Now raises `ExternalIdentifierError` instead of returning `nil` for models without objid
|
279
|
+
3. **Method names changed** - `generate_objid` → `generate_object_identifier`, `generate_external_identifier` → `derive_external_identifier`
|
280
|
+
|
281
|
+
## New Security Capabilities
|
282
|
+
|
283
|
+
1. **Cryptographic identifier verification** - Prevent forged IDs with HMAC signatures
|
284
|
+
2. **Scoped namespaces** - Isolate identifiers by tenant, environment, or role
|
285
|
+
3. **Provenance tracking** - Know which generator created each identifier
|
286
|
+
4. **Information leakage prevention** - External IDs no longer directly expose internal IDs
|
287
|
+
5. **Input validation** - Clear error messages for invalid operations
|
288
|
+
|
289
|
+
## Testing Your Migration
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
# Test ObjectIdentifier changes
|
293
|
+
model = YourModel.new
|
294
|
+
objid = model.generate_object_identifier
|
295
|
+
extid = model.derive_external_identifier
|
296
|
+
puts "Generator: #{model.object_identifier_generator_type}"
|
297
|
+
|
298
|
+
# Test VerifiableIdentifier (if using)
|
299
|
+
vid = model.generate_verifiable_id
|
300
|
+
puts "Verifiable: #{YourModel.verified_identifier?(vid)}"
|
301
|
+
|
302
|
+
# Test scoped identifiers (if using)
|
303
|
+
scoped_vid = model.generate_verifiable_id(scope: 'production')
|
304
|
+
puts "Scoped valid: #{YourModel.verified_identifier?(scoped_vid, scope: 'production')}"
|
305
|
+
puts "Wrong scope: #{YourModel.verified_identifier?(scoped_vid, scope: 'development')}"
|
306
|
+
```
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Migrating Guide: Security Features (v2.0.0-pre5)
|
2
|
+
|
3
|
+
This guide covers adopting the security enhancements introduced in v2.0.0-pre5.
|
4
|
+
|
5
|
+
## Security Feature Adoption
|
6
|
+
|
7
|
+
### 1. Configure Encryption Keys
|
8
|
+
|
9
|
+
Before using encrypted fields, configure encryption keys:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Familia.configure do |config|
|
13
|
+
config.encryption_keys = {
|
14
|
+
v1: 'your-32-byte-base64-encoded-key==',
|
15
|
+
v2: 'newer-32-byte-base64-encoded-key=='
|
16
|
+
}
|
17
|
+
config.current_key_version = :v2
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
**Key Management:**
|
22
|
+
- Use secure key storage (environment variables, key management services)
|
23
|
+
- Rotate keys regularly by adding new versions
|
24
|
+
- Never remove old key versions while data exists
|
25
|
+
|
26
|
+
### 2. Identify Sensitive Fields
|
27
|
+
|
28
|
+
Mark fields that contain sensitive data:
|
29
|
+
|
30
|
+
**For Encryption:**
|
31
|
+
```ruby
|
32
|
+
class Vault < Familia::Horreum
|
33
|
+
feature :encrypted_fields
|
34
|
+
|
35
|
+
field :name # Plaintext
|
36
|
+
encrypted_field :secret_key # Encrypted at rest
|
37
|
+
encrypted_field :api_token # Transparent access
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
**For Transient Fields:**
|
42
|
+
```ruby
|
43
|
+
class User < Familia::Horreum
|
44
|
+
feature :transient_fields
|
45
|
+
|
46
|
+
field :email # Persisted
|
47
|
+
transient_field :password # Never persisted
|
48
|
+
transient_field :session_token # Runtime only
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
### 3. Update Serialization Code
|
53
|
+
|
54
|
+
Handle `RedactedString` in serialization:
|
55
|
+
|
56
|
+
**Before:**
|
57
|
+
```ruby
|
58
|
+
def to_json
|
59
|
+
{ name: name, password: password }.to_json
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
**After:**
|
64
|
+
```ruby
|
65
|
+
def to_json
|
66
|
+
# RedactedString automatically excluded from serialization
|
67
|
+
{ name: name }.to_json # password field omitted if transient
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
**Manual RedactedString Handling:**
|
72
|
+
```ruby
|
73
|
+
# Access original value when needed
|
74
|
+
password.reveal # Returns actual string value
|
75
|
+
password.redacted? # Returns true if redacted
|
76
|
+
```
|
77
|
+
|
78
|
+
### 4. Implement Key Rotation Procedures
|
79
|
+
|
80
|
+
**Rotation Process:**
|
81
|
+
1. Add new key version to configuration
|
82
|
+
2. Update `current_key_version`
|
83
|
+
3. Re-encrypt existing data gradually
|
84
|
+
4. Remove old keys after migration complete
|
85
|
+
|
86
|
+
**Example Rotation Script:**
|
87
|
+
```ruby
|
88
|
+
# Add new key version
|
89
|
+
Familia.config.encryption_keys[:v3] = 'new-key'
|
90
|
+
Familia.config.current_key_version = :v3
|
91
|
+
|
92
|
+
# Re-encrypt existing records
|
93
|
+
Vault.all.each do |vault|
|
94
|
+
vault.save # Automatically uses new key version
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
## Security Best Practices
|
99
|
+
|
100
|
+
- **Environment Variables:** Store keys in environment variables, not code
|
101
|
+
- **Key Rotation:** Rotate encryption keys regularly (quarterly/annually)
|
102
|
+
- **Field Selection:** Only encrypt fields that truly need protection
|
103
|
+
- **Memory Clearing:** Use transient fields for temporary sensitive data
|
104
|
+
- **Logging:** Verify RedactedString prevents accidental logging
|
105
|
+
|
106
|
+
## Next Steps
|
107
|
+
|
108
|
+
After implementing security features:
|
109
|
+
1. Review [Architecture Migration](v2.0.0-pre6.md) for persistence improvements
|
110
|
+
2. Explore [Relationships Migration](v2.0.0-pre7.md) for the relationship system
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Migrating Guide: Architecture Improvements (v2.0.0-pre6)
|
2
|
+
|
3
|
+
This guide covers the architecture enhancements and new persistence methods in v2.0.0-pre6.
|
4
|
+
|
5
|
+
## Architecture Improvements
|
6
|
+
|
7
|
+
### 1. Enhanced Persistence Operations
|
8
|
+
|
9
|
+
**New `save_if_not_exists` Method:**
|
10
|
+
```ruby
|
11
|
+
user = User.new(email: 'user@example.com')
|
12
|
+
|
13
|
+
# Only save if the user doesn't already exist
|
14
|
+
if user.save_if_not_exists
|
15
|
+
puts "User created successfully"
|
16
|
+
else
|
17
|
+
puts "User already exists"
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
**Atomic Persistence with Transactions:**
|
22
|
+
```ruby
|
23
|
+
user.transaction do |conn|
|
24
|
+
conn.set(user.key, user.serialize)
|
25
|
+
conn.sadd("all_users", user.identifier)
|
26
|
+
conn.expire(user.key, user.ttl) if user.ttl
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
### 2. Modular Class Structure
|
31
|
+
|
32
|
+
The Horreum class structure was reorganized for better maintainability:
|
33
|
+
|
34
|
+
**Core Modules:**
|
35
|
+
- `Familia::Horreum::Core` - Essential functionality
|
36
|
+
- `Familia::Horreum::ClassMethods` - Class-level methods
|
37
|
+
- `Familia::Horreum::Serialization` - Object serialization
|
38
|
+
- `Familia::Horreum::Commands` - Redis command wrappers
|
39
|
+
|
40
|
+
**Feature System Improvements:**
|
41
|
+
- Dependency management between features
|
42
|
+
- Cleaner feature activation
|
43
|
+
- Better error handling for missing dependencies
|
44
|
+
|
45
|
+
### 3. Enhanced Error Handling
|
46
|
+
|
47
|
+
**New Exception Types:**
|
48
|
+
```ruby
|
49
|
+
begin
|
50
|
+
user.save!
|
51
|
+
rescue Familia::PersistenceError => e
|
52
|
+
puts "Failed to save: #{e.message}"
|
53
|
+
rescue Familia::ValidationError => e
|
54
|
+
puts "Validation failed: #{e.message}"
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
**Improved Data Consistency:**
|
59
|
+
- Automatic retry for transient Redis connection issues
|
60
|
+
- Better handling of concurrent modifications
|
61
|
+
- Enhanced validation before persistence operations
|
62
|
+
|
63
|
+
## Migration Steps
|
64
|
+
|
65
|
+
### 1. Update Error Handling
|
66
|
+
|
67
|
+
**Before (v2.0.0-pre5):**
|
68
|
+
```ruby
|
69
|
+
begin
|
70
|
+
user.save
|
71
|
+
rescue => e
|
72
|
+
puts "Something went wrong: #{e}"
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
**After (v2.0.0-pre6):**
|
77
|
+
```ruby
|
78
|
+
begin
|
79
|
+
user.save
|
80
|
+
rescue Familia::PersistenceError => e
|
81
|
+
puts "Persistence failed: #{e.message}"
|
82
|
+
# Handle specific persistence issues
|
83
|
+
rescue Familia::ValidationError => e
|
84
|
+
puts "Invalid data: #{e.message}"
|
85
|
+
# Handle validation failures
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
### 2. Adopt Conditional Persistence
|
90
|
+
|
91
|
+
Replace existence checks with atomic operations:
|
92
|
+
|
93
|
+
**Before:**
|
94
|
+
```ruby
|
95
|
+
user = User.new(email: email)
|
96
|
+
unless User.exists?(email)
|
97
|
+
user.save
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
**After:**
|
102
|
+
```ruby
|
103
|
+
user = User.new(email: email)
|
104
|
+
user.save_if_not_exists
|
105
|
+
```
|
106
|
+
|
107
|
+
### 3. Leverage Transaction Support
|
108
|
+
|
109
|
+
For complex operations, use transactions:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# Before - Multiple separate operations
|
113
|
+
user.save
|
114
|
+
user.tags.add(tag)
|
115
|
+
user.scores.add(score, timestamp)
|
116
|
+
|
117
|
+
# After - Atomic transaction
|
118
|
+
user.transaction do |conn|
|
119
|
+
conn.set(user.key, user.serialize)
|
120
|
+
conn.sadd(user.tags.key, tag)
|
121
|
+
conn.zadd(user.scores.key, timestamp, score)
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
## Performance Improvements
|
126
|
+
|
127
|
+
### Connection Management
|
128
|
+
- Improved connection pooling with better resource utilization
|
129
|
+
- Reduced connection overhead through intelligent connection reuse
|
130
|
+
- Enhanced concurrent operation support
|
131
|
+
|
132
|
+
### Feature System
|
133
|
+
- Lazy feature loading reduces memory footprint
|
134
|
+
- Optimized method dispatch for feature methods
|
135
|
+
- Better dependency resolution
|
136
|
+
|
137
|
+
## Breaking Changes
|
138
|
+
|
139
|
+
### Method Signatures
|
140
|
+
- Some internal methods changed signatures for better consistency
|
141
|
+
- Error handling improved with specific exception types
|
142
|
+
- Transaction block interface standardized
|
143
|
+
|
144
|
+
### Feature Dependencies
|
145
|
+
- Features now explicitly declare dependencies
|
146
|
+
- Better error messages for missing feature requirements
|
147
|
+
- Automatic dependency resolution where possible
|
148
|
+
|
149
|
+
## Next Steps
|
150
|
+
|
151
|
+
After completing architecture migration:
|
152
|
+
1. Explore [Relationships Migration](v2.0.0-pre7.md) for the comprehensive relationship system
|
153
|
+
2. Review updated documentation for architectural patterns
|
154
|
+
3. Consider adopting new persistence patterns in your application
|