familia 2.0.0.pre12 → 2.0.0.pre14

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +2 -3
  3. data/CHANGELOG.rst +529 -0
  4. data/CLAUDE.md +1 -1
  5. data/Gemfile +1 -6
  6. data/Gemfile.lock +13 -7
  7. data/README.md +21 -2
  8. data/changelog.d/README.md +5 -5
  9. data/{setup.cfg → changelog.d/scriv.ini} +1 -1
  10. data/docs/guides/Feature-System-Autoloading.md +228 -0
  11. data/docs/guides/time-utilities.md +221 -0
  12. data/docs/migrating/v2.0.0-pre11.md +14 -16
  13. data/docs/migrating/v2.0.0-pre13.md +95 -0
  14. data/docs/migrating/v2.0.0-pre14.md +37 -0
  15. data/examples/autoloader/mega_customer/safe_dump_fields.rb +6 -0
  16. data/examples/autoloader/mega_customer.rb +17 -0
  17. data/examples/safe_dump.rb +1 -1
  18. data/familia.gemspec +1 -0
  19. data/lib/familia/autoloader.rb +53 -0
  20. data/lib/familia/base.rb +5 -0
  21. data/lib/familia/data_type.rb +4 -0
  22. data/lib/familia/encryption/encrypted_data.rb +4 -4
  23. data/lib/familia/encryption/manager.rb +6 -4
  24. data/lib/familia/encryption.rb +1 -1
  25. data/lib/familia/errors.rb +3 -0
  26. data/lib/familia/features/autoloadable.rb +113 -0
  27. data/lib/familia/features/encrypted_fields/concealed_string.rb +4 -2
  28. data/lib/familia/features/expiration.rb +4 -0
  29. data/lib/familia/features/external_identifier.rb +3 -3
  30. data/lib/familia/features/quantization.rb +5 -0
  31. data/lib/familia/features/safe_dump.rb +7 -0
  32. data/lib/familia/features.rb +20 -16
  33. data/lib/familia/field_type.rb +2 -0
  34. data/lib/familia/horreum/core/serialization.rb +3 -3
  35. data/lib/familia/horreum/subclass/definition.rb +3 -4
  36. data/lib/familia/horreum.rb +2 -0
  37. data/lib/familia/json_serializer.rb +70 -0
  38. data/lib/familia/logging.rb +12 -10
  39. data/lib/familia/refinements/logger_trace.rb +57 -0
  40. data/lib/familia/refinements/snake_case.rb +40 -0
  41. data/lib/familia/refinements/time_literals.rb +279 -0
  42. data/lib/familia/refinements.rb +3 -49
  43. data/lib/familia/utils.rb +2 -0
  44. data/lib/familia/validation/{test_helpers.rb → validation_helpers.rb} +2 -2
  45. data/lib/familia/validation.rb +1 -1
  46. data/lib/familia/version.rb +1 -1
  47. data/lib/familia.rb +15 -3
  48. data/try/core/autoloader_try.rb +112 -0
  49. data/try/core/extensions_try.rb +38 -21
  50. data/try/core/familia_extended_try.rb +4 -3
  51. data/try/core/time_utils_try.rb +130 -0
  52. data/try/data_types/datatype_base_try.rb +3 -2
  53. data/try/features/autoloadable/autoloadable_try.rb +61 -0
  54. data/try/features/encrypted_fields/concealed_string_core_try.rb +8 -3
  55. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +59 -17
  56. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +36 -12
  57. data/try/features/external_identifier/external_identifier_try.rb +26 -0
  58. data/try/features/feature_improvements_try.rb +2 -1
  59. data/try/features/real_feature_integration_try.rb +1 -1
  60. data/try/features/safe_dump/safe_dump_autoloading_try.rb +111 -0
  61. data/try/helpers/test_helpers.rb +24 -0
  62. data/try/integration/cross_component_try.rb +3 -1
  63. metadata +34 -6
  64. data/CHANGELOG.md +0 -247
  65. data/lib/familia/core_ext.rb +0 -135
  66. data/lib/familia/features/autoloader.rb +0 -57
