familia 2.0.0.pre26 → 2.0.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.
@@ -1,241 +0,0 @@
1
- # Migrating Guide: v2.0.0-pre22
2
-
3
- This version introduces significant performance optimizations for Redis operations, completes the bidirectional relationships feature, and improves flexibility for external identifiers.
4
-
5
- ## Major Features
6
-
7
- ### Bidirectional Relationship Methods
8
-
9
- **What's New:**
10
-
11
- The `participates_in` declarations now generate reverse collection methods with the `_instances` suffix, providing symmetric access to relationships from both directions.
12
-
13
- **Generated Methods:**
14
- ```ruby
15
- class User < Familia::Horreum
16
- participates_in Team, :members
17
- participates_in Organization, :employees
18
- end
19
-
20
- # New reverse collection methods:
21
- user.team_instances # => [team1, team2]
22
- user.team_ids # => ["team_123", "team_456"]
23
- user.team? # => true/false
24
- user.team_count # => 2
25
-
26
- user.organization_instances # => [org1]
27
- user.organization_ids # => ["org_789"]
28
- ```
29
-
30
- **Custom Names:**
31
- ```ruby
32
- class User < Familia::Horreum
33
- participates_in Organization, :contractors, as: :clients
34
- end
35
-
36
- user.clients_instances # Instead of organization_instances
37
- user.clients_ids # Instead of organization_ids
38
- ```
39
-
40
- **Migration:**
41
-
42
- No changes required for existing code. The new methods are additive and don't affect existing `participates_in` functionality.
43
-
44
- ### Pipelined Bulk Loading
45
-
46
- **What's New:**
47
-
48
- New `load_multi` methods provide up to 2× performance improvement for bulk object loading by using Redis pipelining.
49
-
50
- **Before (N×2 commands):**
51
- ```ruby
52
- users = ids.map { |id| User.find_by_id(id) }
53
- # For 14 objects: 28 Redis commands (14 EXISTS + 14 HGETALL)
54
- ```
55
-
56
- **After (1 round trip):**
57
- ```ruby
58
- users = User.load_multi(ids)
59
- # For 14 objects: 1 pipelined batch with 14 HGETALL commands
60
- ```
61
-
62
- **Additional Methods:**
63
- ```ruby
64
- # Load by full dbkeys
65
- users = User.load_multi_by_keys(['user:123:object', 'user:456:object'])
66
-
67
- # Filter out nils for missing objects
68
- existing_only = User.load_multi(ids).compact
69
- ```
70
-
71
- ### Optional EXISTS Check Optimization
72
-
73
- **What's New:**
74
-
75
- The `find_by_id` and related methods now support skipping the EXISTS check for 50% reduction in Redis commands.
76
-
77
- ```ruby
78
- # Default behavior (unchanged, 2 commands)
79
- user = User.find_by_id(123)
80
-
81
- # Optimized mode (1 command)
82
- user = User.find_by_id(123, check_exists: false)
83
- ```
84
-
85
- **When to Use:**
86
- - Performance-critical paths
87
- - Bulk operations with known-to-exist keys
88
- - High-throughput APIs
89
- - Loading from sorted set results
90
-
91
- ## Enhanced Features
92
-
93
- ### Flexible External Identifier Format
94
-
95
- **What's New:**
96
-
97
- The `external_identifier` feature now supports custom format templates.
98
-
99
- **Examples:**
100
- ```ruby
101
- # Default format (unchanged)
102
- class User < Familia::Horreum
103
- feature :external_identifier
104
- end
105
- user.extid # => "ext_abc123def456"
106
-
107
- # Custom prefix
108
- class Customer < Familia::Horreum
109
- feature :external_identifier, format: 'cust_%{id}'
110
- end
111
- customer.extid # => "cust_abc123def456"
112
-
113
- # Different separator
114
- class APIKey < Familia::Horreum
115
- feature :external_identifier, format: 'api-%{id}'
116
- end
117
- key.extid # => "api-abc123def456"
118
- ```
119
-
120
- ### Atomic Index Rebuilding
121
-
122
- **What's New:**
123
-
124
- Auto-generated rebuild methods for all unique and multi indexes with zero downtime.
125
-
126
- **Examples:**
127
-
128
- ```ruby
129
- # Class-level unique index
130
- User.rebuild_email_lookup
131
-
132
- # Instance-scoped unique index
133
- company.rebuild_badge_index
134
-
135
- # With progress tracking
136
- User.rebuild_email_lookup(batch_size: 100) do |progress|
137
- puts "#{progress[:completed]}/#{progress[:total]}"
138
- end
139
- ```
140
-
141
- When to Use:
142
- - After data migrations or bulk imports
143
- - Recovering from index corruption
144
- - Adding indexes to existing data
145
-
146
- Migration:
147
-
148
- Run rebuild methods once after upgrade to ensure index consistency. No code changes required—methods are auto-generated from existing
149
- unique_index and multi_index declarations.
150
-
151
-
152
- ## Bug Fixes
153
-
154
- ### Symbol/String Target Classes in participates_in
155
-
156
- **What Was Fixed:**
157
-
158
- Fixed multiple bugs when using Symbol or String class names in `participates_in`:
159
-
160
- ```ruby
161
- class Domain < Familia::Horreum
162
- # All forms now work correctly:
163
- participates_in Customer, :domains # Class object
164
- participates_in :Customer, :domains # Symbol (was broken)
165
- participates_in 'Customer', :domains # String (was broken)
166
- end
167
- ```
168
-
169
- **Errors Fixed:**
170
- - `NoMethodError: private method 'member_by_config_name'`
171
- - `NoMethodError: undefined method 'familia_name' for Symbol`
172
- - `NoMethodError: undefined method 'config_name' for Symbol`
173
- - Confusing nil errors for unloaded classes
174
-
175
- **New Behavior:**
176
-
177
- When a target class can't be resolved, you now get a helpful error:
178
- ```
179
- Target class 'Customer' could not be resolved.
180
- Possible causes:
181
- 1. The class hasn't been loaded yet (load order issue)
182
- 2. The class name is misspelled
183
- 3. The class doesn't inherit from Familia::Horreum
184
-
185
- Registered Familia classes: ["User", "Team", "Organization"]
186
- ```
187
-
188
- ## Performance Recommendations
189
-
190
- ### Use Bulk Loading for Collections
191
-
192
- ```ruby
193
- # ❌ Avoid N+1 queries
194
- team.members.to_a.map { |id| User.find_by_id(id) }
195
-
196
- # ✅ Use bulk loading
197
- User.load_multi(team.members.to_a)
198
- ```
199
-
200
- ### Skip EXISTS Checks When Safe
201
-
202
- ```ruby
203
- # When loading from sorted sets (keys guaranteed to exist)
204
- task_ids = project.tasks.range(0, 9)
205
- tasks = Task.load_multi(task_ids) # Or use check_exists: false
206
-
207
- # For known-existing keys
208
- user = User.find_by_id(session[:user_id], check_exists: false)
209
- ```
210
-
211
- ### Leverage Reverse Collection Methods
212
-
213
- ```ruby
214
- # ❌ Manual parsing of participations
215
- team_keys = user.participations.members.select { |k| k.start_with?("team:") }
216
- team_ids = team_keys.map { |k| k.split(':')[1] }
217
- teams = Team.load_multi(team_ids)
218
-
219
- # ✅ Use generated methods
220
- teams = user.team_instances
221
- ```
222
-
223
- ## Backwards Compatibility
224
-
225
- All changes in this version are backwards compatible:
226
-
227
- - New methods are additive and don't affect existing APIs
228
- - Default behaviors remain unchanged
229
- - Symbol/String fixes don't require code changes
230
-
231
- ## Recommended Actions
232
-
233
- 1. **Adopt bulk loading** for performance-critical paths
234
- 2. **Use reverse collection methods** to simplify relationship queries
235
- 3. **Consider check_exists: false** for guaranteed-existing keys
236
- 4. **Update external_identifier formats** if custom prefixes are needed
237
-
238
- ## See Also
239
-
240
- - [Relationships Guide](../guides/feature-relationships.md)
241
- - [Performance Optimization Guide](../guides/optimized-loading.md)
@@ -1,131 +0,0 @@
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 `ConcealedString` 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
- # ConcealedString automatically excluded from serialization
67
- { name: name }.to_json # password field omitted if transient
68
- end
69
- ```
70
-
71
- **Manual ConcealedString Handling:**
72
- ```ruby
73
- # Access original value when needed
74
- password.reveal # Returns actual string value
75
- password.cleared? # Returns true if cleared from memory
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 using `re_encrypt_fields!`
84
- 4. Verify migration completion
85
- 5. Remove old keys after migration complete
86
-
87
- **Example Rotation Script:**
88
- ```ruby
89
- # Step 1: Add new key version
90
- Familia.configure do |config|
91
- config.encryption_keys = {
92
- v2: ENV['OLD_ENCRYPTION_KEY'],
93
- v3: ENV['NEW_ENCRYPTION_KEY']
94
- }
95
- config.current_key_version = :v3
96
- end
97
-
98
- # Step 2: Validate configuration
99
- Familia::Encryption.validate_configuration!
100
-
101
- # Step 3: Re-encrypt existing records
102
- Vault.all.each do |vault|
103
- vault.re_encrypt_fields! # Re-encrypts with current key
104
- vault.save
105
- end
106
-
107
- # Step 4: Verify migration
108
- Vault.all.each do |vault|
109
- status = vault.encrypted_fields_status
110
- puts "Vault #{vault.identifier}: #{status}"
111
- end
112
-
113
- # Step 5: Remove old key (after verification)
114
- Familia.config.encryption_keys.delete(:v2)
115
- ```
116
-
117
- ## Security Best Practices
118
-
119
- - **Environment Variables:** Store keys in environment variables, not code
120
- - **Key Rotation:** Rotate encryption keys regularly (quarterly/annually)
121
- - **Field Selection:** Only encrypt fields that truly need protection
122
- - **Memory Clearing:** Use transient fields for temporary sensitive data
123
- - **Logging:** Verify ConcealedString prevents accidental logging
124
- - **Configuration Validation:** Use `validate_configuration!` before production
125
- - **Monitoring:** Use `encrypted_fields_status` to track encryption state
126
-
127
- ## Next Steps
128
-
129
- After implementing security features:
130
- 1. Review [Architecture Migration](v2.0.0-pre6.md) for persistence improvements
131
- 2. Explore [Relationships Migration](v2.0.0-pre7.md) for the relationship system
@@ -1,154 +0,0 @@
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` - Valkey/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 Valkey/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