familia 2.0.0.pre5 → 2.0.0.pre6

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +8 -5
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +4 -3
  5. data/docs/wiki/API-Reference.md +95 -18
  6. data/docs/wiki/Connection-Pooling-Guide.md +437 -0
  7. data/docs/wiki/Encrypted-Fields-Overview.md +40 -3
  8. data/docs/wiki/Expiration-Feature-Guide.md +596 -0
  9. data/docs/wiki/Feature-System-Guide.md +600 -0
  10. data/docs/wiki/Features-System-Developer-Guide.md +892 -0
  11. data/docs/wiki/Field-System-Guide.md +784 -0
  12. data/docs/wiki/Home.md +72 -15
  13. data/docs/wiki/Implementation-Guide.md +126 -33
  14. data/docs/wiki/Quantization-Feature-Guide.md +721 -0
  15. data/docs/wiki/RelatableObjects-Guide.md +563 -0
  16. data/docs/wiki/Security-Model.md +65 -25
  17. data/docs/wiki/Transient-Fields-Guide.md +280 -0
  18. data/lib/familia/base.rb +1 -1
  19. data/lib/familia/data_type/types/counter.rb +38 -0
  20. data/lib/familia/data_type/types/hashkey.rb +18 -0
  21. data/lib/familia/data_type/types/lock.rb +43 -0
  22. data/lib/familia/data_type/types/string.rb +9 -2
  23. data/lib/familia/data_type.rb +2 -2
  24. data/lib/familia/encryption/encrypted_data.rb +137 -0
  25. data/lib/familia/encryption/manager.rb +21 -4
  26. data/lib/familia/encryption/providers/aes_gcm_provider.rb +20 -0
  27. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +20 -0
  28. data/lib/familia/encryption.rb +1 -1
  29. data/lib/familia/errors.rb +17 -3
  30. data/lib/familia/features/encrypted_fields/concealed_string.rb +295 -0
  31. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +94 -26
  32. data/lib/familia/features/expiration.rb +1 -1
  33. data/lib/familia/features/quantization.rb +1 -1
  34. data/lib/familia/features/safe_dump.rb +1 -1
  35. data/lib/familia/features/transient_fields/redacted_string.rb +1 -1
  36. data/lib/familia/features/transient_fields.rb +1 -1
  37. data/lib/familia/field_type.rb +5 -2
  38. data/lib/familia/horreum/{connection.rb → core/connection.rb} +2 -8
  39. data/lib/familia/horreum/{database_commands.rb → core/database_commands.rb} +14 -3
  40. data/lib/familia/horreum/core/serialization.rb +535 -0
  41. data/lib/familia/horreum/{utils.rb → core/utils.rb} +0 -2
  42. data/lib/familia/horreum/core.rb +21 -0
  43. data/lib/familia/horreum/{settings.rb → shared/settings.rb} +0 -2
  44. data/lib/familia/horreum/{definition_methods.rb → subclass/definition.rb} +44 -28
  45. data/lib/familia/horreum/{management_methods.rb → subclass/management.rb} +9 -8
  46. data/lib/familia/horreum/{related_fields_management.rb → subclass/related_fields_management.rb} +15 -10
  47. data/lib/familia/horreum.rb +17 -17
  48. data/lib/familia/version.rb +1 -1
  49. data/lib/familia.rb +1 -1
  50. data/try/core/create_method_try.rb +240 -0
  51. data/try/core/database_consistency_try.rb +299 -0
  52. data/try/core/errors_try.rb +25 -4
  53. data/try/core/familia_try.rb +1 -1
  54. data/try/core/persistence_operations_try.rb +297 -0
  55. data/try/data_types/counter_try.rb +93 -0
  56. data/try/data_types/lock_try.rb +133 -0
  57. data/try/debugging/debug_aad_process.rb +82 -0
  58. data/try/debugging/debug_concealed_internal.rb +59 -0
  59. data/try/debugging/debug_concealed_reveal.rb +61 -0
  60. data/try/debugging/debug_context_aad.rb +68 -0
  61. data/try/debugging/debug_context_simple.rb +80 -0
  62. data/try/debugging/debug_cross_context.rb +62 -0
  63. data/try/debugging/debug_database_load.rb +64 -0
  64. data/try/debugging/debug_encrypted_json_check.rb +53 -0
  65. data/try/debugging/debug_encrypted_json_step_by_step.rb +62 -0
  66. data/try/debugging/debug_exists_lifecycle.rb +54 -0
  67. data/try/debugging/debug_field_decrypt.rb +74 -0
  68. data/try/debugging/debug_fresh_cross_context.rb +73 -0
  69. data/try/debugging/debug_load_path.rb +66 -0
  70. data/try/debugging/debug_method_definition.rb +46 -0
  71. data/try/debugging/debug_method_resolution.rb +41 -0
  72. data/try/debugging/debug_minimal.rb +24 -0
  73. data/try/debugging/debug_provider.rb +68 -0
  74. data/try/debugging/debug_secure_behavior.rb +73 -0
  75. data/try/debugging/debug_string_class.rb +46 -0
  76. data/try/debugging/debug_test.rb +46 -0
  77. data/try/debugging/debug_test_design.rb +80 -0
  78. data/try/encryption/encryption_core_try.rb +3 -3
  79. data/try/features/encrypted_fields_core_try.rb +19 -11
  80. data/try/features/encrypted_fields_integration_try.rb +66 -70
  81. data/try/features/encrypted_fields_no_cache_security_try.rb +22 -8
  82. data/try/features/encrypted_fields_security_try.rb +151 -144
  83. data/try/features/encryption_fields/aad_protection_try.rb +108 -23
  84. data/try/features/encryption_fields/concealed_string_core_try.rb +250 -0
  85. data/try/features/encryption_fields/context_isolation_try.rb +29 -8
  86. data/try/features/encryption_fields/error_conditions_try.rb +6 -6
  87. data/try/features/encryption_fields/fresh_key_derivation_try.rb +20 -14
  88. data/try/features/encryption_fields/fresh_key_try.rb +27 -22
  89. data/try/features/encryption_fields/key_rotation_try.rb +16 -10
  90. data/try/features/encryption_fields/nonce_uniqueness_try.rb +15 -13
  91. data/try/features/encryption_fields/secure_by_default_behavior_try.rb +310 -0
  92. data/try/features/encryption_fields/thread_safety_try.rb +6 -6
  93. data/try/features/encryption_fields/universal_serialization_safety_try.rb +174 -0
  94. data/try/features/feature_dependencies_try.rb +3 -3
  95. data/try/features/transient_fields_core_try.rb +1 -1
  96. data/try/features/transient_fields_integration_try.rb +1 -1
  97. data/try/helpers/test_helpers.rb +25 -0
  98. data/try/horreum/enhanced_conflict_handling_try.rb +1 -1
  99. data/try/horreum/initialization_try.rb +1 -1
  100. data/try/horreum/relations_try.rb +1 -1
  101. data/try/horreum/serialization_persistent_fields_try.rb +8 -8
  102. data/try/horreum/serialization_try.rb +39 -4
  103. data/try/models/customer_safe_dump_try.rb +1 -1
  104. data/try/models/customer_try.rb +1 -1
  105. metadata +51 -10
  106. data/TEST_COVERAGE.md +0 -40
  107. data/lib/familia/horreum/serialization.rb +0 -473