@@ -17,11 +17,11 @@ This approach provides several benefits:
17
17
 
18
18
  ## Relevant paths
19
19
 
20
- * `changelog.d/` - (e.g. changelog.d/YYYYMMDD_HHmmss_username_branch.md)
20
+ * `changelog.d/` - (e.g. changelog.d/YYYYMMDD_HHmmss_username_branch.rst)
21
21
  * `docs/migrating/` - (e.g. docs/migrating/v2.0.0-pre.md)
22
- * `CHANGELOG.md` - The full changelog for all releases, in reverse chronological order. Careful: LARGE DOCUMENT. Limit reading to the first 50 lines.
22
+ * `CHANGELOG.rst` - The full changelog for all releases, in reverse chronological order. Careful: LARGE DOCUMENT. Limit reading to the first 50 lines.
23
23
 
24
- * `setup.cfg` - Scriv tool settings
24
+ * `changelog.d/scriv.ini` - Scriv tool settings
25
25
 
26
26
  ## How to Add a Changelog Entry
27
27
 
@@ -44,7 +44,7 @@ Compare the headers of your draft content with the headers of the previous migra
44
44
 
45
45
  4. **Commit with Your Code:**
46
46
  ```bash
47
- git add changelog.d/YYYYMMDD_HHmmss_username_branch.md [docs/migrating/v2.0.0-pre.md]
47
+ git add changelog.d/YYYYMMDD_HHmmss_username_branch.rst [docs/migrating/v2.0.0-pre.md]
48
48
  git commit
49
49
  ```
50
50
 
@@ -74,4 +74,4 @@ Use these categories:
74
74
 
75
75
  ## Release Process
76
76
 
77
- At release time, scriv will collect all fragments into the main `CHANGELOG.md` file with th command `scriv collect`. The version is taken automatically from `lib/familia/version.rb`.
77
+ At release time, scriv will collect all fragments into the main `CHANGELOG.rst` file with th command `scriv collect`. The version is taken automatically from `lib/familia/version.rb`.
@@ -1,5 +1,5 @@
1
1
  [scriv]
2
+ format = rst
2
3
  categories = Added, Changed, Deprecated, Removed, Fixed, Security, Documentation, AI Assistance
3
4
  version = command: ruby -r ./lib/familia/version.rb -e "puts Familia::VERSION"
4
5
  main_branches = main, develop
