familia 2.0.0.pre13 → 2.0.0.pre15

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rst +29 -7
  3. data/Gemfile.lock +1 -1
  4. data/README.md +21 -2
  5. data/docs/guides/Feature-System-Autoloading.md +3 -33
  6. data/docs/guides/time-utilities.md +4 -4
  7. data/docs/migrating/v2.0.0-pre11.md +2 -2
  8. data/docs/migrating/v2.0.0-pre13.md +38 -272
  9. data/docs/migrating/v2.0.0-pre14.md +37 -0
  10. data/examples/safe_dump.rb +1 -1
  11. data/lib/familia/base.rb +1 -1
  12. data/lib/familia/data_type.rb +1 -1
  13. data/lib/familia/{autoloader.rb → features/autoloader.rb} +33 -25
  14. data/lib/familia/features/encrypted_fields.rb +2 -2
  15. data/lib/familia/features/expiration/extensions.rb +61 -0
  16. data/lib/familia/features/expiration.rb +5 -62
  17. data/lib/familia/features/external_identifier.rb +6 -4
  18. data/lib/familia/features/object_identifier.rb +2 -1
  19. data/lib/familia/features/quantization.rb +3 -3
  20. data/lib/familia/features/relationships.rb +5 -6
  21. data/lib/familia/features/safe_dump.rb +3 -5
  22. data/lib/familia/features/transient_fields.rb +3 -1
  23. data/lib/familia/features.rb +20 -11
  24. data/lib/familia/field_type.rb +1 -1
  25. data/lib/familia/horreum.rb +1 -1
  26. data/lib/familia/refinements/{time_utils.rb → time_literals.rb} +35 -4
  27. data/lib/familia/refinements.rb +1 -1
  28. data/lib/familia/utils.rb +1 -1
  29. data/lib/familia/version.rb +1 -1
  30. data/lib/familia.rb +0 -1
  31. data/try/core/autoloader_try.rb +9 -9
  32. data/try/core/extensions_try.rb +1 -1
  33. data/try/core/time_utils_try.rb +18 -18
  34. data/try/features/external_identifier/external_identifier_try.rb +26 -0
  35. data/try/features/safe_dump/module_based_extensions_try.rb +100 -0
  36. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -4
  37. data/try/helpers/test_helpers.rb +6 -6
  38. metadata +6 -5
  39. data/lib/familia/features/autoloadable.rb +0 -113
  40. data/try/features/autoloadable/autoloadable_try.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca101edcf531af301428b4b29bc83f464a84e47339a9d67f1b8a70ead828aa74
4
- data.tar.gz: 56255e7fbc191f8c15b75d5cab0a990b83c3e54fe07362f1b20dbf392edca65d
3
+ metadata.gz: 00416beac08786c1fffd75a0c2578669c6a0d18169824ca1a23e9122018a386b
4
+ data.tar.gz: c38f7bfe985a7d6151abf2ce4b6d7ca32ddb97adf8d069880f7e919662937851
5
5
  SHA512:
6
- metadata.gz: d92e09f275bcf262a73afe443fa5e099fff6c0836502de52d485358ec1f5d5a095878fe69ff9c4d5fcdbe26aa00c404474418cc72e9748b10d783ded2942e1b5
7
- data.tar.gz: 2e22b89f457a6b188b31820e794960a5ad288c406ad7550fc47f48501489394fb891c966f7e147fe2b89b8d2eaa36b736258b75184a0aee5e5aa2aaf63ac6878
6
+ metadata.gz: a3d6a60c86460578567fbd6f33e96c8ae0ee2dbfc80f3ffc6f123dc6fb84c6020efd359601122e63543c0128401d235587e9770df8acbbfa1f55cb908a012e07
7
+ data.tar.gz: 5062f93d7494df05a6d62dade395a0522ca8ab02afd4fdc5a00ee21db08b05ace8c3227d07739ef7cdd727fa72888ea4ec415491eb60028b25166d107d574e8e
data/CHANGELOG.rst CHANGED
@@ -12,6 +12,28 @@ Versioning <https://semver.org/spec/v2.0.0.html>`__.
12
12
 
13
13
  <!--scriv-insert-here-->
14
14
 
