familia 2.0.0.pre18 → 2.0.0.pre21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/claude-code-review.yml +4 -9
- data/.github/workflows/code-smells.yml +64 -3
- data/.pre-commit-config.yaml +8 -6
- data/.reek.yml +10 -9
- data/.rubocop.yml +4 -0
- data/CHANGELOG.rst +205 -88
- data/CLAUDE.md +62 -10
- data/Gemfile +3 -3
- data/Gemfile.lock +27 -62
- data/README.md +39 -0
- data/bin/try +16 -0
- data/bin/tryouts +16 -0
- data/changelog.d/20251105_flexible_external_identifier_format.rst +66 -0
- data/changelog.d/20251107_112554_delano_179_participation_asymmetry.rst +44 -0
- data/changelog.d/20251107_213121_delano_fix_thread_safety_races_011CUumCP492Twxm4NLt2FvL.rst +20 -0
- data/changelog.d/20251107_fix_participates_in_symbol_resolution.rst +91 -0
- data/changelog.d/20251107_optimized_redis_exists_checks.rst +94 -0
- data/changelog.d/20251108_frozen_string_literal_pragma.rst +44 -0
- data/docs/1106-participates_in-bidirectional-solution.md +129 -0
- data/docs/guides/encryption.md +486 -0
- data/docs/guides/feature-encrypted-fields.md +123 -7
- data/docs/guides/feature-expiration.md +177 -133
- data/docs/guides/feature-external-identifiers.md +415 -443
- data/docs/guides/feature-object-identifiers.md +400 -269
- data/docs/guides/feature-quantization.md +120 -6
- data/docs/guides/feature-relationships-indexing.md +318 -0
- data/docs/guides/feature-relationships-methods.md +146 -604
- data/docs/guides/feature-relationships-participation.md +263 -0
- data/docs/guides/feature-relationships.md +118 -136
- data/docs/guides/feature-system-devs.md +176 -693
- data/docs/guides/feature-system.md +119 -6
- data/docs/guides/feature-transient-fields.md +81 -0
- data/docs/guides/field-system.md +778 -0
- data/docs/guides/index.md +32 -15
- data/docs/guides/logging.md +187 -0
- data/docs/guides/optimized-loading.md +674 -0
- data/docs/guides/thread-safety-monitoring.md +61 -0
- data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
- data/docs/migrating/v2.0.0-pre19.md +197 -0
- data/docs/migrating/v2.0.0-pre22.md +241 -0
- data/docs/overview.md +7 -9
- data/docs/reference/api-technical.md +267 -320
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
- data/examples/autoloader/mega_customer.rb +2 -0
- data/examples/datatype_standalone.rb +282 -0
- data/examples/encrypted_fields.rb +2 -1
- data/examples/json_usage_patterns.rb +2 -0
- data/examples/relationships.rb +3 -0
- data/examples/safe_dump.rb +2 -1
- data/examples/sampling_demo.rb +53 -0
- data/examples/single_connection_transaction_confusions.rb +2 -1
- data/familia.gemspec +2 -1
- data/lib/familia/base.rb +2 -0
- data/lib/familia/connection/behavior.rb +254 -0
- data/lib/familia/connection/handlers.rb +97 -0
- data/lib/familia/connection/individual_command_proxy.rb +2 -0
- data/lib/familia/connection/middleware.rb +34 -24
- data/lib/familia/connection/operation_core.rb +3 -1
- data/lib/familia/connection/operations.rb +2 -0
- data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +4 -2
- data/lib/familia/connection/transaction_core.rb +75 -9
- data/lib/familia/connection.rb +21 -5
- data/lib/familia/data_type/class_methods.rb +3 -1
- data/lib/familia/data_type/connection.rb +153 -7
- data/lib/familia/data_type/database_commands.rb +9 -4
- data/lib/familia/data_type/serialization.rb +10 -4
- data/lib/familia/data_type/settings.rb +2 -0
- data/lib/familia/data_type/types/counter.rb +2 -0
- data/lib/familia/data_type/types/hashkey.rb +8 -6
- data/lib/familia/data_type/types/listkey.rb +2 -0
- data/lib/familia/data_type/types/lock.rb +2 -0
- data/lib/familia/data_type/types/sorted_set.rb +2 -0
- data/lib/familia/data_type/types/stringkey.rb +2 -0
- data/lib/familia/data_type/types/unsorted_set.rb +2 -0
- data/lib/familia/data_type.rb +2 -0
- data/lib/familia/encryption/encrypted_data.rb +4 -2
- data/lib/familia/encryption/manager.rb +2 -0
- data/lib/familia/encryption/provider.rb +2 -0
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
- data/lib/familia/encryption/registry.rb +2 -0
- data/lib/familia/encryption/request_cache.rb +2 -0
- data/lib/familia/encryption.rb +9 -2
- data/lib/familia/errors.rb +53 -14
- data/lib/familia/features/autoloader.rb +2 -0
- data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
- data/lib/familia/features/encrypted_fields.rb +2 -2
- data/lib/familia/features/expiration/extensions.rb +11 -11
- data/lib/familia/features/expiration.rb +29 -21
- data/lib/familia/features/external_identifier.rb +33 -7
- data/lib/familia/features/object_identifier.rb +2 -0
- data/lib/familia/features/quantization.rb +3 -1
- data/lib/familia/features/relationships/README.md +3 -1
- data/lib/familia/features/relationships/collection_operations.rb +2 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +177 -47
- data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +203 -63
- data/lib/familia/features/relationships/indexing.rb +40 -42
- data/lib/familia/features/relationships/indexing_relationship.rb +17 -5
- data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
- data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
- data/lib/familia/features/relationships/participation.rb +155 -69
- data/lib/familia/features/relationships/participation_membership.rb +69 -0
- data/lib/familia/features/relationships/participation_relationship.rb +34 -6
- data/lib/familia/features/relationships/score_encoding.rb +2 -0
- data/lib/familia/features/relationships.rb +5 -3
- data/lib/familia/features/safe_dump.rb +2 -0
- data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
- data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
- data/lib/familia/features/transient_fields.rb +2 -0
- data/lib/familia/features.rb +2 -0
- data/lib/familia/field_type.rb +5 -2
- data/lib/familia/horreum/connection.rb +28 -36
- data/lib/familia/horreum/database_commands.rb +131 -10
- data/lib/familia/horreum/definition.rb +18 -7
- data/lib/familia/horreum/management.rb +233 -57
- data/lib/familia/horreum/persistence.rb +314 -122
- data/lib/familia/horreum/related_fields.rb +2 -0
- data/lib/familia/horreum/serialization.rb +26 -4
- data/lib/familia/horreum/settings.rb +2 -0
- data/lib/familia/horreum/utils.rb +2 -8
- data/lib/familia/horreum.rb +46 -13
- data/lib/familia/identifier_extractor.rb +2 -0
- data/lib/familia/instrumentation.rb +156 -0
- data/lib/familia/json_serializer.rb +2 -0
- data/lib/familia/logging.rb +94 -37
- data/lib/familia/refinements/dear_json.rb +2 -0
- data/lib/familia/refinements/stylize_words.rb +2 -14
- data/lib/familia/refinements/time_literals.rb +2 -0
- data/lib/familia/refinements.rb +2 -0
- data/lib/familia/secure_identifier.rb +10 -2
- data/lib/familia/settings.rb +9 -7
- data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
- data/lib/familia/thread_safety/monitor.rb +328 -0
- data/lib/familia/utils.rb +13 -0
- data/lib/familia/verifiable_identifier.rb +3 -1
- data/lib/familia/version.rb +3 -1
- data/lib/familia.rb +31 -4
- data/lib/middleware/database_command_counter.rb +152 -0
- data/lib/middleware/database_logger.rb +325 -129
- data/lib/multi_result.rb +2 -0
- data/try/edge_cases/empty_identifiers_try.rb +2 -0
- data/try/edge_cases/hash_symbolization_try.rb +2 -0
- data/try/edge_cases/json_serialization_try.rb +2 -0
- data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
- data/try/edge_cases/race_conditions_try.rb +4 -0
- data/try/edge_cases/reserved_keywords_try.rb +4 -0
- data/try/edge_cases/string_coercion_try.rb +6 -4
- data/try/edge_cases/ttl_side_effects_try.rb +4 -0
- data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
- data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
- data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
- data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
- data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
- data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
- data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
- data/try/features/encrypted_fields/memory_security_try.rb +4 -0
- data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
- data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
- data/try/features/encryption/config_persistence_try.rb +4 -0
- data/try/features/encryption/core_try.rb +4 -0
- data/try/features/encryption/instance_variable_scope_try.rb +4 -0
- data/try/features/encryption/module_loading_try.rb +4 -0
- data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
- data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
- data/try/features/encryption/roundtrip_validation_try.rb +4 -0
- data/try/features/encryption/secure_memory_handling_try.rb +4 -0
- data/try/features/expiration/expiration_try.rb +5 -1
- data/try/features/external_identifier/external_identifier_try.rb +171 -8
- data/try/features/feature_dependencies_try.rb +2 -0
- data/try/features/feature_improvements_try.rb +2 -0
- data/try/features/field_groups_try.rb +2 -0
- data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
- data/try/features/object_identifier/object_identifier_try.rb +2 -0
- data/try/features/quantization/quantization_try.rb +4 -0
- data/try/features/real_feature_integration_try.rb +2 -0
- data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
- data/try/features/relationships/indexing_rebuild_try.rb +600 -0
- data/try/features/relationships/indexing_try.rb +30 -4
- data/try/features/relationships/participation_bidirectional_try.rb +242 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
- data/try/features/relationships/participation_commands_verification_try.rb +2 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
- data/try/features/relationships/participation_reverse_index_try.rb +15 -13
- data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
- data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
- data/try/features/relationships/relationships_api_changes_try.rb +6 -4
- data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
- data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
- data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
- data/try/features/relationships/relationships_performance_try.rb +4 -0
- data/try/features/relationships/relationships_performance_working_try.rb +4 -0
- data/try/features/relationships/relationships_try.rb +6 -4
- data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
- data/try/features/safe_dump/safe_dump_try.rb +4 -0
- data/try/features/transient_fields/redacted_string_try.rb +2 -0
- data/try/features/transient_fields/refresh_reset_try.rb +3 -0
- data/try/features/transient_fields/simple_refresh_test.rb +3 -0
- data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
- data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
- data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
- data/try/integration/connection/fiber_context_preservation_try.rb +7 -3
- data/try/integration/connection/handler_constraints_try.rb +4 -0
- data/try/integration/connection/isolated_dbclient_try.rb +4 -0
- data/try/integration/connection/middleware_reconnect_try.rb +2 -0
- data/try/integration/connection/operation_mode_guards_try.rb +5 -1
- data/try/integration/connection/pipeline_fallback_integration_try.rb +15 -12
- data/try/integration/connection/pools_try.rb +4 -0
- data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
- data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
- data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
- data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
- data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
- data/try/integration/connection/transaction_modes_try.rb +4 -0
- data/try/integration/conventional_inheritance_try.rb +4 -0
- data/try/integration/create_method_try.rb +26 -22
- data/try/integration/cross_component_try.rb +4 -0
- data/try/integration/data_types/datatype_pipelines_try.rb +108 -0
- data/try/integration/data_types/datatype_transactions_try.rb +251 -0
- data/try/integration/database_consistency_try.rb +4 -0
- data/try/integration/familia_extended_try.rb +4 -0
- data/try/integration/familia_members_methods_try.rb +4 -0
- data/try/integration/models/customer_safe_dump_try.rb +9 -1
- data/try/integration/models/customer_try.rb +4 -0
- data/try/integration/models/datatype_base_try.rb +4 -0
- data/try/integration/models/familia_object_try.rb +5 -1
- data/try/integration/persistence_operations_try.rb +166 -10
- data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
- data/try/integration/save_methods_consistency_try.rb +241 -0
- data/try/integration/scenarios_try.rb +4 -0
- data/try/integration/secure_identifier_try.rb +4 -0
- data/try/integration/transaction_safety_core_try.rb +176 -0
- data/try/integration/transaction_safety_workflow_try.rb +291 -0
- data/try/integration/verifiable_identifier_try.rb +4 -0
- data/try/investigation/pipeline_routing/README.md +228 -0
- data/try/performance/benchmarks_try.rb +4 -0
- data/try/performance/transaction_safety_benchmark_try.rb +238 -0
- data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
- data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
- data/try/support/debugging/cache_behavior_tracer.rb +4 -0
- data/try/support/debugging/debug_aad_process.rb +3 -0
- data/try/support/debugging/debug_concealed_internal.rb +3 -0
- data/try/support/debugging/debug_concealed_reveal.rb +3 -0
- data/try/support/debugging/debug_context_aad.rb +3 -0
- data/try/support/debugging/debug_context_simple.rb +3 -0
- data/try/support/debugging/debug_cross_context.rb +3 -0
- data/try/support/debugging/debug_database_load.rb +3 -0
- data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
- data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
- data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
- data/try/support/debugging/debug_field_decrypt.rb +3 -0
- data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
- data/try/support/debugging/debug_load_path.rb +3 -0
- data/try/support/debugging/debug_method_definition.rb +3 -0
- data/try/support/debugging/debug_method_resolution.rb +3 -0
- data/try/support/debugging/debug_minimal.rb +3 -0
- data/try/support/debugging/debug_provider.rb +3 -0
- data/try/support/debugging/debug_secure_behavior.rb +3 -0
- data/try/support/debugging/debug_string_class.rb +3 -0
- data/try/support/debugging/debug_test.rb +3 -0
- data/try/support/debugging/debug_test_design.rb +3 -0
- data/try/support/debugging/encryption_method_tracer.rb +4 -0
- data/try/support/debugging/provider_diagnostics.rb +4 -0
- data/try/support/helpers/test_cleanup.rb +4 -0
- data/try/support/helpers/test_helpers.rb +5 -0
- data/try/support/memory/memory_basic_test.rb +4 -0
- data/try/support/memory/memory_detailed_test.rb +4 -0
- data/try/support/memory/memory_search_for_string.rb +4 -0
- data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
- data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
- data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
- data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
- data/try/support/prototypes/atomic_saves_v4.rb +4 -0
- data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
- data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
- data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
- data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
- data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
- data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
- data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
- data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
- data/try/support/prototypes/pooling/pool_siege.rb +4 -2
- data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
- data/try/thread_safety/README.md +496 -0
- data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
- data/try/thread_safety/connection_chain_race_try.rb +148 -0
- data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
- data/try/thread_safety/feature_registry_race_try.rb +226 -0
- data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
- data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
- data/try/thread_safety/field_registration_race_try.rb +222 -0
- data/try/thread_safety/logger_initialization_race_try.rb +170 -0
- data/try/thread_safety/middleware_registration_race_try.rb +154 -0
- data/try/thread_safety/module_config_race_try.rb +175 -0
- data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
- data/try/unit/core/autoloader_try.rb +4 -0
- data/try/unit/core/base_enhancements_try.rb +4 -0
- data/try/unit/core/connection_try.rb +4 -0
- data/try/unit/core/errors_try.rb +4 -0
- data/try/unit/core/extensions_try.rb +4 -0
- data/try/unit/core/familia_logger_try.rb +2 -0
- data/try/unit/core/familia_try.rb +4 -0
- data/try/unit/core/middleware_sampling_try.rb +335 -0
- data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
- data/try/unit/core/middleware_thread_safety_try.rb +245 -0
- data/try/unit/core/middleware_try.rb +4 -0
- data/try/unit/core/settings_try.rb +4 -0
- data/try/unit/core/time_utils_try.rb +4 -0
- data/try/unit/core/tools_try.rb +4 -0
- data/try/unit/core/utils_try.rb +37 -0
- data/try/unit/data_types/boolean_try.rb +5 -1
- data/try/unit/data_types/counter_try.rb +4 -0
- data/try/unit/data_types/datatype_base_try.rb +4 -0
- data/try/unit/data_types/hash_try.rb +4 -0
- data/try/unit/data_types/list_try.rb +4 -0
- data/try/unit/data_types/lock_try.rb +4 -0
- data/try/unit/data_types/sorted_set_try.rb +4 -0
- data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
- data/try/unit/data_types/string_try.rb +5 -1
- data/try/unit/data_types/unsortedset_try.rb +4 -0
- data/try/unit/familia_resolve_class_try.rb +116 -0
- data/try/unit/horreum/auto_indexing_on_save_try.rb +36 -16
- data/try/unit/horreum/automatic_index_validation_try.rb +255 -0
- data/try/unit/horreum/base_try.rb +5 -1
- data/try/unit/horreum/class_methods_try.rb +6 -2
- data/try/unit/horreum/commands_try.rb +4 -0
- data/try/unit/horreum/defensive_initialization_try.rb +4 -0
- data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +4 -0
- data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
- data/try/unit/horreum/field_categories_try.rb +4 -0
- data/try/unit/horreum/field_definition_try.rb +4 -0
- data/try/unit/horreum/initialization_try.rb +5 -1
- data/try/unit/horreum/json_type_preservation_try.rb +2 -0
- data/try/unit/horreum/optimized_loading_try.rb +156 -0
- data/try/unit/horreum/relations_try.rb +8 -4
- data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
- data/try/unit/horreum/serialization_try.rb +6 -2
- data/try/unit/horreum/settings_try.rb +4 -0
- data/try/unit/horreum/unique_index_edge_cases_try.rb +380 -0
- data/try/unit/horreum/unique_index_guard_validation_try.rb +283 -0
- data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
- data/try/unit/middleware/database_logger_methods_try.rb +251 -0
- data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
- data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
- data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
- data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
- data/try/unit/thread_safety_monitor_try.rb +149 -0
- metadata +81 -14
- data/.github/workflows/code-quality.yml +0 -138
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
- data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
- data/docs/archive/FAMILIA_UPDATE.md +0 -226
- data/docs/archive/README.md +0 -64
- data/docs/archive/api-reference.md +0 -333
- data/docs/guides/core-field-system.md +0 -806
- data/docs/guides/implementation.md +0 -276
- data/docs/guides/security-model.md +0 -183
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Thread Safety Monitoring Usage Guide
|
|
2
|
+
|
|
3
|
+
### Development
|
|
4
|
+
```ruby
|
|
5
|
+
# Enable monitoring during local testing
|
|
6
|
+
Familia.start_monitoring!
|
|
7
|
+
# ... run your application ...
|
|
8
|
+
report = Familia.thread_safety_report
|
|
9
|
+
puts report[:hot_spots] # See which mutexes are contentious
|
|
10
|
+
puts report[:recommendations] # Get actionable insights
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### CI/CD
|
|
14
|
+
```bash
|
|
15
|
+
# Add to test pipeline for race condition detection
|
|
16
|
+
FAMILIA_THREAD_SAFETY=1 bundle exec rspec
|
|
17
|
+
# Check for any race detections in CI logs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# In test setup
|
|
22
|
+
if ENV['FAMILIA_THREAD_SAFETY']
|
|
23
|
+
Familia.start_monitoring!
|
|
24
|
+
at_exit do
|
|
25
|
+
report = Familia.thread_safety_report
|
|
26
|
+
if report[:summary][:race_detections] > 0
|
|
27
|
+
puts "❌ Race conditions detected: #{report[:summary][:race_detections]}"
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Production
|
|
35
|
+
**APM Integration:**
|
|
36
|
+
```ruby
|
|
37
|
+
# Export metrics to DataDog, NewRelic, etc.
|
|
38
|
+
Thread.new do
|
|
39
|
+
loop do
|
|
40
|
+
metrics = Familia.thread_safety_metrics
|
|
41
|
+
StatsD.gauge('familia.thread_safety.health_score', metrics['familia.thread_safety.health_score'])
|
|
42
|
+
StatsD.count('familia.thread_safety.contentions', metrics['familia.thread_safety.mutex_contentions'])
|
|
43
|
+
sleep 60
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Health Check Endpoint:**
|
|
49
|
+
```ruby
|
|
50
|
+
# In Rails routes or Sinatra
|
|
51
|
+
get '/health/thread_safety' do
|
|
52
|
+
report = Familia.thread_safety_report
|
|
53
|
+
status = report[:health] >= 80 ? 200 : 503
|
|
54
|
+
json report
|
|
55
|
+
end
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Key Alerts:**
|
|
59
|
+
- `health_score < 70` → Investigate contention
|
|
60
|
+
- `race_detections > 0` → Critical issue
|
|
61
|
+
- `avg_wait_ms > 100` → Performance problem
|
|
@@ -9,8 +9,8 @@ The `Familia::Refinements::TimeLiterals` module extends Ruby's built-in classes
|
|
|
9
9
|
```ruby
|
|
10
10
|
using Familia::Refinements::TimeLiterals
|
|
11
11
|
|
|
12
|
-
2.hours #=> 7200 (seconds)
|
|
13
|
-
"30m".in_seconds #=> 1800
|
|
12
|
+
2.hours #=> 7200.0 (seconds)
|
|
13
|
+
"30m".in_seconds #=> 1800.0
|
|
14
14
|
timestamp.days_old #=> 5.2
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -45,12 +45,12 @@ using Familia::Refinements::TimeLiterals
|
|
|
45
45
|
# Singular and plural forms work identically
|
|
46
46
|
1.second #=> 1
|
|
47
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
|
|
48
|
+
5.minutes #=> 300.0
|
|
49
|
+
2.hours #=> 7200.0
|
|
50
|
+
3.days #=> 259200.0
|
|
51
|
+
1.week #=> 604800.0
|
|
52
|
+
2.months #=> 5259492.0
|
|
53
|
+
1.year #=> 31556952.0
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
### Converting Time Units Back
|
|
@@ -80,9 +80,9 @@ Parse human-readable time strings:
|
|
|
80
80
|
|
|
81
81
|
| Unit | Abbreviations | Example |
|
|
82
82
|
|------|---------------|---------|
|
|
83
|
-
| Microseconds | `us`, `μs`, `microsecond`, `microseconds` | `"
|
|
83
|
+
| Microseconds | `us`, `μs`, `microsecond`, `microseconds` | `"500μs"` |
|
|
84
84
|
| Milliseconds | `ms`, `millisecond`, `milliseconds` | `"250ms"` |
|
|
85
|
-
| Seconds |
|
|
85
|
+
| Seconds | (no unit) | `"30"` |
|
|
86
86
|
| Minutes | `m`, `minute`, `minutes` | `"15m"` |
|
|
87
87
|
| Hours | `h`, `hour`, `hours` | `"2h"` |
|
|
88
88
|
| Days | `d`, `day`, `days` | `"7d"` |
|
|
@@ -90,7 +90,7 @@ Parse human-readable time strings:
|
|
|
90
90
|
| Months | `mo`, `month`, `months` | `"6mo"` |
|
|
91
91
|
| Years | `y`, `year`, `years` | `"1y"` |
|
|
92
92
|
|
|
93
|
-
**Note**: Use `"mo"` for months to avoid confusion with `"m"` (minutes).
|
|
93
|
+
**Note**: Use `"mo"` for months to avoid confusion with `"m"` (minutes). Seconds don't require a unit suffix.
|
|
94
94
|
|
|
95
95
|
## Age Calculations
|
|
96
96
|
|
|
@@ -215,7 +215,7 @@ If upgrading from earlier versions:
|
|
|
215
215
|
12.months != 1.year # Different values
|
|
216
216
|
|
|
217
217
|
# New behavior (consistent)
|
|
218
|
-
12.months == 1.year # Same value: 31,556,952 seconds
|
|
218
|
+
12.months == 1.year # Same value: 31,556,952.0 seconds
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
Update any code that relied on the old 365-day year constant to expect the new Gregorian year values.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Migrating Guide: v2.0.0-pre19
|
|
2
|
+
|
|
3
|
+
This version introduces significant improvements to Familia's database operations, making them more atomic, reliable, and consistent with Rails conventions. The changes include enhanced error handling, optimistic locking support, and breaking API changes.
|
|
4
|
+
|
|
5
|
+
## Breaking Changes
|
|
6
|
+
|
|
7
|
+
### Management.create → create!
|
|
8
|
+
|
|
9
|
+
**What Changed:**
|
|
10
|
+
|
|
11
|
+
The `create` class method has been renamed to `create!` to follow Rails conventions and indicate that it raises exceptions on failure.
|
|
12
|
+
|
|
13
|
+
**Before:**
|
|
14
|
+
```ruby
|
|
15
|
+
user = User.create(email: "test@example.com")
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**After:**
|
|
19
|
+
```ruby
|
|
20
|
+
user = User.create!(email: "test@example.com")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Why This Matters:**
|
|
24
|
+
|
|
25
|
+
- Follows Rails naming conventions where `!` methods raise exceptions
|
|
26
|
+
- Makes it clear that `CreationError` will be raised if object already exists
|
|
27
|
+
- Prevents silent failures in object creation
|
|
28
|
+
|
|
29
|
+
### save_if_not_exists → save_if_not_exists!
|
|
30
|
+
|
|
31
|
+
**What Changed:**
|
|
32
|
+
|
|
33
|
+
The `save_if_not_exists` method has been renamed to `save_if_not_exists!` and now includes optimistic locking with automatic retry logic.
|
|
34
|
+
|
|
35
|
+
**Before:**
|
|
36
|
+
```ruby
|
|
37
|
+
success = user.save_if_not_exists
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**After:**
|
|
41
|
+
```ruby
|
|
42
|
+
success = user.save_if_not_exists!
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Behavior Changes:**
|
|
46
|
+
- Now uses Redis WATCH/MULTI/EXEC for optimistic locking
|
|
47
|
+
- Automatically retries up to 3 times on `OptimisticLockError`
|
|
48
|
+
- Raises `RecordExistsError` if object already exists
|
|
49
|
+
- More atomic and thread-safe
|
|
50
|
+
|
|
51
|
+
## Enhanced Error Handling
|
|
52
|
+
|
|
53
|
+
### New Error Hierarchy
|
|
54
|
+
|
|
55
|
+
**What's New:**
|
|
56
|
+
|
|
57
|
+
A structured error hierarchy provides better error categorization:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
Familia::Problem # Base class
|
|
61
|
+
├── Familia::PersistenceError # Redis/database errors
|
|
62
|
+
│ ├── Familia::NonUniqueKey
|
|
63
|
+
│ ├── Familia::OptimisticLockError
|
|
64
|
+
│ ├── Familia::OperationModeError
|
|
65
|
+
│ ├── Familia::NoConnectionAvailable
|
|
66
|
+
│ ├── Familia::NotFound
|
|
67
|
+
│ └── Familia::NotConnected
|
|
68
|
+
└── Familia::HorreumError # Model-related errors
|
|
69
|
+
├── Familia::CreationError
|
|
70
|
+
├── Familia::NoIdentifier
|
|
71
|
+
├── Familia::FieldTypeError
|
|
72
|
+
├── Familia::AutoloadError
|
|
73
|
+
├── Familia::SerializerError
|
|
74
|
+
├── Familia::UnknownFieldError
|
|
75
|
+
└── Familia::NotDistinguishableError
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Migration:**
|
|
79
|
+
|
|
80
|
+
Update exception handling to use specific error classes:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Before
|
|
84
|
+
rescue Familia::Problem => e
|
|
85
|
+
# Handle all errors
|
|
86
|
+
|
|
87
|
+
# After - More granular handling
|
|
88
|
+
rescue Familia::CreationError => e
|
|
89
|
+
# Handle creation failures specifically
|
|
90
|
+
rescue Familia::OptimisticLockError => e
|
|
91
|
+
# Handle concurrent modification
|
|
92
|
+
rescue Familia::PersistenceError => e
|
|
93
|
+
# Handle database-related errors
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### New Exception Types
|
|
97
|
+
|
|
98
|
+
- **`CreationError`**: Raised when object creation fails (replaces generic errors)
|
|
99
|
+
- **`OptimisticLockError`**: Raised when WATCH fails due to concurrent modification
|
|
100
|
+
- **`UnknownFieldError`**: Raised when referencing non-existent fields
|
|
101
|
+
|
|
102
|
+
## Database Operation Improvements
|
|
103
|
+
|
|
104
|
+
### Atomic Save Operations
|
|
105
|
+
|
|
106
|
+
**What Changed:**
|
|
107
|
+
|
|
108
|
+
The `save` method now uses a single Redis transaction for complete atomicity:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# All operations now happen atomically:
|
|
112
|
+
# 1. Save all fields (HMSET)
|
|
113
|
+
# 2. Set expiration (EXPIRE)
|
|
114
|
+
# 3. Update indexes
|
|
115
|
+
# 4. Add to instances collection
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Benefits:**
|
|
119
|
+
- Eliminates race conditions during save operations
|
|
120
|
+
- Ensures data consistency across related operations
|
|
121
|
+
- Better performance with fewer round trips
|
|
122
|
+
|
|
123
|
+
### Enhanced Timestamp Precision
|
|
124
|
+
|
|
125
|
+
**What Changed:**
|
|
126
|
+
|
|
127
|
+
Created/updated timestamps now use float values instead of integers for higher precision:
|
|
128
|
+
|
|
129
|
+
**Before:**
|
|
130
|
+
```ruby
|
|
131
|
+
user.created # => 1697234567 (integer seconds)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**After:**
|
|
135
|
+
```ruby
|
|
136
|
+
user.created # => 1697234567.123 (float with milliseconds)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Migration:**
|
|
140
|
+
|
|
141
|
+
No code changes needed. Existing integer timestamps continue to work.
|
|
142
|
+
|
|
143
|
+
## Redis Command Enhancements
|
|
144
|
+
|
|
145
|
+
### New Commands Available
|
|
146
|
+
|
|
147
|
+
Added support for optimistic locking commands:
|
|
148
|
+
- `watch(key)` - Watch key for changes
|
|
149
|
+
- `unwatch()` - Remove all watches
|
|
150
|
+
- `discard()` - Discard queued commands
|
|
151
|
+
|
|
152
|
+
### Improved Command Logging
|
|
153
|
+
|
|
154
|
+
Database command logging now includes:
|
|
155
|
+
- Structured format for better readability
|
|
156
|
+
- Pipelined operation tracking
|
|
157
|
+
- Transaction boundary markers
|
|
158
|
+
- Command timing information
|
|
159
|
+
|
|
160
|
+
## Terminology Updates
|
|
161
|
+
|
|
162
|
+
**What Changed:**
|
|
163
|
+
|
|
164
|
+
Standardized on "pipelined" terminology throughout (previously mixed "pipeline"/"pipelined").
|
|
165
|
+
|
|
166
|
+
**Files Affected:**
|
|
167
|
+
- Method names now consistently use "pipelined"
|
|
168
|
+
- Documentation updated to match Redis terminology
|
|
169
|
+
- Log messages standardized
|
|
170
|
+
|
|
171
|
+
**Migration:**
|
|
172
|
+
|
|
173
|
+
No code changes needed - this was an internal consistency improvement.
|
|
174
|
+
|
|
175
|
+
## Recommended Actions
|
|
176
|
+
|
|
177
|
+
1. **Update method calls:**
|
|
178
|
+
```ruby
|
|
179
|
+
# Replace all instances
|
|
180
|
+
Model.create(...) → Model.create!(...)
|
|
181
|
+
obj.save_if_not_exists → obj.save_if_not_exists!
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
2. **Review error handling:**
|
|
185
|
+
```ruby
|
|
186
|
+
# Consider more specific error handling
|
|
187
|
+
rescue Familia::CreationError
|
|
188
|
+
rescue Familia::OptimisticLockError
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
3. **Test concurrent operations:**
|
|
192
|
+
- The new optimistic locking provides better concurrency handling
|
|
193
|
+
- Verify your application handles `OptimisticLockError` appropriately
|
|
194
|
+
|
|
195
|
+
4. **Review logging:**
|
|
196
|
+
- Enhanced database command logging may affect log volume
|
|
197
|
+
- Adjust log levels if needed for production environments
|
|
@@ -0,0 +1,241 @@
|
|
|
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)
|
data/docs/overview.md
CHANGED
|
@@ -289,13 +289,11 @@ class SecureModel < Familia::Horreum
|
|
|
289
289
|
# → password, password= (no fast writer method)
|
|
290
290
|
# → Values wrapped in RedactedString
|
|
291
291
|
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
# → security_question, security_question=, security_question!
|
|
292
|
+
# Note: All transient field values are automatically wrapped in RedactedString
|
|
293
|
+
# for security - they never persist to the database
|
|
295
294
|
|
|
296
|
-
# Object identifier fields auto-generate unique IDs
|
|
295
|
+
# Object identifier fields auto-generate unique IDs when using the feature
|
|
297
296
|
# → objid, objid= (lazy generation, preserves initialization values)
|
|
298
|
-
# → objid_generator_used (provenance tracking)
|
|
299
297
|
end
|
|
300
298
|
|
|
301
299
|
# Usage examples
|
|
@@ -856,7 +854,7 @@ Familia.debug = true # Shows feature loading sequence
|
|
|
856
854
|
Familia.debug = true
|
|
857
855
|
|
|
858
856
|
# Check what's in Valkey
|
|
859
|
-
Familia.
|
|
857
|
+
Familia.dbclient.keys('*') # List all keys (use carefully in production)
|
|
860
858
|
```
|
|
861
859
|
|
|
862
860
|
## Testing
|
|
@@ -880,7 +878,7 @@ Familia.config.current_key_version = :v1
|
|
|
880
878
|
|
|
881
879
|
# Clear data between tests
|
|
882
880
|
def clear_redis
|
|
883
|
-
Familia.
|
|
881
|
+
Familia.dbclient.flushdb
|
|
884
882
|
end
|
|
885
883
|
|
|
886
884
|
# Feature-specific testing patterns
|
|
@@ -898,8 +896,8 @@ end
|
|
|
898
896
|
|
|
899
897
|
def test_relationships_cleanup
|
|
900
898
|
# Clean up relationship indexes
|
|
901
|
-
Familia.
|
|
902
|
-
Familia.
|
|
899
|
+
Familia.dbclient.keys('*:relationships:*').each do |key|
|
|
900
|
+
Familia.dbclient.del(key)
|
|
903
901
|
end
|
|
904
902
|
end
|
|
905
903
|
```
|