5
- md_header_level = 2
@@ -0,0 +1,228 @@
1
+ # Feature System Autoloading
2
+
3
+ ## Overview
4
+
5
+ Familia's feature system includes an autoloading mechanism that automatically discovers and loads feature-specific extension files when features are included in your classes. This allows you to keep your main model files clean while organizing feature-specific configurations in separate files.
6
+
7
+ ## The Problem It Solves
8
+
9
+ When you include a feature like `safe_dump` in a Familia class:
10
+
11
+ ```ruby
12
+ class User < Familia::Horreum
13
+ feature :safe_dump # This line should trigger autoloading
14
+ end
15
+ ```
16
+
17
+ You want to be able to define the safe dump configuration in a separate file:
18
+
19
+ ```ruby
20
+ # user/safe_dump_extensions.rb
21
+ class User
22
+ safe_dump_fields :name, :email # Don't dump :password
23
+ end
24
+ ```
25
+
26
+ But there's a **timing problem**: when should this extension file be loaded?
27
+
28
+ ## Why Standard `included` Hook Doesn't Work
29
+
30
+ The original approach tried to use the standard `included` hook:
31
+
32
+ ```ruby
33
+ module SafeDump
34
+ def self.included(base)
35
+ # Try to autoload here - BUT THIS IS TOO EARLY!
36
+ autoload_files_for(base)
37
+ end
38
+ end
39
+ ```
40
+
41
+ **Problem**: This happens **during** the feature inclusion process, before the feature is fully set up. The class isn't in a stable state yet.
42
+
43
+ ## The Solution: Post-Inclusion Hook
44
+
45
+ The `post_inclusion_autoload` system works in **two phases**:
46
+
47
+ ### Phase 1: Feature System Hook
48
+
49
+ In `lib/familia/features.rb`, after including the feature module:
50
+
51
+ ```ruby
52
+ def feature(feature_name, **options)
53
+ # ... setup code ...
54
+
55
+ include feature_class # Include the feature module
56
+
57
+ # NOW call the post-inclusion hook
58
+ if feature_class.respond_to?(:post_inclusion_autoload)
59
+ feature_class.post_inclusion_autoload(self, feature_name, options)
60
+ end
61
+ end
62
+ ```
63
+
64
+ ### Phase 2: Autoloadable Implementation
65
+
66
+ The `post_inclusion_autoload` method in `lib/familia/features/autoloadable.rb`:
67
+
68
+ 1. **Gets the source location** of the user's class file using Ruby's introspection:
69
+ ```ruby
70
+ location_info = Module.const_source_location(base.name)
71
+ source_location = location_info&.first # e.g., "/path/to/models/user.rb"
72
+ ```
73
+
74
+ 2. **Calculates extension file paths** based on conventions:
75
+ ```ruby
76
+ base_dir = File.dirname(location_path) # "/path/to/models"
77
+ model_name = base.name.snake_case # "user"
78
+
79
+ # Look for files like:
80
+ patterns = [
81
+ "/path/to/models/user/safe_dump_*.rb",
82
+ "/path/to/models/user/features/safe_dump_*.rb",
83
+ "/path/to/models/features/safe_dump_*.rb"
84
+ ]
85
+ ```
86
+
87
+ 3. **Loads any matching files** found in those locations
88
+
89
+ ## Why This Timing Matters
90
+
91
+ ```ruby
92
+ class User < Familia::Horreum
93
+ feature :safe_dump # ← Timing is critical here
94
+ end
95
+ ```
96
+
97
+ **What happens in order:**
98
+
99
+ 1. `feature :safe_dump` is called
100
+ 2. Feature system includes `Familia::Features::SafeDump` module
101
+ 3. **Feature is now fully included and stable**
102
+ 4. `post_inclusion_autoload` is called
103
+ 5. Extension files are discovered and loaded
104
+ 6. `safe_dump_fields :name, :email` executes in the extension file
105
+
106
+ ## File Naming Conventions
107
+
108
+ The autoloading system looks for files matching these patterns (in order of precedence):
109
+
110
+ 1. `{model_directory}/{model_name}/{feature_name}_*.rb`
111
+ 2. `{model_directory}/{model_name}/features/{feature_name}_*.rb`
112
+ 3. `{model_directory}/features/{feature_name}_*.rb`
113
+
114
+ ### Examples
115
+
116
+ For a `User` class defined in `app/models/user.rb` with `feature :safe_dump`:
117
+
118
+ ```
119
+ app/models/user/safe_dump_extensions.rb # ← Most specific
120
+ app/models/user/safe_dump_config.rb # ← Also matches pattern
121
+ app/models/user/features/safe_dump_*.rb # ← Feature subdirectory
122
+ app/models/features/safe_dump_*.rb # ← Shared feature configs
123
+ ```
124
+
125
+ ## Complete Example
126
+
127
+ ### Main Model File
128
+
129
+ ```ruby
130
+ # app/models/user.rb
131
+ class User < Familia::Horreum
132
+ field :name
133
+ field :email
134
+ field :password
135
+ field :created_at
136
+
137
+ feature :safe_dump # ← Triggers autoloading
138
+ feature :expiration
139
+ end
140
+ ```
141
+
142
+ ### Safe Dump Extensions
143
+
144
+ ```ruby
145
+ # app/models/user/safe_dump_extensions.rb
146
+ class User
147
+ # Configure which fields are safe to dump in API responses
148
+ safe_dump_fields :name, :email, :created_at
149
+ # Note: :password is intentionally excluded for security
150
+
151
+ def safe_dump_display_name
152
+ "#{name} (#{email})"
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### Expiration Extensions
158
+
159
+ ```ruby
160
+ # app/models/user/expiration_config.rb
161
+ class User
162
+ # Set default TTL for user objects
163
+ expires_in 30.days
164
+
165
+ # Custom expiration logic
166
+ def should_expire?
167
+ !active? && last_login_at < 90.days.ago
168
+ end
169
+ end
170
+ ```
171
+
172
+ ### Result
173
+
174
+ After loading, the `User` class has:
175
+ - `User.safe_dump_field_names` returns `[:name, :email, :created_at]`
176
+ - `User.ttl` returns the configured expiration
177
+ - All extension methods are available on instances
178
+
179
+ ## Key Benefits
180
+
181
+ 1. **Separation of Concerns**: Main model file focuses on core definition, extension files handle feature-specific configuration
182
+
183
+ 2. **Convention Over Configuration**: No manual requires, just follow naming conventions
184
+
185
+ 3. **Safe Timing**: Extension files load after the feature is fully set up
186
+
187
+ 4. **Thread Safe**: No shared state between classes
188
+
189
+ 5. **Discoverable**: Clear file organization makes extensions easy to find
190
+
191
+ ## Why It's Better Than Alternatives
192
+
193
+ - **Manual requires**: Error-prone, verbose, easy to forget
194
+ - **Configuration blocks**: Clutters the main model file
195
+ - **Included hook**: Wrong timing, class not stable yet
196
+ - **Class_eval strings**: Complex, hard to debug and maintain
197
+
198
+ The `post_inclusion_autoload` system provides a clean, automatic, and safe way to extend feature behavior without polluting the main class definitions.
199
+
200
+ ## Implementation Details
201
+
202
+ ### Autoloadable Module
203
+
204
+ Features that support autoloading include the `Familia::Features::Autoloadable` module:
205
+
206
+ ```ruby
207
+ module Familia::Features::SafeDump
208
+ include Familia::Features::Autoloadable # ← Enables autoloading
209
+
210
+ # Feature implementation...
211
+ end
212
+ ```
213
+
214
+ ### Anonymous Class Handling
215
+
216
+ The system gracefully handles edge cases:
217
+
218
+ - **Anonymous classes**: Classes without names (e.g., `Class.new`) are skipped
219
+ - **Eval contexts**: Classes defined in `eval` or irb are skipped
220
+ - **Missing files**: No errors if extension files don't exist
221
+
222
+ ### Error Handling
223
+
224
+ - Missing extension files are silently ignored
225
+ - Syntax errors in extension files propagate normally
226
+ - `NameError` during constant resolution is caught and logged
227
+
228
+ This robust error handling ensures the autoloading system never breaks your application, even with unusual class definitions or missing files.
@@ -0,0 +1,221 @@
1
+ # Time Utilities Guide
2
+
3
+ Familia provides a comprehensive time utilities refinement that adds convenient time conversion and age calculation methods to Ruby's `Numeric` and `String` classes.
4
+
5
+ ## Overview
6
+
7
+ The `Familia::Refinements::TimeLiterals` module extends Ruby's built-in classes with intuitive time manipulation methods:
8
+
9
+ ```ruby
10
+ using Familia::Refinements::TimeLiterals
11
+
12
+ 2.hours #=> 7200 (seconds)
13
+ "30m".in_seconds #=> 1800
14
+ timestamp.days_old #=> 5.2
15
+ ```
16
+
17
+ ## Time Constants
18
+
19
+ Familia uses **Gregorian year** calculations for consistent time conversions:
20
+
21
+ ```ruby
22
+ PER_YEAR = 31_556_952.0 # 365.2425 days (accounts for leap years)
23
+ PER_MONTH = 2_629_746.0 # PER_YEAR / 12 (30.437 days)
24
+ PER_WEEK = 604_800.0 # 7 days
25
+ PER_DAY = 86_400.0 # 24 hours
26
+ ```
27
+
28
+ ### Why Gregorian Year?
29
+
30
+ The key design decision ensures **mathematical consistency**:
31
+
32
+ ```ruby
33
+ 12.months == 1.year #=> true (both equal 31,556,952 seconds)
34
+ ```
35
+
36
+ This prevents subtle bugs where `12.months` and `1.year` would differ by ~5.8 hours.
37
+
38
+ ## Basic Time Conversions
39
+
40
+ ### Converting Numbers to Time Units
41
+
42
+ ```ruby
43
+ using Familia::Refinements::TimeLiterals
44
+
45
+ # Singular and plural forms work identically
46
+ 1.second #=> 1
47
+ 30.seconds #=> 30
48
+ 5.minutes #=> 300
49
+ 2.hours #=> 7200
50
+ 3.days #=> 259200
51
+ 1.week #=> 604800
52
+ 2.months #=> 5259492
53
+ 1.year #=> 31556952
54
+ ```
55
+
56
+ ### Converting Time Units Back
57
+
58
+ ```ruby
59
+ 7200.in_hours #=> 2.0
60
+ 259200.in_days #=> 3.0
61
+ 5259492.in_months #=> 2.0
62
+ 31556952.in_years #=> 1.0
63
+ ```
64
+
65
+ ## String Time Parsing
66
+
67
+ Parse human-readable time strings:
68
+
69
+ ```ruby
70
+ "30s".in_seconds #=> 30.0
71
+ "5m".in_seconds #=> 300.0
72
+ "2h".in_seconds #=> 7200.0
73
+ "1d".in_seconds #=> 86400.0
74
+ "1w".in_seconds #=> 604800.0
75
+ "2mo".in_seconds #=> 5259492.0
76
+ "1y".in_seconds #=> 31556952.0
77
+ ```
78
+
79
+ ### Supported String Formats
80
+
81
+ | Unit | Abbreviations | Example |
82
+ |------|---------------|---------|
83
+ | Microseconds | `us`, `μs`, `microsecond`, `microseconds` | `"500us"` |
84
+ | Milliseconds | `ms`, `millisecond`, `milliseconds` | `"250ms"` |
85
+ | Seconds | `s`, `second`, `seconds` | `"30s"` |
86
+ | Minutes | `m`, `minute`, `minutes` | `"15m"` |
87
+ | Hours | `h`, `hour`, `hours` | `"2h"` |
88
+ | Days | `d`, `day`, `days` | `"7d"` |
89
+ | Weeks | `w`, `week`, `weeks` | `"2w"` |
90
+ | Months | `mo`, `month`, `months` | `"6mo"` |
91
+ | Years | `y`, `year`, `years` | `"1y"` |
92
+
93
+ **Note**: Use `"mo"` for months to avoid confusion with `"m"` (minutes).
94
+
95
+ ## Age Calculations
96
+
97
+ Calculate how old timestamps are:
98
+
99
+ ```ruby
100
+ # Basic age calculation
101
+ old_timestamp = 2.days.ago.to_i
102
+ old_timestamp.age_in(:days) #=> ~2.0
103
+ old_timestamp.age_in(:hours) #=> ~48.0
104
+ old_timestamp.age_in(:months) #=> ~0.066
105
+
106
+ # Convenience methods
107
+ old_timestamp.days_old #=> ~2.0
108
+ old_timestamp.hours_old #=> ~48.0
109
+ old_timestamp.minutes_old #=> ~2880.0
110
+ old_timestamp.weeks_old #=> ~0.28
111
+ old_timestamp.months_old #=> ~0.066
112
+ old_timestamp.years_old #=> ~0.005
113
+
114
+ # Calculate age from specific reference time
115
+ past_timestamp = 1.week.ago.to_i
116
+ reference_time = 3.days.ago
117
+ past_timestamp.age_in(:days, reference_time) #=> ~4.0
118
+ ```
119
+
120
+ ## Time Comparisons
121
+
122
+ ```ruby
123
+ timestamp = 2.hours.ago.to_i
124
+
125
+ # Check if older than duration
126
+ timestamp.older_than?(1.hour) #=> true
127
+ timestamp.older_than?(3.hours) #=> false
128
+
129
+ # Check if newer than duration (future)
130
+ future_timestamp = 1.hour.from_now.to_i
131
+ future_timestamp.newer_than?(30.minutes) #=> true
132
+
133
+ # Check if within duration of now (past or future)
134
+ timestamp.within?(3.hours) #=> true
135
+ timestamp.within?(1.hour) #=> false
136
+ ```
137
+
138
+ ## Practical Examples
139
+
140
+ ### Cache Expiration
141
+
142
+ ```ruby
143
+ using Familia::Refinements::TimeLiterals
144
+
145
+ class CacheEntry < Familia::Horreum
146
+ field :data
147
+ field :created_at
148
+
149
+ def expired?
150
+ created_at.to_i.older_than?(1.hour)
151
+ end
152
+
153
+ def cache_age
154
+ created_at.to_i.minutes_old
155
+ end
156
+ end
157
+ ```
158
+
159
+ ### Session Management
160
+
161
+ ```ruby
162
+ class UserSession < Familia::Horreum
163
+ field :last_active
164
+
165
+ def stale?
166
+ last_active.to_i.older_than?(30.minutes)
167
+ end
168
+
169
+ def session_duration
170
+ "Active for #{last_active.to_i.hours_old.round(1)} hours"
171
+ end
172
+ end
173
+ ```
174
+
175
+ ### Data Retention
176
+
177
+ ```ruby
178
+ def cleanup_old_logs
179
+ Log.all.select do |log|
180
+ log.timestamp.to_i.older_than?(30.days)
181
+ end.each(&:destroy)
182
+ end
183
+ ```
184
+
185
+ ## Important Notes
186
+
187
+ ### Calendar vs. Precise Time
188
+
189
+ Familia's time utilities use **average durations** suitable for:
190
+ - Age calculations
191
+ - Cache expiration
192
+ - Time-based cleanup
193
+ - General time arithmetic
194
+
195
+ For **calendar-aware operations** (exact months, leap years), use Ruby's `Date`/`Time` classes:
196
+
197
+ ```ruby
198
+ # For average durations (Familia)
199
+ user_age = signup_date.to_i.months_old #=> 6.2
200
+
201
+ # For exact calendar operations (Ruby stdlib)
202
+ exact_months = Date.today.months_since(signup_date) #=> 6
203
+ ```
204
+
205
+ ### Thread Safety
206
+
207
+ All time utility methods are thread-safe and work with frozen objects.
208
+
209
+ ## Migration from v1.x
210
+
211
+ If upgrading from earlier versions:
212
+
213
+ ```ruby
214
+ # Old behavior (inconsistent)
215
+ 12.months != 1.year # Different values
216
+
217
+ # New behavior (consistent)
218
+ 12.months == 1.year # Same value: 31,556,952 seconds
219
+ ```
220
+
221
+ Update any code that relied on the old 365-day year constant to expect the new Gregorian year values.
@@ -102,7 +102,7 @@ end
102
102
 
103
103
  ### Before: Manual Loading
104
104
  ```ruby