15
+ .. _changelog-2.0.0.pre14:
16
+
17
+ 2.0.0.pre14 — 2025-09-08
18
+ ========================
19
+
20
+ Changed
21
+ -------
22
+
23
+ - **BREAKING CHANGE**: Renamed ``Familia::Refinements::TimeUtils`` to ``Familia::Refinements::TimeLiterals`` to better reflect the module's primary purpose of enabling numeric and string values to be treated as time unit literals (e.g., ``5.minutes``, ``"30m".in_seconds``). Functionality remains the same - only the module name has changed. Users must update their refinement usage from ``using Familia::Refinements::TimeUtils`` to ``using Familia::Refinements::TimeLiterals``.
24
+
25
+ Fixed
26
+ -----
27
+
28
+ - Fixed ExternalIdentifier HashKey method calls by replacing incorrect ``.del()`` calls with ``.remove_field()`` in three critical locations: extid setter (cleanup old mapping when changing value), find_by_extid (cleanup orphaned mapping when object not found), and destroy! (cleanup mapping when object is destroyed). Added comprehensive test coverage for all scenarios to prevent regression. PR #100
29
+
30
+ AI Assistance
31
+ -------------
32
+
33
+ - Claude Code helped rename TimeUtils to TimeLiterals throughout the codebase, including module name, file path, all usage references, and updating existing documentation.
34
+ - Gemini 2.5 Flash wrote the inline docs for TimeLiterals based on a discussion re: naming rationale.
35
+ - Claude Code fixed the ExternalIdentifier HashKey method bug, replacing incorrect ``.del()`` calls with proper ``.remove_field()`` calls, and implemented test coverage for the affected scenarios.
36
+
15
37
  .. _changelog-2.0.0.pre13:
16
38
 
17
39
  2.0.0.pre13 — 2025-09-07
@@ -20,13 +42,13 @@ Versioning <https://semver.org/spec/v2.0.0.html>`__.
20
42
  Added
21
43
  -----
22
44
 
23
- - **Feature-specific autoloading**: Features can now automatically discover and load extension files from your project directories. When you include a feature like ``safe_dump``, Familia searches for configuration files using conventional patterns like ``{model_name}/{feature_name}_*.rb``, enabling clean separation between core model definitions and feature-specific configurations.
45
+ - **Feature Autoloading System**: Features can now automatically discover and load extension files from your project directories. When you include a feature like ``safe_dump``, Familia searches for configuration files using conventional patterns like ``{model_name}/{feature_name}_*.rb``, enabling clean separation between core model definitions and feature-specific configurations. See ``docs/migrating/v2.0.0-pre13.md`` for migration details.
24
46
 
25
- - **Consolidated autoloader architecture**: Introduced ``Familia::Autoloader`` as a shared utility for consistent file loading patterns across the framework, supporting both general-purpose and feature-specific autoloading scenarios.
47
+ - **Consolidated autoloader architecture**: Introduced ``Familia::Features::Autoloader`` as a shared utility for consistent file loading patterns across the framework, supporting both general-purpose and feature-specific autoloading scenarios.
26
48
 
27
49
  - Added ``PER_MONTH`` constant (2,629,746 seconds = 30.437 days) derived from Gregorian year for consistent month calculations.
28
50
  - Added ``months``, ``month``, and ``in_months`` conversion methods to Numeric refinement.
29
- - Added month unit mappings (``'mo'``, ``'month'``, ``'months'``) to TimeUtils ``UNIT_METHODS`` hash.
51
+ - Added month unit mappings (``'mo'``, ``'month'``, ``'months'``) to TimeLiterals ``UNIT_METHODS`` hash.
30
52
 
31
53
  - **Error Handling**: Added ``NotSupportedError`` for invalid serialization mode combinations in encryption subsystem. PR #97
32
54
 
@@ -34,7 +56,7 @@ Changed
34
56
  -------
35
57
 
36
58
  - Refactored time and numeric extensions from global monkey patches to proper Ruby refinements for better encapsulation and reduced global namespace pollution
37
- - Updated all internal classes to use refinements via ``using Familia::Refinements::TimeUtils`` statements
59
+ - Updated all internal classes to use refinements via ``using Familia::Refinements::TimeLiterals`` statements
38
60
  - Added centralized ``RefinedContext`` module in test helpers to support refinement testing in tryouts files
39
61
 
