familia 2.0.0.pre19 → 2.0.0.pre22
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/.talismanrc +5 -1
- data/CHANGELOG.rst +220 -112
- data/CLAUDE.md +28 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +20 -17
- data/bin/try +16 -0
- data/bin/tryouts +16 -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 +161 -117
- 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-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 +4 -3
- 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 +2 -0
- data/lib/familia/connection/handlers.rb +2 -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 -2
- data/lib/familia/connection/operations.rb +2 -0
- data/lib/familia/connection/pipelined_core.rb +3 -3
- data/lib/familia/connection/transaction_core.rb +69 -2
- data/lib/familia/connection.rb +18 -3
- data/lib/familia/data_type/class_methods.rb +3 -1
- data/lib/familia/data_type/connection.rb +2 -0
- data/lib/familia/data_type/database_commands.rb +2 -0
- data/lib/familia/data_type/serialization.rb +79 -52
- 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 +7 -5
- 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 +7 -10
- data/lib/familia/data_type/types/stringkey.rb +24 -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 +2 -0
- 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 +3 -1
- data/lib/familia/features/expiration.rb +12 -4
- data/lib/familia/features/external_identifier.rb +62 -7
- data/lib/familia/features/object_identifier.rb +49 -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 +138 -9
- data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +97 -21
- data/lib/familia/features/relationships/indexing.rb +3 -0
- data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
- 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 +3 -1
- data/lib/familia/horreum/connection.rb +17 -1
- data/lib/familia/horreum/database_commands.rb +8 -1
- data/lib/familia/horreum/definition.rb +16 -6
- data/lib/familia/horreum/management.rb +353 -52
- data/lib/familia/horreum/persistence.rb +179 -108
- data/lib/familia/horreum/related_fields.rb +2 -0
- data/lib/familia/horreum/serialization.rb +23 -4
- data/lib/familia/horreum/settings.rb +2 -0
- data/lib/familia/horreum/utils.rb +2 -0
- data/lib/familia/horreum.rb +15 -1
- data/lib/familia/identifier_extractor.rb +3 -1
- data/lib/familia/instrumentation.rb +156 -0
- data/lib/familia/json_serializer.rb +2 -0
- data/lib/familia/logging.rb +92 -32
- 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 +2 -0
- 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 +295 -170
- data/lib/multi_result.rb +61 -31
- 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 +2 -0
- data/try/edge_cases/ttl_side_effects_try.rb +4 -0
- data/try/features/count_any_edge_cases_try.rb +486 -0
- data/try/features/count_any_methods_try.rb +197 -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 +4 -0
- data/try/features/external_identifier/external_identifier_try.rb +305 -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 +140 -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 +606 -0
- data/try/features/relationships/indexing_try.rb +2 -0
- 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 +2 -0
- 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 +4 -0
- 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 +4 -0
- data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
- 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 +4 -0
- data/try/integration/cross_component_try.rb +4 -0
- data/try/integration/data_types/datatype_pipelines_try.rb +9 -3
- data/try/integration/data_types/datatype_transactions_try.rb +17 -7
- 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 +4 -0
- data/try/integration/models/customer_try.rb +7 -3
- data/try/integration/models/datatype_base_try.rb +4 -0
- data/try/integration/models/familia_object_try.rb +4 -0
- data/try/integration/persistence_operations_try.rb +4 -0
- 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 +39 -22
- 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 +6 -2
- 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/serialization_try.rb +386 -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 +4 -0
- 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 +5 -1
- data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
- data/try/unit/horreum/base_try.rb +4 -0
- data/try/unit/horreum/class_methods_try.rb +4 -0
- 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 +6 -1
- 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 +4 -0
- 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 +4 -0
- data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
- data/try/unit/horreum/serialization_try.rb +4 -0
- data/try/unit/horreum/settings_try.rb +4 -0
- data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
- data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -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 +69 -17
- data/.github/workflows/code-quality.yml +0 -138
- data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
- data/changelog.d/20251011_203905_delano_next.rst +0 -30
- data/changelog.d/20251011_212633_delano_next.rst +0 -13
- data/changelog.d/20251011_221253_delano_next.rst +0 -26
- 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,263 @@
|
|
|
1
|
+
# Relationships Participation Guide
|
|
2
|
+
|
|
3
|
+
Participation creates bidirectional associations between Familia objects with automatic reverse tracking, semantic scoring, and lifecycle management.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
Participation manages "belongs to" relationships where:
|
|
8
|
+
- **Membership has meaning** - Customer owns Domains, User belongs to Teams
|
|
9
|
+
- **Scores have semantic value** - Priority, timestamps, permissions
|
|
10
|
+
- **Bidirectional tracking** - Both sides know about the relationship
|
|
11
|
+
- **Lifecycle matters** - Automatic cleanup on destroy
|
|
12
|
+
|
|
13
|
+
## Basic Usage
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
class Domain < Familia::Horreum
|
|
17
|
+
feature :relationships
|
|
18
|
+
field :created_at
|
|
19
|
+
|
|
20
|
+
participates_in Customer, :domains, score: :created_at
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Customer < Familia::Horreum
|
|
24
|
+
feature :relationships
|
|
25
|
+
# sorted_set :domains created automatically
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Bidirectional relationship management
|
|
29
|
+
customer.add_domains_instance(domain) # Add with timestamp score
|
|
30
|
+
domain.in_customer_domains?(customer) # => true
|
|
31
|
+
domain.customer_instances # => [customer]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Collection Types
|
|
35
|
+
|
|
36
|
+
### Sorted Set (Default)
|
|
37
|
+
|
|
38
|
+
Ordered collections with semantic scores:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
participates_in Project, :tasks, score: :priority
|
|
42
|
+
|
|
43
|
+
project.tasks.range(0, 4, order: 'DESC') # Top 5 by priority
|
|
44
|
+
task.score_in_project_tasks(project) # Get current score
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Unsorted Set
|
|
48
|
+
|
|
49
|
+
Simple membership without ordering:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
participates_in Team, :members, type: :set
|
|
53
|
+
|
|
54
|
+
team.members.member?(user.identifier) # Fast O(1) check
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### List
|
|
58
|
+
|
|
59
|
+
Ordered sequences:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
participates_in Playlist, :songs, type: :list
|
|
63
|
+
|
|
64
|
+
song.position_in_playlist_songs(playlist) # Get position
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Scoring Strategies
|
|
68
|
+
|
|
69
|
+
### Field-Based
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
participates_in Category, :articles, score: :published_at
|
|
73
|
+
participates_in User, :bookmarks, score: :rating
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Lambda-Based
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
participates_in Department, :employees, score: -> {
|
|
80
|
+
performance_rating * 100 + tenure_years * 10
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Permission Encoding
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
participates_in Customer, :domains, score: -> {
|
|
88
|
+
permission_encode(created_at, permission_bits)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
customer.domains_with_permission(:read) # Query by permission
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Class-Level Participation
|
|
95
|
+
|
|
96
|
+
Track all instances automatically:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
class User < Familia::Horreum
|
|
100
|
+
class_participates_in :all_users, score: :created_at
|
|
101
|
+
class_participates_in :active_users,
|
|
102
|
+
score: ->(u) { u.active? ? u.last_activity : 0 }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
User.all_users.size # Total count
|
|
106
|
+
User.active_users.range(0, 9) # Top 10 active
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Multiple Collections
|
|
110
|
+
|
|
111
|
+
Participants can belong to multiple collections:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
class User < Familia::Horreum
|
|
115
|
+
participates_in Team, :members
|
|
116
|
+
participates_in Team, :admins
|
|
117
|
+
participates_in Organization, :employees, as: :employers
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Separate methods per collection
|
|
121
|
+
user.add_to_team_members(team)
|
|
122
|
+
user.add_to_team_admins(team)
|
|
123
|
+
|
|
124
|
+
# Reverse methods union collections
|
|
125
|
+
user.team_instances # Union of members + admins
|
|
126
|
+
user.employers_instances # Custom name via 'as:'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Lifecycle Management
|
|
130
|
+
|
|
131
|
+
### Automatic Tracking
|
|
132
|
+
|
|
133
|
+
Familia maintains a reverse index for cleanup:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
domain.participations.members
|
|
137
|
+
# => ["customer:cust_123:domains", "customer:cust_456:domains"]
|
|
138
|
+
|
|
139
|
+
domain.current_participations
|
|
140
|
+
# => [
|
|
141
|
+
# { collection_key: "customer:cust_123:domains", score: 1640995200 },
|
|
142
|
+
# { collection_key: "customer:cust_456:domains", score: 1640995300 }
|
|
143
|
+
# ]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Cleanup
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
class Domain < Familia::Horreum
|
|
150
|
+
before_destroy :cleanup_relationships
|
|
151
|
+
|
|
152
|
+
def cleanup_relationships
|
|
153
|
+
# Automatic removal from all collections
|
|
154
|
+
super
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Advanced Patterns
|
|
160
|
+
|
|
161
|
+
### Conditional Scoring
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
participates_in Project, :tasks, score: -> {
|
|
165
|
+
status == 'active' ? priority : 0
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# Filter by score
|
|
169
|
+
active_tasks = project.tasks.range_by_score(1, '+inf')
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Time-Based Expiration
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
participates_in User, :sessions, score: :expires_at
|
|
176
|
+
|
|
177
|
+
# Query active sessions
|
|
178
|
+
now = Time.now.to_i
|
|
179
|
+
active = user.sessions.range_by_score(now, '+inf')
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Validation
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
def add_members_instance(user, score = nil)
|
|
186
|
+
raise "Team is full" if members.size >= max_members
|
|
187
|
+
raise "User not active" unless user.status == 'active'
|
|
188
|
+
super
|
|
189
|
+
end
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Performance Best Practices
|
|
193
|
+
|
|
194
|
+
### Bulk Operations
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# ✅ Efficient bulk add
|
|
198
|
+
customer.add_domains([domain1, domain2, domain3])
|
|
199
|
+
|
|
200
|
+
# ❌ Avoid loops
|
|
201
|
+
domains.each { |d| customer.add_domains_instance(d) }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Pagination
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
# ✅ Paginated access
|
|
208
|
+
customer.domains.range(0, 19) # First 20
|
|
209
|
+
customer.domains.range(20, 39) # Next 20
|
|
210
|
+
|
|
211
|
+
# ❌ Loading all
|
|
212
|
+
customer.domains.to_a # Loads all IDs
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Direct Collection Access
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# For IDs only
|
|
219
|
+
customer.domains.to_a # Just IDs
|
|
220
|
+
customer.domains.merge([id1, id2]) # Bulk ID operations
|
|
221
|
+
|
|
222
|
+
# For objects
|
|
223
|
+
domain.customer_instances # Efficient bulk loading
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Troubleshooting
|
|
227
|
+
|
|
228
|
+
### Common Issues
|
|
229
|
+
|
|
230
|
+
**Method not found:**
|
|
231
|
+
- Ensure `feature :relationships` on both classes
|
|
232
|
+
- Verify `participates_in` declaration
|
|
233
|
+
- Check method naming patterns
|
|
234
|
+
|
|
235
|
+
**Inconsistent relationships:**
|
|
236
|
+
- Use transactions for complex operations
|
|
237
|
+
- Implement validation in overridden methods
|
|
238
|
+
- Monitor reverse index consistency
|
|
239
|
+
|
|
240
|
+
**Performance issues:**
|
|
241
|
+
- Use bulk operations
|
|
242
|
+
- Implement pagination
|
|
243
|
+
- Consider direct collection access for IDs
|
|
244
|
+
|
|
245
|
+
### Debugging
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
# Check configuration
|
|
249
|
+
Domain.participation_relationships
|
|
250
|
+
# => [{ target_class: Customer, collection_name: :domains, ... }]
|
|
251
|
+
|
|
252
|
+
# Inspect participations
|
|
253
|
+
domain.current_participations
|
|
254
|
+
|
|
255
|
+
# Validate consistency
|
|
256
|
+
domain.validate_relationships!
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## See Also
|
|
260
|
+
|
|
261
|
+
- [**Relationships Overview**](feature-relationships.md) - Core concepts
|
|
262
|
+
- [**Methods Reference**](feature-relationships-methods.md) - Complete API
|
|
263
|
+
- [**Indexing Guide**](feature-relationships-indexing.md) - Attribute lookups
|
|
@@ -1,200 +1,182 @@
|
|
|
1
1
|
# Relationships Feature Guide
|
|
2
2
|
|
|
3
|
-
The Relationships feature
|
|
4
|
-
|
|
5
|
-
This guide provides a breadth-first introduction to the four core relationship capabilities: **participation**, **indexing**, **querying**, and **cascading operations**.
|
|
3
|
+
The Relationships feature provides automatic bidirectional associations between Familia objects, eliminating manual foreign key management while enabling efficient queries through Redis-native data structures.
|
|
6
4
|
|
|
7
5
|
> [!TIP]
|
|
8
|
-
> Enable
|
|
9
|
-
|
|
10
|
-
## What Are Relationships?
|
|
6
|
+
> Enable with `feature :relationships` and define associations using `participates_in` for automatic method generation.
|
|
11
7
|
|
|
12
|
-
|
|
8
|
+
## Quick Start
|
|
13
9
|
|
|
14
10
|
```ruby
|
|
15
|
-
|
|
16
|
-
customer.domain_ids.add(domain.identifier)
|
|
17
|
-
domain.customer_id = customer.identifier
|
|
18
|
-
|
|
19
|
-
# With relationships - automatic and clean
|
|
20
|
-
customer.domains << domain # Updates both sides automatically
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Key Benefits:**
|
|
24
|
-
- **Automatic bidirectional updates** - no manual synchronization
|
|
25
|
-
- **Ruby-like syntax** - familiar `<<` and collection operations
|
|
26
|
-
- **O(1) lookups** - efficient Valkey/Redis-backed indexing
|
|
27
|
-
- **Lifecycle management** - automatic cleanup and maintenance
|
|
28
|
-
|
|
29
|
-
## Core Relationship Capabilities
|
|
30
|
-
|
|
31
|
-
### 1. Participation - Bidirectional Object Links
|
|
32
|
-
|
|
33
|
-
Connect objects with automatic synchronization:
|
|
34
|
-
|
|
35
|
-
```ruby
|
|
36
|
-
class User < Familia::Horreum
|
|
11
|
+
class Customer < Familia::Horreum
|
|
37
12
|
feature :relationships
|
|
38
|
-
|
|
13
|
+
# Collection 'domains' created automatically
|
|
39
14
|
end
|
|
40
15
|
|
|
41
|
-
class
|
|
16
|
+
class Domain < Familia::Horreum
|
|
42
17
|
feature :relationships
|
|
43
|
-
participates_in
|
|
18
|
+
participates_in Customer, :domains
|
|
44
19
|
end
|
|
45
20
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
21
|
+
# Automatic bidirectional relationship management
|
|
22
|
+
customer.add_domains_instance(domain) # Add relationship
|
|
23
|
+
domain.in_customer_domains?(customer) # => true
|
|
24
|
+
domain.customer_instances # => [customer]
|
|
49
25
|
```
|
|
50
26
|
|
|
51
|
-
|
|
27
|
+
## Core Capabilities
|
|
28
|
+
|
|
29
|
+
### Participation - Bidirectional Associations
|
|
30
|
+
|
|
31
|
+
Create semantic relationships between objects with automatic reverse tracking:
|
|
32
|
+
|
|
52
33
|
```ruby
|
|
53
|
-
class
|
|
54
|
-
|
|
34
|
+
class User < Familia::Horreum
|
|
35
|
+
feature :relationships
|
|
36
|
+
participates_in Team, :members, score: :joined_at
|
|
37
|
+
participates_in Team, :admins
|
|
55
38
|
end
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
# Generated methods on Team (target)
|
|
41
|
+
team.add_members_instance(user) # Add single member
|
|
42
|
+
team.add_members([user1, user2]) # Bulk add
|
|
43
|
+
team.members.range(0, 9) # First 10 members
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
# Generated methods on User (participant)
|
|
46
|
+
user.add_to_team_members(team) # Add self to team
|
|
47
|
+
user.in_team_admins?(team) # Check membership
|
|
48
|
+
user.team_instances # All teams (members + admins)
|
|
64
49
|
```
|
|
65
50
|
|
|
66
|
-
###
|
|
51
|
+
### Indexing - Fast Attribute Lookups
|
|
67
52
|
|
|
68
|
-
Enable O(1)
|
|
53
|
+
Enable O(1) field-based queries with automatic index management:
|
|
69
54
|
|
|
70
55
|
```ruby
|
|
71
56
|
class User < Familia::Horreum
|
|
72
57
|
feature :relationships
|
|
73
|
-
field :email, :
|
|
74
|
-
|
|
75
|
-
# Global unique lookups
|
|
76
|
-
class_indexed_by :email, :email_lookup
|
|
58
|
+
field :email, :username
|
|
77
59
|
|
|
78
|
-
#
|
|
79
|
-
|
|
60
|
+
# Global unique indexes (auto-managed on save/destroy)
|
|
61
|
+
unique_index :email, :email_lookup
|
|
62
|
+
unique_index :username, :username_lookup
|
|
80
63
|
end
|
|
81
64
|
|
|
82
|
-
#
|
|
83
|
-
User.
|
|
84
|
-
User.all_users.range(0, 9) # Most recent 10 users
|
|
85
|
-
```
|
|
65
|
+
User.find_by_email("alice@example.com") # O(1) lookup
|
|
66
|
+
User.find_by_username("alice") # O(1) lookup
|
|
86
67
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
# Scoped indexing (manual management required)
|
|
69
|
+
class Employee < Familia::Horreum
|
|
70
|
+
feature :relationships
|
|
71
|
+
unique_index :badge_number, :badge_index, within: Company
|
|
72
|
+
multi_index :department, :dept_index, within: Company
|
|
92
73
|
end
|
|
93
74
|
|
|
94
|
-
|
|
75
|
+
employee.add_to_company_badge_index(company)
|
|
76
|
+
company.find_by_badge_number("12345") # Scoped lookup
|
|
77
|
+
company.find_all_by_department("engineering") # Multi-value
|
|
95
78
|
```
|
|
96
79
|
|
|
97
|
-
###
|
|
80
|
+
### Scoring - Semantic Ordering
|
|
98
81
|
|
|
99
|
-
|
|
82
|
+
Use scores for temporal tracking, priority systems, or custom ordering:
|
|
100
83
|
|
|
101
84
|
```ruby
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
85
|
+
class Task < Familia::Horreum
|
|
86
|
+
feature :relationships
|
|
87
|
+
field :priority, :created_at
|
|
88
|
+
|
|
89
|
+
# Field-based scoring
|
|
90
|
+
participates_in Project, :tasks, score: :priority
|
|
91
|
+
|
|
92
|
+
# Lambda-based scoring
|
|
93
|
+
participates_in Sprint, :tasks, score: -> {
|
|
94
|
+
priority * 100 + (Time.now - created_at) / 3600
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
project.tasks.range(0, 4, order: 'DESC') # Top 5 by priority
|
|
99
|
+
sprint.tasks.range_by_score(500, '+inf') # High priority tasks
|
|
115
100
|
```
|
|
116
101
|
|
|
117
|
-
|
|
118
|
-
|
|
102
|
+
## Generated Method Reference
|
|
103
|
+
|
|
104
|
+
### When Domain declares `participates_in Customer, :domains`
|
|
105
|
+
|
|
106
|
+
| Class | Method | Purpose |
|
|
107
|
+
|-------|--------|---------|
|
|
108
|
+
| **Customer** | `domains` | Access collection |
|
|
109
|
+
| | `add_domains_instance(domain)` | Add single item |
|
|
110
|
+
| | `add_domains([domains])` | Bulk add |
|
|
111
|
+
| | `remove_domains_instance(domain)` | Remove item |
|
|
112
|
+
| **Domain** | `add_to_customer_domains(customer)` | Add to collection |
|
|
113
|
+
| | `remove_from_customer_domains(customer)` | Remove from collection |
|
|
114
|
+
| | `in_customer_domains?(customer)` | Check membership |
|
|
115
|
+
| | `score_in_customer_domains(customer)` | Get score (sorted_set) |
|
|
116
|
+
| | `customer_instances` | Load all customers |
|
|
117
|
+
| | `customer_ids` | Get customer IDs |
|
|
118
|
+
| | `customer?` | Has any customers? |
|
|
119
|
+
| | `customer_count` | Count relationships |
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
## Common Patterns
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
### Multiple Collections
|
|
123
124
|
|
|
124
125
|
```ruby
|
|
125
|
-
class
|
|
126
|
-
feature :relationships
|
|
127
|
-
sorted_set :events # Scored collection
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
class Event < Familia::Horreum
|
|
126
|
+
class User < Familia::Horreum
|
|
131
127
|
feature :relationships
|
|
132
|
-
|
|
133
|
-
participates_in
|
|
128
|
+
participates_in Project, :contributors
|
|
129
|
+
participates_in Project, :reviewers
|
|
130
|
+
participates_in Organization, :employees, as: :employers
|
|
134
131
|
end
|
|
135
132
|
|
|
136
|
-
#
|
|
137
|
-
|
|
133
|
+
# Separate methods per collection
|
|
134
|
+
user.add_to_project_contributors(project)
|
|
135
|
+
user.add_to_project_reviewers(project)
|
|
138
136
|
|
|
139
|
-
#
|
|
140
|
-
|
|
141
|
-
top_priority = project.tasks.range(0, 4, order: 'DESC') # Highest priority first
|
|
137
|
+
# Custom reverse method names
|
|
138
|
+
user.employers_instances # Instead of organization_instances
|
|
142
139
|
```
|
|
143
140
|
|
|
144
|
-
|
|
145
|
-
- **Timestamps** for chronological ordering
|
|
146
|
-
- **Priority levels** for ranking systems
|
|
147
|
-
- **User ratings** for recommendation systems
|
|
148
|
-
- **Custom lambdas** for complex scoring logic
|
|
141
|
+
### Class-Level Tracking
|
|
149
142
|
|
|
150
|
-
## Best Practices
|
|
151
|
-
|
|
152
|
-
**Performance:**
|
|
153
|
-
- Use `merge([id1, id2, id3])` for bulk additions
|
|
154
|
-
- Use `multiget(*ids)` for efficient bulk loading
|
|
155
|
-
- Use pagination: `collection.range(0, 9)` instead of loading all
|
|
156
|
-
|
|
157
|
-
**Lifecycle Management:**
|
|
158
143
|
```ruby
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
144
|
+
class Customer < Familia::Horreum
|
|
145
|
+
feature :relationships
|
|
146
|
+
class_participates_in :all_customers, score: :created_at
|
|
147
|
+
class_participates_in :premium_customers,
|
|
148
|
+
score: ->(c) { c.tier == 'premium' ? c.last_activity : 0 }
|
|
162
149
|
end
|
|
163
|
-
```
|
|
164
150
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def add_member(user_id)
|
|
168
|
-
raise "Team is full" if members.size >= max_members
|
|
169
|
-
members << user_id
|
|
170
|
-
end
|
|
151
|
+
Customer.all_customers.size # Total count
|
|
152
|
+
Customer.premium_customers.range(0, 9) # Top 10 premium
|
|
171
153
|
```
|
|
172
154
|
|
|
173
|
-
|
|
155
|
+
### Performance Optimization
|
|
174
156
|
|
|
175
|
-
**Conditional Scoring:**
|
|
176
157
|
```ruby
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```
|
|
158
|
+
# Bulk operations
|
|
159
|
+
team.add_members([user1, user2, user3])
|
|
180
160
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
#
|
|
184
|
-
customer.domains << domain
|
|
161
|
+
# Pagination
|
|
162
|
+
team.members.range(0, 19) # First 20
|
|
163
|
+
team.members.range(20, 39) # Next 20
|
|
185
164
|
|
|
186
|
-
#
|
|
187
|
-
|
|
165
|
+
# Direct ID access (no object loading)
|
|
166
|
+
team.members.to_a # Just IDs
|
|
167
|
+
team.member_instances # Load objects
|
|
188
168
|
```
|
|
189
169
|
|
|
190
|
-
|
|
170
|
+
## Best Practices
|
|
191
171
|
|
|
192
|
-
|
|
172
|
+
1. **Use bulk methods** for multiple additions: `add_domains([d1, d2, d3])`
|
|
173
|
+
2. **Paginate large collections**: `range(0, 19)` instead of loading all
|
|
174
|
+
3. **Leverage reverse methods**: `domain.customer_instances` for efficient loading
|
|
175
|
+
4. **Clean up on destroy**: Call `cleanup_relationships` before deletion
|
|
176
|
+
5. **Validate before adding**: Check capacity/eligibility in overridden methods
|
|
193
177
|
|
|
194
|
-
|
|
195
|
-
- **[Relationship Methods Guide](feature-relationships-methods.md)** - Complete method reference
|
|
196
|
-
- **[Feature System Guide](feature-system.md)** - Understanding Familia's feature architecture
|
|
197
|
-
- **[Implementation Guide](implementation.md)** - Production deployment and configuration patterns
|
|
178
|
+
## See Also
|
|
198
179
|
|
|
199
|
-
|
|
200
|
-
|
|
180
|
+
- [**Relationship Methods**](feature-relationships-methods.md) - Complete API reference
|
|
181
|
+
- [**Participation Guide**](feature-relationships-participation.md) - Deep dive into associations
|
|
182
|
+
- [**Indexing Guide**](feature-relationships-indexing.md) - Attribute lookup patterns
|