105
- # apps/api/v2/models/customer/features.rb
105
+ # customer/features.rb
106
106
 
107
107
  # Manual feature loading (copied from Familia)
108
108
  features_dir = File.join(__dir__, 'features')
@@ -112,29 +112,26 @@ if Dir.exist?(features_dir)
112
112
  end
113
113
  end
114
114
 
115
- module V2
116
- class Customer < Familia::Horreum
117
- # Features now available for use
118
- feature :deprecated_fields
119
- end
115
+ class Customer < Familia::Horreum
116
+ # Features now available for use
117
+ feature :deprecated_fields
120
118
  end
121
119
  ```
122
120
 
123
121
  ### After: Automatic Loading
124
122
  ```ruby
125
- # apps/api/v2/models/customer/features.rb
126
- module V2::Customer
123
+ # customer/features.rb
124
+
125
+ class Customer < Familia::Horreum
127
126
  module Features
128
- include Familia::Features::Autoloader
127
+ include Familia::Autoloader
129
128
  # Automatically discovers and loads all *.rb files from customer/features/
130
129
  end
131
130
  end
132
131
 
133
- module V2
134
- class Customer < Familia::Horreum
135
- # Features automatically loaded and available
136
- feature :deprecated_fields
137
- end
132
+ class Customer < Familia::Horreum
133
+ # Features automatically loaded and available
134
+ feature :deprecated_fields
138
135
  end