40
62
  - Updated ``PER_YEAR`` constant to use Gregorian year (31,556,952 seconds = 365.2425 days) for calendar consistency.
@@ -49,7 +71,7 @@ Fixed
49
71
  - Fixed byte conversion logic in ``to_bytes`` method to correctly handle exact 1024-byte boundaries (``size >= 1024`` instead of ``size > 1024``)
50
72
  - Resolved refinement testing issues in tryouts by implementing ``eval``-based code execution within refined contexts
51
73
 
52
- - Fixed TimeUtils refinement ``months_old`` and ``years_old`` methods returning incorrect values (raw seconds instead of months/years). The underlying ``age_in`` method now properly handles ``:months`` and ``:years`` units. Issue #94.
74
+ - Fixed TimeLiterals refinement ``months_old`` and ``years_old`` methods returning incorrect values (raw seconds instead of months/years). The underlying ``age_in`` method now properly handles ``:months`` and ``:years`` units. Issue #94.
53
75
  - Fixed calendar consistency issue where ``12.months != 1.year`` by updating ``PER_YEAR`` to use Gregorian year (365.2425 days) and defining ``PER_MONTH`` as ``PER_YEAR / 12``.
54
76
 
55
77
  Security
@@ -72,7 +94,7 @@ AI Assistance
72
94
 
73
95
  - Significant AI assistance in architectural design and implementation of the feature-specific autoloading system, including pattern matching logic, Ruby introspection methods, and comprehensive debugging of edge cases and thread safety considerations.
74
96
 
75
- - Claude Code assisted with implementing the fix for broken ``months_old`` and ``years_old`` methods in the TimeUtils refinement, including analysis, implementation, testing, and documentation.
97
+ - Claude Code assisted with implementing the fix for broken ``months_old`` and ``years_old`` methods in the TimeLiterals refinement, including analysis, implementation, testing, and documentation.
76
98
 
77
99
  - Performance optimization research and OJ gem integration strategy, including compatibility analysis and testing approach for seamless stdlib JSON replacement. PR #97
78
100
 
@@ -161,7 +183,7 @@ Added
161
183
  with ancestry chain traversal for model-specific feature
162
184
  registration. This enables better organization, standardized naming,
163
185
  and automatic loading of project-specific features via the new
164
- ``Familia::Autoloader`` module.
186
+ ``Familia::Features::Autoloader`` module.
165
187
  - **Improved SafeDump DSL**: Replaced the internal
166
188
  ``@safe_dump_fields`` implementation with a cleaner, more robust DSL
167
189
  using ``safe_dump_field`` and ``safe_dump_fields`` methods.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- familia (2.0.0.pre13)
4
+ familia (2.0.0.pre15)
5
5
  benchmark (~> 0.4)
6
6
  connection_pool (~> 2.5)
7
7
  csv (~> 3.3)
data/README.md CHANGED
@@ -170,7 +170,26 @@ All relationships support automatic indexing and tracking - objects are automati
170
170
 
171
171
  ## Organizing Complex Models
172
172
 
173
- For large applications, you can organize model complexity using custom features:
173
+ For large applications, you can organize model complexity using custom features and the Feature Autoloading System:
174
+
175
+ ### Feature Autoloading System
176
+
177
+ Familia automatically discovers and loads feature-specific configuration files, enabling clean separation between core model definitions and feature configurations:
178
+
179
+ ```ruby
180
+ # app/models/user.rb - Clean model definition
181
+ class User < Familia::Horreum
182
+ field :name, :email, :password
183
+ feature :safe_dump # Configuration auto-loaded
184
+ end
185
+
186
+ # app/models/user/safe_dump_extensions.rb - Automatically discovered
187
+ class User
188
+ safe_dump_fields :name, :email # password excluded for security
189
+ end
190
+ ```
191
+
192
+ Extension files follow the pattern: `{model_name}/{feature_name}_*.rb`
174
193
 
175
194
  ### Self-Registering Features
176
195
 
@@ -201,7 +220,7 @@ class Customer < Familia::Horreum
201
220
  end