@@ -0,0 +1,892 @@
1
+ # Features System Developer Guide
2
+
3
+ ## Overview
4
+
5
+ This developer guide covers the internal architecture of Familia's feature system, including feature registration, dependency resolution, loading mechanisms, and best practices for creating robust, maintainable features.
6
+
7
+ ## Architecture Deep Dive
8
+
9
+ ### Core Components
10
+
11
+ #### 1. Feature Registration (`Familia::Base`)
12
+
13
+ ```ruby
14
+ # lib/familia/base.rb
15
+ module Familia::Base
16
+ @features_available = {} # Registry of available features
17
+ @feature_definitions = {} # Feature metadata and dependencies
18
+
19
+ def self.add_feature(klass, feature_name, depends_on: [])
20
+ @features_available ||= {}
21
+
22
+ # Create feature definition with metadata
23
+ feature_def = FeatureDefinition.new(
24
+ name: feature_name,
25
+ depends_on: depends_on,
26
+ )
27
+
28
+ @feature_definitions ||= {}
29
+ @feature_definitions[feature_name] = feature_def
30
+ features_available[feature_name] = klass
31
+ end
32
+ end
33
+ ```
34
+
35
+ #### 2. Feature Activation (Horreum Classes)
36
+
37
+ ```ruby
38
+ # When a class declares `feature :name`
39
+ module Familia::Horreum::ClassMethods
40
+ def feature(name)
41
+ # 1. Validate feature exists
42
+ feature_klass = Familia::Base.features_available[name]
43
+ raise Familia::Problem, "Unknown feature: #{name}" unless feature_klass
44
+
45
+ # 2. Check dependencies
46
+ validate_feature_dependencies(name)
47
+
48
+ # 3. Include the feature module
49
+ include feature_klass
50
+
51
+ # 4. Track enabled features
52
+ @features_enabled ||= Set.new
53
+ @features_enabled.add(name)
54
+ end
55
+
56
+ private
57
+
58
+ def validate_feature_dependencies(feature_name)
59
+ feature_def = Familia::Base.feature_definitions[feature_name]
60
+ return unless feature_def&.depends_on&.any?
61
+
62
+ missing_deps = feature_def.depends_on - features_enabled.to_a
63
+ if missing_deps.any?
64
+ raise Familia::Problem,
65
+ "Feature #{feature_name} requires: #{missing_deps.join(', ')}"
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ #### 3. Feature Definition Structure
72
+
73
+ ```ruby
74
+ class FeatureDefinition
75
+ attr_reader :name, :depends_on, :conflicts_with, :provides
76
+
77
+ def initialize(name:, depends_on: [], conflicts_with: [], provides: [])
78
+ @name = name.to_sym
79
+ @depends_on = Array(depends_on).map(&:to_sym)
80
+ @conflicts_with = Array(conflicts_with).map(&:to_sym)
81
+ @provides = Array(provides).map(&:to_sym)
82
+ end
83
+
84
+ def compatible_with?(other_feature)
85
+ !conflicts_with.include?(other_feature.name)
86
+ end
87
+
88
+ def dependencies_satisfied?(enabled_features)
89
+ depends_on.all? { |dep| enabled_features.include?(dep) }
90
+ end
91
+ end
92
+ ```
93
+
94
+ ### Feature Loading Lifecycle
95
+
96
+ #### 1. Automatic Discovery
97
+
98
+ ```ruby
99
+ # lib/familia/features.rb - loads all features automatically
100
+ features_dir = File.join(__dir__, 'features')
101
+ Dir.glob(File.join(features_dir, '*.rb')).sort.each do |feature_file|
102
+ begin
103
+ require_relative feature_file
104
+ rescue LoadError => e
105
+ Familia.logger.warn "Failed to load feature #{feature_file}: #{e.message}"
106
+ end
107
+ end
108
+ ```
109
+
110
+ #### 2. Feature Self-Registration
111
+
112
+ ```ruby
113
+ # Each feature registers itself when loaded
114
+ module Familia::Features::MyFeature
115
+ def self.included(base)
116
+ base.extend ClassMethods
117
+ base.prepend InstanceMethods
118
+ end
119
+
120
+ module ClassMethods
121
+ # Class-level functionality
122
+ end
123
+
124
+ module InstanceMethods
125
+ # Instance-level functionality
126
+ end
127
+
128
+ # Self-registration at module definition time
129
+ Familia::Base.add_feature self, :my_feature, depends_on: [:other_feature]
130
+ end
131
+ ```
132
+
133
+ #### 3. Runtime Inclusion
134
+
135
+ ```ruby
136
+ # When a class declares a feature
137
+ class MyModel < Familia::Horreum
138
+ feature :expiration # 1. Validation and dependency check
139
+ feature :encrypted_fields # 2. Module inclusion
140
+ feature :safe_dump # 3. Method definition and setup
141
+ end
142
+ ```
143
+
144
+ ## Advanced Feature Patterns
145
+
146
+ ### Conditional Feature Loading
147
+
148
+ ```ruby
149
+ module Familia::Features::ConditionalFeature
150
+ def self.included(base)
151
+ # Only add functionality if conditions are met
152
+ if defined?(Rails) && Rails.env.production?
153
+ base.extend ProductionMethods
154
+ else
155
+ base.extend DevelopmentMethods
156
+ end
157
+
158
+ # Conditional method definitions based on available libraries
159
+ if defined?(Sidekiq)
160
+ base.include BackgroundJobIntegration
161
+ end
162
+
163
+ if defined?(ActiveRecord)
164
+ base.include ActiveRecordCompatibility
165
+ end
166
+ end
167
+
168
+ module ProductionMethods
169
+ def production_only_method
170
+ # Implementation only available in production
171
+ end
172
+ end
173
+
174
+ module DevelopmentMethods
175
+ def debug_helper_method
176
+ # Development and test helper methods
177
+ end
178
+ end
179
+
180
+ # Register with environment-specific dependencies
181
+ dependencies = []
182
+ dependencies << :logging if defined?(Rails)
183
+ dependencies << :metrics if ENV['ENABLE_METRICS']
184
+
185
+ Familia::Base.add_feature self, :conditional_feature, depends_on: dependencies
186
+ end
187
+ ```
188
+
189
+ ### Feature Conflicts and Compatibility
190
+
191
+ ```ruby
192
+ # Feature that conflicts with others
193
+ module Familia::Features::AlternativeImplementation
194
+ def self.included(base)
195
+ # Check for conflicting features
196
+ conflicting_features = [:original_implementation, :legacy_mode]
197
+ enabled_conflicts = conflicting_features & base.features_enabled.to_a
198
+
199
+ if enabled_conflicts.any?
200
+ raise Familia::Problem,
201
+ "#{self} conflicts with: #{enabled_conflicts.join(', ')}"
202
+ end
203
+
204
+ base.extend ClassMethods
205
+ end
206
+
207
+ module ClassMethods
208
+ def alternative_method
209
+ # Different implementation approach
210
+ end
211
+ end
212
+
213
+ Familia::Base.add_feature self, :alternative_implementation,
214
+ conflicts_with: [:original_implementation, :legacy_mode]
215
+ end
216
+ ```
217
+
218
+ ### Feature Capability Flags
219
+
220
+ ```ruby
221
+ module Familia::Features::CapabilityProvider
222
+ def self.included(base)
223
+ base.extend ClassMethods
224
+
225
+ # Add capability flags to the class
226
+ base.instance_variable_set(:@capabilities, Set.new)
227
+ base.capabilities.merge([:search, :indexing, :full_text])
228
+ end
229
+
230
+ module ClassMethods
231
+ attr_reader :capabilities
232
+
233
+ def has_capability?(capability)
234
+ capabilities.include?(capability.to_sym)
235
+ end
236
+
237
+ def requires_capability(capability)
238
+ unless has_capability?(capability)
239
+ raise Familia::Problem,
240
+ "#{self} requires #{capability} capability"
241
+ end
242
+ end
243
+ end
244
+
245
+ # Feature provides capabilities that other features can depend on
246
+ Familia::Base.add_feature self, :capability_provider, provides: [:search, :indexing]
247
+ end
248
+
249
+ # Feature that requires specific capabilities
250
+ module Familia::Features::SearchDependent
251
+ def self.included(base)
252
+ # Check that required capabilities are available
253
+ base.requires_capability(:search)
254
+ base.requires_capability(:indexing)
255
+
256
+ base.extend ClassMethods
257
+ end
258
+
259
+ module ClassMethods
260
+ def search_by_field(field, query)
261
+ # Implementation that uses search capabilities
262
+ end
263
+ end
264
+
265
+ Familia::Base.add_feature self, :search_dependent,
266
+ depends_on: [:capability_provider]
267
+ end
268
+ ```
269
+
270
+ ### Dynamic Feature Configuration
271
+
272
+ ```ruby
273
+ module Familia::Features::ConfigurableFeature
274
+ def self.included(base)
275
+ base.extend ClassMethods
276
+
277
+ # Initialize configuration
278
+ config = base.feature_config(:configurable_feature)
279
+
280
+ if config[:enable_caching]
281
+ base.include CachingMethods
282
+ end
283
+
284
+ if config[:enable_logging]
285
+ base.include LoggingMethods
286
+ end
287
+
288
+ # Configure behavior based on settings
289
+ base.instance_variable_set(:@batch_size, config[:batch_size] || 100)
290
+ end
291
+
292
+ module ClassMethods
293
+ def feature_config(feature_name)
294
+ @feature_configs ||= {}
295
+ @feature_configs[feature_name] ||= load_feature_config(feature_name)
296
+ end
297
+
298
+ private
299
+
300
+ def load_feature_config(feature_name)
301
+ # Load from various sources
302
+ config = {}
303
+
304
+ # 1. Default configuration
305
+ config.merge!(default_config_for(feature_name))
306
+
307
+ # 2. Environment variables
308
+ env_config = ENV.select { |k, v| k.start_with?("FAMILIA_#{feature_name.upcase}_") }
309
+ env_config.each { |k, v| config[k.split('_').last.downcase.to_sym] = v }
310
+
311
+ # 3. Configuration files
312
+ if defined?(Rails)
313
+ rails_config = Rails.application.config.familia&.features&.dig(feature_name)
314
+ config.merge!(rails_config) if rails_config
315
+ end
316
+
317
+ config
318
+ end
319
+
320
+ def default_config_for(feature_name)
321
+ case feature_name
322
+ when :configurable_feature
323
+ {
324
+ enable_caching: true,
325
+ enable_logging: Rails.env.development?,
326
+ batch_size: 100,
327
+ timeout: 30
328
+ }
329
+ else
330
+ {}
331
+ end
332
+ end
333
+ end
334
+
335
+ module CachingMethods
336
+ def cached_operation(&block)
337
+ # Caching implementation
338
+ end
339
+ end
340
+
341
+ module LoggingMethods
342
+ def log_operation(operation, &block)
343
+ # Logging implementation
344
+ end
345
+ end
346
+
347
+ Familia::Base.add_feature self, :configurable_feature
348
+ end
349
+ ```
350
+
351
+ ## Feature Development Best Practices
352
+
353
+ ### 1. Feature Structure Template
354
+
355
+ ```ruby
356
+ # lib/familia/features/my_feature.rb
357
+ module Familia
358
+ module Features
359
+ module MyFeature
360
+ # Feature metadata
361
+ FEATURE_VERSION = '1.0.0'
362
+ REQUIRED_FAMILIA_VERSION = '>= 2.0.0'
363
+
364
+ def self.included(base)
365
+ # Validation and setup
366
+ validate_environment!(base)
367
+
368
+ Familia.ld "[#{base}] Loading #{self} v#{FEATURE_VERSION}"
369
+
370
+ # Module inclusion
371
+ base.extend ClassMethods
372
+ base.prepend InstanceMethods # Use prepend for method interception
373
+ base.include HelperMethods # Use include for utility methods
374
+
375
+ # Post-inclusion setup
376
+ configure_feature(base)
377
+ end
378
+
379
+ def self.validate_environment!(base)
380
+ # Check Familia version compatibility
381
+ familia_version = Gem::Version.new(Familia::VERSION)
382
+ required_version = Gem::Requirement.new(REQUIRED_FAMILIA_VERSION)
383
+
384
+ unless required_version.satisfied_by?(familia_version)
385
+ raise Familia::Problem,
386
+ "#{self} requires Familia #{REQUIRED_FAMILIA_VERSION}, " \
387
+ "got #{familia_version}"
388
+ end
389
+
390
+ # Check for required methods/capabilities on the base class
391
+ required_methods = [:identifier_field, :field]
392
+ missing_methods = required_methods.reject { |m| base.respond_to?(m) }
393
+
394
+ if missing_methods.any?
395
+ raise Familia::Problem,
396
+ "#{base} missing required methods: #{missing_methods.join(', ')}"
397
+ end
398
+ end
399
+
400
+ def self.configure_feature(base)
401
+ # Feature-specific initialization
402
+ base.instance_variable_set(:@my_feature_config, {
403
+ enabled: true,
404
+ options: {}
405
+ })
406
+
407
+ # Set up feature-specific data structures
408
+ base.class_eval do
409
+ @my_feature_data ||= {}
410
+ end
411
+ end
412
+
413
+ # Class-level methods added to including class
414
+ module ClassMethods
415
+ def my_feature_config
416
+ @my_feature_config ||= { enabled: true, options: {} }
417
+ end
418
+
419
+ def configure_my_feature(**options)
420
+ my_feature_config[:options].merge!(options)
421
+ end
422
+
423
+ def my_feature_enabled?
424
+ my_feature_config[:enabled]
425
+ end
426
+ end
427
+
428
+ # Instance methods that intercept/override existing methods
429
+ module InstanceMethods
430
+ def save
431
+ # Pre-processing
432
+ before_my_feature_save if respond_to?(:before_my_feature_save, true)
433
+
434
+ # Call original save
435
+ result = super
436
+
437
+ # Post-processing
438
+ after_my_feature_save if respond_to?(:after_my_feature_save, true)
439
+
440
+ result
441
+ end
442
+
443
+ private
444
+
445
+ def before_my_feature_save
446
+ # Feature-specific pre-save logic
447
+ end
448
+
449
+ def after_my_feature_save
450
+ # Feature-specific post-save logic
451
+ end
452
+ end
453
+
454
+ # Utility methods that don't override existing functionality
455
+ module HelperMethods
456
+ def my_feature_helper
457
+ return unless self.class.my_feature_enabled?
458
+ # Helper implementation
459
+ end
460
+ end
461
+
462
+ # Register the feature
463
+ Familia::Base.add_feature self, :my_feature, depends_on: [:required_feature]
464
+ end
465
+ end
466
+ end
467
+ ```
468
+
469
+ ### 2. Robust Error Handling
470
+
471
+ ```ruby
472
+ module Familia::Features::RobustFeature
473
+ class FeatureError < Familia::Problem; end
474
+ class ConfigurationError < FeatureError; end
475
+ class DependencyError < FeatureError; end
476
+
477
+ def self.included(base)
478
+ begin
479
+ validate_dependencies!(base)
480
+ configure_feature_safely(base)
481
+ rescue => e
482
+ handle_inclusion_error(base, e)
483
+ end
484
+ end
485
+
486
+ def self.validate_dependencies!(base)
487
+ # Check external dependencies
488
+ unless defined?(SomeGem)
489
+ raise DependencyError, "#{self} requires 'some_gem' gem"
490
+ end
491
+
492
+ # Check feature dependencies
493
+ required_features = [:base_feature]
494
+ missing_features = required_features - base.features_enabled.to_a
495
+
496
+ if missing_features.any?
497
+ raise DependencyError,
498
+ "#{self} requires features: #{missing_features.join(', ')}"
499
+ end
500
+ end
501
+
502
+ def self.configure_feature_safely(base)
503
+ # Safely configure with fallbacks
504
+ config = load_configuration
505
+ apply_configuration(base, config)
506
+ rescue => e
507
+ Familia.logger.warn "Feature configuration failed: #{e.message}"
508
+ apply_default_configuration(base)
509
+ end
510
+
511
+ def self.handle_inclusion_error(base, error)
512
+ case error
513
+ when DependencyError
514
+ # Log dependency issues and disable feature
515
+ Familia.logger.error "Feature #{self} disabled: #{error.message}"
516
+ base.instance_variable_set(:@robust_feature_disabled, true)
517
+ when ConfigurationError
518
+ # Try default configuration
519
+ Familia.logger.warn "Using default configuration: #{error.message}"
520
+ apply_default_configuration(base)
521
+ else
522
+ # Re-raise unexpected errors
523
+ raise
524
+ end
525
+ end
526
+
527
+ module ClassMethods
528
+ def robust_feature_enabled?
529
+ !@robust_feature_disabled
530
+ end
531
+
532
+ def with_robust_feature(&block)
533
+ return unless robust_feature_enabled?
534
+ block.call
535
+ rescue => e
536
+ Familia.logger.error "Robust feature operation failed: #{e.message}"
537
+ nil
538
+ end
539
+ end
540
+
541
+ Familia::Base.add_feature self, :robust_feature
542
+ end
543
+ ```
544
+
545
+ ### 3. Feature Testing Infrastructure
546
+
547
+ ```ruby
548
+ # Test helpers for feature development
549
+ module FeatureTestHelpers
550
+ def with_feature(feature_name, config = {})
551
+ # Create temporary test class with feature
552
+ test_class = Class.new(Familia::Horreum) do
553
+ def self.name
554
+ 'FeatureTestClass'
555
+ end
556
+
557
+ identifier_field :test_id
558
+ field :test_id
559
+ end
560
+
561
+ # Configure feature if needed
562
+ if config.any?
563
+ test_class.define_singleton_method(:feature_config) do |name|
564
+ config
565
+ end
566
+ end
567
+
568
+ # Enable the feature
569
+ test_class.feature feature_name
570
+
571
+ yield test_class
572
+ end
573
+
574
+ def feature_enabled?(klass, feature_name)
575
+ klass.features_enabled.include?(feature_name)
576
+ end
577
+
578
+ def assert_feature_methods(klass, expected_methods)
579
+ expected_methods.each do |method|
580
+ assert klass.method_defined?(method),
581
+ "Expected #{klass} to have method #{method}"
582
+ end
583
+ end
584
+ end
585
+
586
+ # RSpec helper
587
+ RSpec.configure do |config|
588
+ config.include FeatureTestHelpers
589
+ end
590
+
591
+ # Feature test example
592
+ RSpec.describe Familia::Features::MyFeature do
593
+ it "adds expected methods to class" do
594
+ with_feature(:my_feature) do |test_class|
595
+ expect(test_class).to respond_to(:my_feature_config)
596
+ expect(test_class.new).to respond_to(:my_feature_helper)
597
+ end
598
+ end
599
+
600
+ it "respects feature configuration" do
601
+ config = { enabled: false }
602
+
603
+ with_feature(:my_feature, config) do |test_class|
604
+ expect(test_class.my_feature_enabled?).to be false
605
+ end
606
+ end
607
+
608
+ it "validates dependencies" do
609
+ expect {
610
+ Class.new(Familia::Horreum) do
611
+ feature :my_feature # Missing :required_feature dependency
612
+ end
613
+ }.to raise_error(Familia::Problem, /requires.*required_feature/)
614
+ end
615
+ end
616
+ ```
617
+
618
+ ## Performance Optimization
619
+
620
+ ### 1. Lazy Feature Loading
621
+
622
+ ```ruby
623
+ module Familia::Features::LazyFeature
624
+ def self.included(base)
625
+ # Minimal setup at include time
626
+ base.extend ClassMethods
627
+
628
+ # Defer expensive setup until first use
629
+ @setup_complete = false
630
+ end
631
+
632
+ module ClassMethods
633
+ def ensure_lazy_feature_setup!
634
+ return if @setup_complete
635
+
636
+ # Expensive setup operations
637
+ perform_expensive_setup
638
+ @setup_complete = true
639
+ end
640
+
641
+ def lazy_feature_method
642
+ ensure_lazy_feature_setup!
643
+ # Method implementation
644
+ end
645
+
646
+ private
647
+
648
+ def perform_expensive_setup
649
+ # Heavy initialization work
650
+ @expensive_data = load_expensive_data
651
+ @compiled_templates = compile_templates
652
+ end
653
+ end
654
+
655
+ Familia::Base.add_feature self, :lazy_feature
656
+ end
657
+ ```
658
+
659
+ ### 2. Feature Method Caching
660
+
661
+ ```ruby
662
+ module Familia::Features::CachedFeature
663
+ def self.included(base)
664
+ base.extend ClassMethods
665
+ end
666
+
667
+ module ClassMethods
668
+ def cached_feature_method(key)
669
+ @method_cache ||= {}
670
+ @method_cache[key] ||= expensive_computation(key)
671
+ end
672
+
673
+ def clear_feature_cache!
674
+ @method_cache = {}
675
+ end
676
+
677
+ private
678
+
679
+ def expensive_computation(key)
680
+ # Expensive operation
681
+ sleep 0.1 # Simulate work
682
+ "computed_#{key}"
683
+ end
684
+ end
685
+
686
+ Familia::Base.add_feature self, :cached_feature
687
+ end
688
+ ```
689
+
690
+ ## Debugging Features
691
+
692
+ ### 1. Feature Introspection
693
+
694
+ ```ruby
695
+ module Familia::Features::Introspection
696
+ def self.included(base)
697
+ base.extend ClassMethods
698
+ end
699
+
700
+ module ClassMethods
701
+ def feature_info
702
+ {
703
+ enabled_features: features_enabled.to_a,
704
+ feature_dependencies: feature_dependency_graph,
705
+ feature_conflicts: feature_conflict_map,
706
+ feature_load_order: feature_load_order
707
+ }
708
+ end
709
+
710
+ def feature_dependency_graph
711
+ graph = {}
712
+ features_enabled.each do |feature|
713
+ definition = Familia::Base.feature_definitions[feature]
714
+ graph[feature] = definition&.depends_on || []
715
+ end
716
+ graph
717
+ end
718
+
719
+ def feature_conflict_map
720
+ conflicts = {}
721
+ features_enabled.each do |feature|
722
+ definition = Familia::Base.feature_definitions[feature]
723
+ conflicts[feature] = definition&.conflicts_with || []
724
+ end
725
+ conflicts
726
+ end
727
+
728
+ def feature_load_order
729
+ # Return the order features were loaded
730
+ @feature_load_order ||= []
731
+ end
732
+
733
+ def debug_feature_issues
734
+ issues = []
735
+
736
+ # Check for circular dependencies
737
+ issues.concat(detect_circular_dependencies)
738
+
739
+ # Check for method conflicts
740
+ issues.concat(detect_method_conflicts)
741
+
742
+ # Check for missing dependencies
743
+ issues.concat(detect_missing_dependencies)
744
+
745
+ issues
746
+ end
747
+
748
+ private
749
+
750
+ def detect_circular_dependencies
751
+ # Implementation for circular dependency detection
752
+ end
753
+
754
+ def detect_method_conflicts
755
+ # Implementation for method conflict detection
756
+ end
757
+
758
+ def detect_missing_dependencies
759
+ # Implementation for missing dependency detection
760
+ end
761
+ end
762
+
763
+ Familia::Base.add_feature self, :introspection
764
+ end
765
+ ```
766
+
767
+ ### 2. Feature Debug Logging
768
+
769
+ ```ruby
770
+ module Familia::Features::DebugLogging
771
+ def self.included(base)
772
+ return unless Familia.debug?
773
+
774
+ base.extend ClassMethods
775
+ original_feature_method = base.method(:feature)
776
+
777
+ base.define_singleton_method(:feature) do |name|
778
+ Familia.ld "[DEBUG] Loading feature #{name} on #{self}"
779
+ start_time = Time.now
780
+
781
+ result = original_feature_method.call(name)
782
+
783
+ load_time = (Time.now - start_time) * 1000
784
+ Familia.ld "[DEBUG] Feature #{name} loaded in #{load_time.round(2)}ms"
785
+
786
+ result
787
+ end
788
+ end
789
+
790
+ module ClassMethods
791
+ def log_feature_method_call(method_name, &block)
792
+ return block.call unless Familia.debug?
793
+
794
+ Familia.ld "[DEBUG] Calling #{method_name} on #{self}"
795
+ start_time = Time.now
796
+
797
+ result = block.call
798
+
799
+ duration = (Time.now - start_time) * 1000
800
+ Familia.ld "[DEBUG] #{method_name} completed in #{duration.round(2)}ms"
801
+
802
+ result
803
+ end
804
+ end
805
+
806
+ Familia::Base.add_feature self, :debug_logging
807
+ end
808
+ ```
809
+
810
+ ## Migration and Versioning
811
+
812
+ ### Feature Versioning
813
+
814
+ ```ruby
815
+ module Familia::Features::VersionedFeature
816
+ VERSION = '2.1.0'
817
+ MIGRATION_PATH = [
818
+ { from: '1.0.0', to: '1.1.0', migration: :migrate_1_0_to_1_1 },
819
+ { from: '1.1.0', to: '2.0.0', migration: :migrate_1_1_to_2_0 },
820
+ { from: '2.0.0', to: '2.1.0', migration: :migrate_2_0_to_2_1 }
821
+ ].freeze
822
+
823
+ def self.included(base)
824
+ check_and_migrate_version(base)
825
+ base.extend ClassMethods
826
+ end
827
+
828
+ def self.check_and_migrate_version(base)
829
+ current_version = get_current_version(base)
830
+ return if current_version == VERSION
831
+
832
+ if current_version.nil?
833
+ # First installation
834
+ set_version(base, VERSION)
835
+ return
836
+ end
837
+
838
+ # Perform migration
839
+ migrate_from_version(base, current_version, VERSION)
840
+ end
841
+
842
+ def self.migrate_from_version(base, from_version, to_version)
843
+ migration_steps = find_migration_path(from_version, to_version)
844
+
845
+ migration_steps.each do |step|
846
+ Familia.logger.info "Migrating #{base} from #{step[:from]} to #{step[:to]}"
847
+ send(step[:migration], base)
848
+ end
849
+
850
+ set_version(base, to_version)
851
+ end
852
+
853
+ def self.find_migration_path(from, to)
854
+ # Find path through migration steps
855
+ current = from
856
+ path = []
857
+
858
+ while current != to
859
+ step = MIGRATION_PATH.find { |s| s[:from] == current }
860
+ break unless step
861
+
862
+ path << step
863
+ current = step[:to]
864
+ end
865
+
866
+ path
867
+ end
868
+
869
+ # Migration methods
870
+ def self.migrate_1_0_to_1_1(base)
871
+ # Migration logic for 1.0 -> 1.1
872
+ end
873
+
874
+ def self.migrate_1_1_to_2_0(base)
875
+ # Migration logic for 1.1 -> 2.0
876
+ end
877
+
878
+ def self.migrate_2_0_to_2_1(base)
879
+ # Migration logic for 2.0 -> 2.1
880
+ end
881
+
882
+ module ClassMethods
883
+ def feature_version
884
+ self.class.instance_variable_get(:@versioned_feature_version) || VERSION
885
+ end
886
+ end
887
+
888
+ Familia::Base.add_feature self, :versioned_feature
889
+ end
890
+ ```
891
+
892
+ This developer guide provides the foundation for creating robust, maintainable features that integrate seamlessly with Familia's architecture while following best practices for error handling, performance, and maintainability.