139
136
  ```
140
137
 
@@ -162,6 +159,7 @@ Feature modules can now define fields directly in their `ClassMethods` modules.
162
159
  ### Example
163
160
  ```ruby
164
161
  # features/common_fields.rb
162
+
165
163
  module CommonFields
166
164
  def self.included(base)
167
165
  base.extend ClassMethods
@@ -210,9 +208,9 @@ If you have project-specific features, set up auto-loading:
210
208
  ```ruby
211
209
  # Create: models/[model_name]/features.rb
212
210
  module YourProject
213
- module ModelName
211
+ class ModelName < Familia::Horreum
214
212
  module Features
215
- include Familia::Features::Autoloader
213
+ include Familia::Autoloader
216
214
  end
217
215
  end
218
216
  end
@@ -0,0 +1,95 @@
1
+ # Migrating Guide: v2.0.0-pre13
2
+
3
+ This version introduces the Feature Autoloading System for automatic discovery and loading of feature-specific configuration files, enabling cleaner separation between core model definitions and feature configurations.
4
+
5
+ ## Feature Autoloading System
6
+
7
+ ### What Changed
8
+
9
+ Features now automatically discover and load extension files from your project directories using conventional file naming patterns. This eliminates the need to configure features in your main model files.
10
+
11
+ ### Basic Migration
12
+
13
+ #### Before
14
+ ```ruby
15
+ # app/models/user.rb
16
+ class User < Familia::Horreum
17
+ field :name, :email, :password
18
+
19
+ feature :safe_dump
20
+ safe_dump_fields :name, :email # Configuration mixed with model
21
+ end
22
+ ```
23
+
24
+ #### After
25
+ ```ruby
26
+ # app/models/user.rb - Clean model definition
27
+ class User < Familia::Horreum
28
+ field :name, :email, :password
29
+ feature :safe_dump # Configuration auto-loaded
30
+ end
31
+
32
+ # app/models/user/safe_dump_extensions.rb - Automatically discovered
33
+ class User
34
+ safe_dump_fields :name, :email
35
+ end
36
+ ```
37
+
38
+ ### File Naming Convention
39
+
40
+ Extension files follow the pattern: `{model_name}/{feature_name}_*.rb`
41
+
42
+ ```
43
+ app/models/
44
+ ├── user.rb
45
+ ├── user/
46
+ │ ├── safe_dump_extensions.rb # SafeDump configuration
47
+ │ └── expiration_config.rb # Expiration settings
48
+ ```
49
+
50
+ ### Migration Steps
51
+
52
+ 1. **Create extension directories**: `mkdir -p app/models/user`
53
+ 2. **Extract feature configuration** from main model files to separate extension files
54
+ 3. **Verify autoloading**: Check that feature methods are available after migration
55
+
56
+ ### Debugging
57
+
58
+ Enable debug output to troubleshoot autoloading:
59
+ ```ruby
60
+ ENV['FAMILIA_DEBUG'] = '1' # Shows discovered and loaded files
61
+ ```
62
+
63
+ Common issues:
64
+ - Files must follow `{feature_name}_*.rb` naming pattern
65
+ - Extension files should reopen the same class as your model
66
+
67
+ ## Architecture
68
+
69
+ The Feature Autoloading System consists of two key components:
70
+
71
+ ### Familia::Autoloader
72
+ A utility module providing shared file loading functionality:
73
+ - Handles Dir.glob pattern matching and file loading
74
+ - Provides consistent debug logging across all autoloading scenarios
75
+ - Used by both feature-specific and general-purpose autoloading
76
+
77
+ ### Familia::Features::Autoloadable
78
+ A mixin for feature modules that enables post-inclusion autoloading:
79
+ - Uses `Module.const_source_location` to find where user classes are defined
80
+ - Discovers extension files using conventional patterns relative to the user class location
81
+ - Integrates with the feature system's inclusion lifecycle
82
+
83
+ When you call `feature :safe_dump`, the SafeDump module (which includes Autoloadable) triggers post-inclusion autoloading that searches for `user/safe_dump_*.rb` files and loads them automatically.
84
+
85
+ ## New Capabilities
86
+
87
+ 1. **Automatic Extension Discovery**: No manual require statements needed
88
+ 2. **Conventional File Organization**: Standard patterns for consistent project structure
89
+ 3. **Feature Isolation**: Clean separation between core models and feature configurations
90
+ 4. **Shared Autoloader Infrastructure**: Consistent loading behavior across all features
91
+ 5. **Debug Support**: Built-in debugging for troubleshooting autoloading issues
92
+
93
+ ## Breaking Changes
94
+
95
+ **None** - This release is fully backward compatible. Existing models and feature configurations continue to work without modification.
@@ -0,0 +1,37 @@
1
+ # Migrating Guide: v2.0.0-pre14
2
+
3
+ This version renames TimeUtils to TimeLiterals for semantic clarity and fixes an ExternalIdentifier bug.
4
+
5
+ ## Breaking Change: TimeUtils → TimeLiterals
6
+
7
+ **Migration Required:**
8
+
9
+ Find and replace in your codebase:
10
+ ```bash
11
+ # Find files to update
12
+ grep -r "using Familia::Refinements::TimeUtils" .
13
+
14
+ # Replace the import
15
+ sed -i 's/using Familia::Refinements::TimeUtils/using Familia::Refinements::TimeLiterals/g' *.rb
16
+ ```
17
+
18
+ **Before:**
19
+ ```ruby
20
+ using Familia::Refinements::TimeUtils
21
+ ```
22
+
23
+ **After:**
24
+ ```ruby
25
+ using Familia::Refinements::TimeLiterals
26
+ ```
27
+
28
+ All functionality remains identical - only the module name changed.
29
+
30
+ ## Bug Fix: ExternalIdentifier
31
+
32
+ Fixed `NoMethodError` when using ExternalIdentifier by replacing incorrect `.del()` calls with `.remove_field()` in HashKey operations. This affected:
33
+ - Changing external identifier values
34
+ - Looking up objects by external ID
35
+ - Destroying objects with external identifiers
36
+
37
+ No migration needed - the fix is automatic.
@@ -0,0 +1,6 @@
1
+ # examples/autoloader/mega_customer/safe_dump_fields.rb
2
+
3
+ # Extend the MegaCustomer class to add safe dump fields
4
+ class MegaCustomer
5
+ safe_dump_fields :custid, :username, :created_at, :updated_at
6
+ end