202
221
  ```
203
222
 
204
- This keeps complex models organized while maintaining Familia's clean, declarative style.
223
+ These approaches keep complex models organized while maintaining Familia's clean, declarative style. For detailed migration information, see the [migration guides](docs/migrating/).
205
224
 
206
225
  ## AI Development Assistance
207
226
 
@@ -44,7 +44,7 @@ end
44
44
 
45
45
  The `post_inclusion_autoload` system works in **two phases**:
46
46
 
47
- ### Phase 1: Feature System Hook
47
+ ### Feature System Hook
48
48
 
49
49
  In `lib/familia/features.rb`, after including the feature module:
50
50
 
@@ -61,29 +61,6 @@ def feature(feature_name, **options)
61
61
  end
62
62
  ```
63
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
64
  3. **Loads any matching files** found in those locations
88
65
 
89
66
  ## Why This Timing Matters
@@ -199,17 +176,10 @@ The `post_inclusion_autoload` system provides a clean, automatic, and safe way t
199
176
 
200
177
  ## Implementation Details
201
178
 
202
- ### Autoloadable Module
179
+ ### Autoloader
203
180
 
204
- Features that support autoloading include the `Familia::Features::Autoloadable` module:
181
+ Looks for features files in models/features.rb, models/features/, models/model_name/features.rb, models/model_name/features/
205
182
 
206
- ```ruby
207
- module Familia::Features::SafeDump
208
- include Familia::Features::Autoloadable # ← Enables autoloading
209
-
210
- # Feature implementation...
211
- end
212
- ```
213
183
 
214
184
  ### Anonymous Class Handling
215
185
 
@@ -4,10 +4,10 @@ Familia provides a comprehensive time utilities refinement that adds convenient
4
4
 
5
5
  ## Overview
6
6
 
7
- The `Familia::Refinements::TimeUtils` module extends Ruby's built-in classes with intuitive time manipulation methods:
7
+ The `Familia::Refinements::TimeLiterals` module extends Ruby's built-in classes with intuitive time manipulation methods:
8
8
 
9
9
  ```ruby
10
- using Familia::Refinements::TimeUtils
10
+ using Familia::Refinements::TimeLiterals
11
11
 
12
12
  2.hours #=> 7200 (seconds)
13
13
  "30m".in_seconds #=> 1800
@@ -40,7 +40,7 @@ This prevents subtle bugs where `12.months` and `1.year` would differ by ~5.8 ho
40
40
  ### Converting Numbers to Time Units
41
41
 
42
42
  ```ruby
43
- using Familia::Refinements::TimeUtils
43
+ using Familia::Refinements::TimeLiterals
44
44
 
45
45
  # Singular and plural forms work identically
46
46
  1.second #=> 1
@@ -140,7 +140,7 @@ timestamp.within?(1.hour) #=> false
140
140
  ### Cache Expiration
141
141
 
142
142
  ```ruby
143
- using Familia::Refinements::TimeUtils
143
+ using Familia::Refinements::TimeLiterals
144
144
 
145
145
  class CacheEntry < Familia::Horreum
146
146
  field :data
@@ -124,7 +124,7 @@ end
124
124
 
125
125
  class Customer < Familia::Horreum
126
126
  module Features
127
- include Familia::Autoloader
127
+ include Familia::Features::Autoloader
128
128
  # Automatically discovers and loads all *.rb files from customer/features/
129
129
  end
130
130
  end
@@ -210,7 +210,7 @@ If you have project-specific features, set up auto-loading:
210
210
  module YourProject
211
211
  class ModelName < Familia::Horreum
212
212
  module Features
213
- include Familia::Autoloader
213
+ include Familia::Features::Autoloader
214
214
  end
215
215
  end
216
216
  end
@@ -1,320 +1,86 @@
1
1
  # Migrating Guide: v2.0.0-pre13
2
2
 
3
- This version introduces significant improvements to Familia's feature system, making it easier to organize and use features across complex projects through automatic discovery and loading of feature-specific configuration files.
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
4
 
5
- ## Feature-Specific Autoloading System
5
+ ## Feature Autoloading System
6
6
 
7
- ### Overview
7
+ ### What Changed
8
8
 
9
- The new autoloading system allows features to automatically discover and load extension files from your project directories. When you include a feature in your model, Familia now searches for configuration files using conventional patterns, enabling clean separation between core model definitions and feature-specific configurations.
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
10
 
11
- ### Basic Usage
11
+ ### Basic Migration
12
12
 
13
- #### Before (Manual Configuration)
13
+ #### Before
14
14
  ```ruby
15
15
  # app/models/user.rb
16
16
  class User < Familia::Horreum
17
17
  field :name, :email, :password
18
18
 
19
19
  feature :safe_dump
20
- # All configuration had to be done in the same file
21
- safe_dump_fields :name, :email # password excluded for security
20
+ safe_dump_fields :name, :email # Configuration mixed with model
22
21
  end
23
22
  ```
24
23
 
25
- #### After (Automatic Discovery)
24
+ #### After
26
25
  ```ruby
27
26
  # app/models/user.rb - Clean model definition
28
27
  class User < Familia::Horreum
29
28
  field :name, :email, :password
30
- feature :safe_dump # Triggers autoloading
29
+ feature :safe_dump # Configuration auto-loaded
31
30
  end
32
31
 
33
- # app/models/user/safe_dump_extensions.rb - Automatically loaded
32
+ # app/models/user/safe_dump_extensions.rb - Automatically discovered
34
33
  class User
35
- safe_dump_fields :name, :email # password excluded for security
34
+ safe_dump_fields :name, :email
36
35
  end
37
36
  ```
38
37
 
39
- ### File Naming Conventions
40
-
41
- The autoloading system follows these patterns for discovering extension files:
38
+ ### File Naming Convention
42
39
 
43
- #### Pattern: `{model_name}/{feature_name}_*.rb`
40
+ Extension files follow the pattern: `{model_name}/{feature_name}_*.rb`
44
41
 
45
- **Example Structures:**
46
42
  ```
47
43
  app/models/
48
- ├── user.rb # Main model
44
+ ├── user.rb
49
45
  ├── user/
50
- │ ├── safe_dump_extensions.rb # SafeDump configuration
51
- ├── safe_dump_custom.rb # Additional SafeDump setup
52
- │ └── relationships_config.rb # Relationships configuration
53
- ├── product.rb # Another model
54
- └── product/
55
- ├── safe_dump_fields.rb # Product's SafeDump config
56
- └── expiration_settings.rb # Expiration configuration
57
- ```
58
-
59
- #### Supported Patterns
60
- - `safe_dump_extensions.rb`
61
- - `safe_dump_*.rb` (any filename starting with the feature name)
62
- - `expiration_config.rb`
63
- - `relationships_setup.rb`
64
-
65
- ### Advanced Configuration Examples
66
-
67
- #### Complex SafeDump Setup
68
- ```ruby
69
- # app/models/customer.rb
70
- class Customer < Familia::Horreum
71
- field :first_name, :last_name, :email, :phone
72
- field :credit_card_number, :ssn, :internal_notes
73
-
74
- feature :safe_dump
75
- end
76
-
77
- # app/models/customer/safe_dump_configuration.rb
78
- class Customer
79
- # Define which fields are safe for API responses
80
- safe_dump_fields :first_name, :last_name, :email
81
-
82
- # Custom serialization for specific fields
83
- def safe_dump_email
84
- email&.downcase
85
- end
86
-
87
- # Computed fields for API
88
- def safe_dump_full_name
89
- "#{first_name} #{last_name}".strip
90
- end
91
- end
92
- ```
93
-
94
- #### Multi-Feature Organization
95
- ```ruby
96
- # app/models/session.rb
97
- class Session < Familia::Horreum
98
- field :user_id, :token, :ip_address, :user_agent
99
-
100
- feature :safe_dump
101
- feature :expiration
102
- end
103
-
104
- # app/models/session/safe_dump_api.rb
105
- class Session
106
- safe_dump_fields :user_id, :ip_address
107
- # token excluded for security
108
- end
109
-
110
- # app/models/session/expiration_policy.rb
111
- class Session
112
- # Sessions expire after 24 hours
113
- def self.default_ttl
114
- 24 * 60 * 60
115
- end
116
-
117
- # Cascade expiration to related data
118
- cascade_expiration_to :user_sessions
119
- end
120
- ```
121
-
122
- ### Consolidated Autoloader Architecture
123
-
124
- #### Familia::Autoloader
125
-
126
- The new `Familia::Autoloader` class provides a shared utility for consistent file loading patterns across the framework:
127
-
128
- ```ruby
129
- # Internal usage example (you typically won't call this directly)
130
- autoloader = Familia::Autoloader.new(
131
- base_class: User,
132
- feature_name: :safe_dump,
133
- search_patterns: ['user/safe_dump_*.rb']
134
- )
135
-
136
- # Discovers and loads matching files
137
- autoloader.discover_and_load_extensions
138
- ```
139
-
140
- #### Autoloading Strategies
141
-
142
- The autoloader supports multiple discovery strategies:
143
-
144
- 1. **Directory-based**: Search in conventional directories
145
- 2. **Pattern-based**: Use glob patterns for flexible matching
146
- 3. **Explicit paths**: Load specific files when found
147
-
148
- ### How Autoloading Works
149
-
150
- #### Discovery Process
151
-
152
- 1. **Feature Activation**: When `feature :safe_dump` is called
153
- 2. **Path Resolution**: Autoloader determines search paths based on model location
154
- 3. **Pattern Matching**: Searches for files matching `{model_name}/{feature_name}_*.rb`
155
- 4. **File Loading**: Loads discovered files in alphabetical order
156
- 5. **Extension Application**: Feature-specific methods become available
157
-
158
- #### Search Locations
159
-
160
- The autoloader searches in these locations (in order):
161
-
162
- ```ruby
163
- # If your model is in app/models/user.rb, it searches:
164
- [
165
- 'app/models/user/', # Same directory as model
166
- 'lib/user/', # Lib directory
167
- 'config/models/user/', # Config directory
168
- './user/' # Current directory
169
- ]
46
+ │ ├── safe_dump_extensions.rb # SafeDump configuration
47
+ └── expiration_config.rb # Expiration settings
170
48
  ```
171
49
 
172
50
  ### Migration Steps
173
51
 
174
- #### 1. Reorganize Existing Models
175
-
176
- Move feature-specific configuration to separate files:
177
-
178
- ```bash
179
- # Create directories for feature configurations
180
- mkdir -p app/models/user
181
- mkdir -p app/models/product
182
- mkdir -p app/models/order
183
-
184
- # Move configurations
185
- # From app/models/user.rb, extract safe_dump configuration to:
186
- # app/models/user/safe_dump_extensions.rb
187
- ```
188
-
189
- #### 2. Update Model Files
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
190
55
 
191
- Clean up your main model files:
56
+ ### Debugging
192
57
 
58
+ Enable debug output to troubleshoot autoloading:
193
59
  ```ruby
194
- # Before: Everything in one file
195
- class User < Familia::Horreum
196
- field :name, :email, :password, :role
197
-
198
- feature :safe_dump
199
- safe_dump_fields :name, :email
200
-
201
- feature :expiration
202
- def self.default_ttl
203
- 86400
204
- end
205
- end
206
-
207
- # After: Clean separation
208
- class User < Familia::Horreum
209
- field :name, :email, :password, :role
210
-
211
- feature :safe_dump # Configuration auto-loaded
212
- feature :expiration # Configuration auto-loaded
213
- end
214
- ```
215
-
216
- #### 3. Create Extension Files
217
-
218
- Set up your feature-specific configurations:
219
-
220
- ```ruby
221
- # app/models/user/safe_dump_extensions.rb
222
- class User
223
- safe_dump_fields :name, :email
224
- # password and role excluded for security
225
- end
226
-
227
- # app/models/user/expiration_config.rb
228
- class User
229
- def self.default_ttl
230
- 86400 # 24 hours
231
- end
232
- end
60
+ ENV['FAMILIA_DEBUG'] = '1' # Shows discovered and loaded files
233
61
  ```
234
62
 
235
- ### Benefits of the New System
63
+ Common issues:
64
+ - Files must follow `{feature_name}_*.rb` naming pattern
65
+ - Extension files should reopen the same class as your model
236
66
 
237
- #### Code Organization
238
- - **Separation of Concerns**: Feature logic separated from core model definition
239
- - **Maintainability**: Easier to find and modify feature-specific code
240
- - **Readability**: Core models are cleaner and more focused
67
+ ## Architecture
241
68
 
242
- #### Team Development
243
- - **Reduced Conflicts**: Multiple developers can work on different features without merge conflicts
244
- - **Feature Ownership**: Clear boundaries for feature-specific code
245
- - **Testing**: Easier to test features in isolation
69
+ The Feature Autoloading System consists of two key components:
246
70
 
247
- #### Scalability
248
- - **Large Models**: Complex models remain manageable
249
- - **Feature Growth**: New features can be added without bloating main model files
250
- - **Refactoring**: Easier to extract and reorganize feature code
71
+ ### Familia::Features::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
251
76
 
252
- ### Debugging Autoloading
253
-
254
- #### Enable Debug Output
255
-
256
- ```ruby
257
- # In your application initialization
258
- ENV['FAMILIA_DEBUG'] = '1'
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
259
82
 
260
- # Or programmatically
261
- Familia::Features::Autoloadable.debug = true
262
- ```
263
-
264
- #### Debug Output Example
265
- ```
266
- [Familia::Autoloader] Searching for User safe_dump extensions...
267
- [Familia::Autoloader] Found: app/models/user/safe_dump_extensions.rb
268
- [Familia::Autoloader] Loading: app/models/user/safe_dump_extensions.rb
269
- [Familia::Autoloader] SafeDump extension loaded successfully for User
270
- ```
271
-
272
- #### Troubleshooting
273
-
274
- **Common Issues:**
275
-
276
- 1. **File Not Found**: Ensure file naming follows the `{feature_name}_*.rb` pattern
277
- 2. **Load Order**: Files are loaded alphabetically; prefix with numbers if order matters
278
- 3. **Class Scope**: Extension files should reopen the same class as your model
279
-
280
- **Validation:**
281
- ```ruby
282
- # Check if autoloading worked
283
- User.respond_to?(:safe_dump_field_names) # => true
284
- User.safe_dump_field_names # => [:name, :email]
285
- ```
286
-
287
- ### Backward Compatibility
288
-
289
- The autoloading system is fully backward compatible:
290
-
291
- - **Existing Models**: Continue to work without changes
292
- - **Manual Configuration**: Still supported alongside autoloading
293
- - **Gradual Migration**: You can migrate models one at a time
294
-
295
- ### Performance Considerations
296
-
297
- - **Load Time**: Files are loaded once during feature activation
298
- - **Memory Usage**: No additional memory overhead after loading
299
- - **Caching**: Discovered files are cached to avoid repeated filesystem scans
300
-
301
- ### Testing Your Migration
302
-
303
- ```ruby
304
- # Test that autoloading is working
305
- class TestUser < Familia::Horreum
306
- field :name, :email
307
- feature :safe_dump
308
- end
309
-
310
- # Verify extension methods are available
311
- puts TestUser.respond_to?(:safe_dump_field_names)
312
- puts TestUser.safe_dump_field_names.inspect
313
-
314
- # Test instance methods
315
- user = TestUser.new(name: "John", email: "john@example.com")
316
- puts user.safe_dump.inspect
317
- ```
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.
318
84
 
319
85
  ## New Capabilities
320
86
 
@@ -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.
@@ -273,7 +273,7 @@ puts "LegacyModel fields after set_safe_dump_fields: #{LegacyModel.safe_dump_fie
273
273
  puts
274
274
  puts '=== Cleaning up test data ==='
275
275
  [User, Product, Order, Address, Customer, LegacyModel].each do |klass|
276
- klass.redis.del(klass.redis.keys("#{klass.name.downcase}:*"))
276
+ klass.dbclient.del(klass.dbclient.keys("#{klass.name.downcase}:*"))
277
277
  rescue StandardError => e
278
278
  puts "Error cleaning #{klass}: #{e.message}"
279
279
  end
data/lib/familia/base.rb CHANGED
@@ -15,7 +15,7 @@ module Familia
15
15
  #
16
16
  module Base
17
17
 
18
- using Familia::Refinements::TimeUtils
18
+ using Familia::Refinements::TimeLiterals
19
19
 
20
20
  @features_available = nil
21
21
  @feature_definitions = nil
@@ -16,7 +16,7 @@ module Familia
16
16
  include Familia::Base
17
17
  extend Familia::Features
18
18
 
19
- using Familia::Refinements::TimeUtils
19
+ using Familia::Refinements::TimeLiterals
20
20
 
21
21
  @registered_types = {}
22
22
  @valid_options = %i[class parent default_expiration default logical_database dbkey dbclient suffix prefix]