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.
Files changed (370) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +4 -9
  3. data/.github/workflows/code-smells.yml +64 -3
  4. data/.pre-commit-config.yaml +8 -6
  5. data/.reek.yml +10 -9
  6. data/.rubocop.yml +4 -0
  7. data/.talismanrc +5 -1
  8. data/CHANGELOG.rst +220 -112
  9. data/CLAUDE.md +28 -1
  10. data/Gemfile +1 -1
  11. data/Gemfile.lock +20 -17
  12. data/bin/try +16 -0
  13. data/bin/tryouts +16 -0
  14. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  15. data/docs/guides/encryption.md +486 -0
  16. data/docs/guides/feature-encrypted-fields.md +123 -7
  17. data/docs/guides/feature-expiration.md +161 -117
  18. data/docs/guides/feature-external-identifiers.md +415 -443
  19. data/docs/guides/feature-object-identifiers.md +400 -269
  20. data/docs/guides/feature-quantization.md +120 -6
  21. data/docs/guides/feature-relationships-indexing.md +318 -0
  22. data/docs/guides/feature-relationships-methods.md +146 -604
  23. data/docs/guides/feature-relationships-participation.md +263 -0
  24. data/docs/guides/feature-relationships.md +118 -136
  25. data/docs/guides/feature-system-devs.md +176 -693
  26. data/docs/guides/feature-system.md +119 -6
  27. data/docs/guides/feature-transient-fields.md +81 -0
  28. data/docs/guides/field-system.md +778 -0
  29. data/docs/guides/index.md +32 -15
  30. data/docs/guides/logging.md +187 -0
  31. data/docs/guides/optimized-loading.md +674 -0
  32. data/docs/guides/thread-safety-monitoring.md +61 -0
  33. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  34. data/docs/migrating/v2.0.0-pre22.md +241 -0
  35. data/docs/overview.md +7 -9
  36. data/docs/reference/api-technical.md +267 -320
  37. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  38. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  39. data/examples/autoloader/mega_customer.rb +2 -0
  40. data/examples/datatype_standalone.rb +4 -3
  41. data/examples/encrypted_fields.rb +2 -1
  42. data/examples/json_usage_patterns.rb +2 -0
  43. data/examples/relationships.rb +3 -0
  44. data/examples/safe_dump.rb +2 -1
  45. data/examples/sampling_demo.rb +53 -0
  46. data/examples/single_connection_transaction_confusions.rb +2 -1
  47. data/familia.gemspec +2 -1
  48. data/lib/familia/base.rb +2 -0
  49. data/lib/familia/connection/behavior.rb +2 -0
  50. data/lib/familia/connection/handlers.rb +2 -0
  51. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  52. data/lib/familia/connection/middleware.rb +34 -24
  53. data/lib/familia/connection/operation_core.rb +3 -2
  54. data/lib/familia/connection/operations.rb +2 -0
  55. data/lib/familia/connection/pipelined_core.rb +3 -3
  56. data/lib/familia/connection/transaction_core.rb +69 -2
  57. data/lib/familia/connection.rb +18 -3
  58. data/lib/familia/data_type/class_methods.rb +3 -1
  59. data/lib/familia/data_type/connection.rb +2 -0
  60. data/lib/familia/data_type/database_commands.rb +2 -0
  61. data/lib/familia/data_type/serialization.rb +79 -52
  62. data/lib/familia/data_type/settings.rb +2 -0
  63. data/lib/familia/data_type/types/counter.rb +2 -0
  64. data/lib/familia/data_type/types/hashkey.rb +7 -5
  65. data/lib/familia/data_type/types/listkey.rb +2 -0
  66. data/lib/familia/data_type/types/lock.rb +2 -0
  67. data/lib/familia/data_type/types/sorted_set.rb +7 -10
  68. data/lib/familia/data_type/types/stringkey.rb +24 -0
  69. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  70. data/lib/familia/data_type.rb +2 -0
  71. data/lib/familia/encryption/encrypted_data.rb +4 -2
  72. data/lib/familia/encryption/manager.rb +2 -0
  73. data/lib/familia/encryption/provider.rb +2 -0
  74. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  75. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  76. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  77. data/lib/familia/encryption/registry.rb +2 -0
  78. data/lib/familia/encryption/request_cache.rb +2 -0
  79. data/lib/familia/encryption.rb +9 -2
  80. data/lib/familia/errors.rb +2 -0
  81. data/lib/familia/features/autoloader.rb +2 -0
  82. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  83. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  84. data/lib/familia/features/encrypted_fields.rb +2 -2
  85. data/lib/familia/features/expiration/extensions.rb +3 -1
  86. data/lib/familia/features/expiration.rb +12 -4
  87. data/lib/familia/features/external_identifier.rb +62 -7
  88. data/lib/familia/features/object_identifier.rb +49 -0
  89. data/lib/familia/features/quantization.rb +3 -1
  90. data/lib/familia/features/relationships/README.md +3 -1
  91. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  92. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +138 -9
  93. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  94. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +97 -21
  95. data/lib/familia/features/relationships/indexing.rb +3 -0
  96. data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
  97. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  98. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  100. data/lib/familia/features/relationships/participation.rb +155 -69
  101. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  102. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  103. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  104. data/lib/familia/features/relationships.rb +5 -3
  105. data/lib/familia/features/safe_dump.rb +2 -0
  106. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  107. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  108. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  109. data/lib/familia/features/transient_fields.rb +2 -0
  110. data/lib/familia/features.rb +2 -0
  111. data/lib/familia/field_type.rb +3 -1
  112. data/lib/familia/horreum/connection.rb +17 -1
  113. data/lib/familia/horreum/database_commands.rb +8 -1
  114. data/lib/familia/horreum/definition.rb +16 -6
  115. data/lib/familia/horreum/management.rb +353 -52
  116. data/lib/familia/horreum/persistence.rb +179 -108
  117. data/lib/familia/horreum/related_fields.rb +2 -0
  118. data/lib/familia/horreum/serialization.rb +23 -4
  119. data/lib/familia/horreum/settings.rb +2 -0
  120. data/lib/familia/horreum/utils.rb +2 -0
  121. data/lib/familia/horreum.rb +15 -1
  122. data/lib/familia/identifier_extractor.rb +3 -1
  123. data/lib/familia/instrumentation.rb +156 -0
  124. data/lib/familia/json_serializer.rb +2 -0
  125. data/lib/familia/logging.rb +92 -32
  126. data/lib/familia/refinements/dear_json.rb +2 -0
  127. data/lib/familia/refinements/stylize_words.rb +2 -14
  128. data/lib/familia/refinements/time_literals.rb +2 -0
  129. data/lib/familia/refinements.rb +2 -0
  130. data/lib/familia/secure_identifier.rb +10 -2
  131. data/lib/familia/settings.rb +2 -0
  132. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  133. data/lib/familia/thread_safety/monitor.rb +328 -0
  134. data/lib/familia/utils.rb +13 -0
  135. data/lib/familia/verifiable_identifier.rb +3 -1
  136. data/lib/familia/version.rb +3 -1
  137. data/lib/familia.rb +31 -4
  138. data/lib/middleware/database_command_counter.rb +152 -0
  139. data/lib/middleware/database_logger.rb +295 -170
  140. data/lib/multi_result.rb +61 -31
  141. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  142. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  143. data/try/edge_cases/json_serialization_try.rb +2 -0
  144. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  145. data/try/edge_cases/race_conditions_try.rb +4 -0
  146. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  147. data/try/edge_cases/string_coercion_try.rb +2 -0
  148. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  149. data/try/features/count_any_edge_cases_try.rb +486 -0
  150. data/try/features/count_any_methods_try.rb +197 -0
  151. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  152. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  153. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  154. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  155. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  156. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  157. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  158. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  159. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  160. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  161. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  162. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  163. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  164. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  165. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  166. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  167. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  168. data/try/features/encryption/config_persistence_try.rb +4 -0
  169. data/try/features/encryption/core_try.rb +4 -0
  170. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  171. data/try/features/encryption/module_loading_try.rb +4 -0
  172. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  173. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  174. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  175. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  176. data/try/features/expiration/expiration_try.rb +4 -0
  177. data/try/features/external_identifier/external_identifier_try.rb +305 -8
  178. data/try/features/feature_dependencies_try.rb +2 -0
  179. data/try/features/feature_improvements_try.rb +2 -0
  180. data/try/features/field_groups_try.rb +2 -0
  181. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  182. data/try/features/object_identifier/object_identifier_try.rb +140 -0
  183. data/try/features/quantization/quantization_try.rb +4 -0
  184. data/try/features/real_feature_integration_try.rb +2 -0
  185. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  186. data/try/features/relationships/indexing_rebuild_try.rb +606 -0
  187. data/try/features/relationships/indexing_try.rb +2 -0
  188. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  189. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  190. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  191. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  192. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  193. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  194. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  195. data/try/features/relationships/relationships_api_changes_try.rb +2 -0
  196. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  197. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  198. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  199. data/try/features/relationships/relationships_performance_try.rb +4 -0
  200. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  201. data/try/features/relationships/relationships_try.rb +6 -4
  202. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  203. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  204. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  205. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  206. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  207. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  208. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  209. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  210. data/try/integration/connection/fiber_context_preservation_try.rb +4 -0
  211. data/try/integration/connection/handler_constraints_try.rb +4 -0
  212. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  213. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  214. data/try/integration/connection/operation_mode_guards_try.rb +4 -0
  215. data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
  216. data/try/integration/connection/pools_try.rb +4 -0
  217. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  218. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  219. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  220. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  221. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  222. data/try/integration/connection/transaction_modes_try.rb +4 -0
  223. data/try/integration/conventional_inheritance_try.rb +4 -0
  224. data/try/integration/create_method_try.rb +4 -0
  225. data/try/integration/cross_component_try.rb +4 -0
  226. data/try/integration/data_types/datatype_pipelines_try.rb +9 -3
  227. data/try/integration/data_types/datatype_transactions_try.rb +17 -7
  228. data/try/integration/database_consistency_try.rb +4 -0
  229. data/try/integration/familia_extended_try.rb +4 -0
  230. data/try/integration/familia_members_methods_try.rb +4 -0
  231. data/try/integration/models/customer_safe_dump_try.rb +4 -0
  232. data/try/integration/models/customer_try.rb +7 -3
  233. data/try/integration/models/datatype_base_try.rb +4 -0
  234. data/try/integration/models/familia_object_try.rb +4 -0
  235. data/try/integration/persistence_operations_try.rb +4 -0
  236. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  237. data/try/integration/save_methods_consistency_try.rb +241 -0
  238. data/try/integration/scenarios_try.rb +4 -0
  239. data/try/integration/secure_identifier_try.rb +4 -0
  240. data/try/integration/transaction_safety_core_try.rb +176 -0
  241. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  242. data/try/integration/verifiable_identifier_try.rb +4 -0
  243. data/try/investigation/pipeline_routing/README.md +228 -0
  244. data/try/performance/benchmarks_try.rb +4 -0
  245. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  246. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  247. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  248. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  249. data/try/support/debugging/debug_aad_process.rb +3 -0
  250. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  251. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  252. data/try/support/debugging/debug_context_aad.rb +3 -0
  253. data/try/support/debugging/debug_context_simple.rb +3 -0
  254. data/try/support/debugging/debug_cross_context.rb +3 -0
  255. data/try/support/debugging/debug_database_load.rb +3 -0
  256. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  257. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  258. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  259. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  260. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  261. data/try/support/debugging/debug_load_path.rb +3 -0
  262. data/try/support/debugging/debug_method_definition.rb +3 -0
  263. data/try/support/debugging/debug_method_resolution.rb +3 -0
  264. data/try/support/debugging/debug_minimal.rb +3 -0
  265. data/try/support/debugging/debug_provider.rb +3 -0
  266. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  267. data/try/support/debugging/debug_string_class.rb +3 -0
  268. data/try/support/debugging/debug_test.rb +3 -0
  269. data/try/support/debugging/debug_test_design.rb +3 -0
  270. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  271. data/try/support/debugging/provider_diagnostics.rb +4 -0
  272. data/try/support/helpers/test_cleanup.rb +4 -0
  273. data/try/support/helpers/test_helpers.rb +5 -0
  274. data/try/support/memory/memory_basic_test.rb +4 -0
  275. data/try/support/memory/memory_detailed_test.rb +4 -0
  276. data/try/support/memory/memory_search_for_string.rb +4 -0
  277. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  278. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  279. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  280. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  281. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  282. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  283. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  284. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  285. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  286. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  287. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  288. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  289. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  290. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  291. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  292. data/try/thread_safety/README.md +496 -0
  293. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  294. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  295. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  296. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  297. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  298. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  299. data/try/thread_safety/field_registration_race_try.rb +222 -0
  300. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  301. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  302. data/try/thread_safety/module_config_race_try.rb +175 -0
  303. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  304. data/try/unit/core/autoloader_try.rb +4 -0
  305. data/try/unit/core/base_enhancements_try.rb +4 -0
  306. data/try/unit/core/connection_try.rb +4 -0
  307. data/try/unit/core/errors_try.rb +4 -0
  308. data/try/unit/core/extensions_try.rb +4 -0
  309. data/try/unit/core/familia_logger_try.rb +2 -0
  310. data/try/unit/core/familia_try.rb +4 -0
  311. data/try/unit/core/middleware_sampling_try.rb +335 -0
  312. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  313. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  314. data/try/unit/core/middleware_try.rb +4 -0
  315. data/try/unit/core/settings_try.rb +4 -0
  316. data/try/unit/core/time_utils_try.rb +4 -0
  317. data/try/unit/core/tools_try.rb +4 -0
  318. data/try/unit/core/utils_try.rb +37 -0
  319. data/try/unit/data_types/boolean_try.rb +39 -22
  320. data/try/unit/data_types/counter_try.rb +4 -0
  321. data/try/unit/data_types/datatype_base_try.rb +4 -0
  322. data/try/unit/data_types/hash_try.rb +6 -2
  323. data/try/unit/data_types/list_try.rb +4 -0
  324. data/try/unit/data_types/lock_try.rb +4 -0
  325. data/try/unit/data_types/serialization_try.rb +386 -0
  326. data/try/unit/data_types/sorted_set_try.rb +4 -0
  327. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  328. data/try/unit/data_types/string_try.rb +4 -0
  329. data/try/unit/data_types/unsortedset_try.rb +4 -0
  330. data/try/unit/familia_resolve_class_try.rb +116 -0
  331. data/try/unit/horreum/auto_indexing_on_save_try.rb +5 -1
  332. data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
  333. data/try/unit/horreum/base_try.rb +4 -0
  334. data/try/unit/horreum/class_methods_try.rb +4 -0
  335. data/try/unit/horreum/commands_try.rb +4 -0
  336. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  337. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +6 -1
  338. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  339. data/try/unit/horreum/field_categories_try.rb +4 -0
  340. data/try/unit/horreum/field_definition_try.rb +4 -0
  341. data/try/unit/horreum/initialization_try.rb +4 -0
  342. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  343. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  344. data/try/unit/horreum/relations_try.rb +4 -0
  345. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  346. data/try/unit/horreum/serialization_try.rb +4 -0
  347. data/try/unit/horreum/settings_try.rb +4 -0
  348. data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
  349. data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -0
  350. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  351. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  352. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  353. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  354. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  355. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  356. data/try/unit/thread_safety_monitor_try.rb +149 -0
  357. metadata +69 -17
  358. data/.github/workflows/code-quality.yml +0 -138
  359. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
  360. data/changelog.d/20251011_203905_delano_next.rst +0 -30
  361. data/changelog.d/20251011_212633_delano_next.rst +0 -13
  362. data/changelog.d/20251011_221253_delano_next.rst +0 -26
  363. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  364. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  365. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  366. data/docs/archive/README.md +0 -64
  367. data/docs/archive/api-reference.md +0 -333
  368. data/docs/guides/core-field-system.md +0 -806
  369. data/docs/guides/implementation.md +0 -276
  370. data/docs/guides/security-model.md +0 -183
@@ -0,0 +1,606 @@
1
+ # try/features/relationships/indexing_rebuild_try.rb
2
+ #
3
+ # Comprehensive tests for index rebuild functionality
4
+ # Tests both unique_index (class-level and instance-scoped) and multi_index rebuild
5
+
6
+ require_relative '../../support/helpers/test_helpers'
7
+
8
+ Familia.enable_database_logging = true
9
+
10
+ # Test classes for class-level unique index rebuild
11
+ class ::RebuildTestUser < Familia::Horreum
12
+ feature :relationships
13
+
14
+ identifier_field :user_id
15
+ field :user_id
16
+ field :email
17
+ field :username
18
+ field :department
19
+
20
+ unique_index :email, :email_lookup
21
+ unique_index :username, :username_lookup
22
+
23
+ class_sorted_set :instances, reference: true
24
+ end
25
+
26
+ # Test classes for instance-scoped unique index rebuild
27
+ # Define Company first so Employee can reference it
28
+ class ::RebuildTestCompany < Familia::Horreum
29
+ feature :relationships
30
+
31
+ identifier_field :company_id
32
+ field :company_id
33
+ field :name
34
+
35
+ sorted_set :employees
36
+
37
+ class_sorted_set :instances, reference: true
38
+ end
39
+
40
+ class ::RebuildTestEmployee < Familia::Horreum
41
+ feature :relationships
42
+
43
+ identifier_field :emp_id
44
+ field :emp_id
45
+ field :email
46
+ field :badge_number
47
+ field :department
48
+
49
+ unique_index :badge_number, :badge_index, within: RebuildTestCompany
50
+ multi_index :department, :dept_index, within: RebuildTestCompany
51
+
52
+ participates_in RebuildTestCompany, :employees, score: :emp_id
53
+
54
+ class_sorted_set :instances, reference: true
55
+ end
56
+
57
+ # Setup: Create test data for class-level unique index
58
+ @user1 = RebuildTestUser.new(user_id: "user_1", email: "user1@test.com", username: "user1")
59
+ @user1.save
60
+ @user2 = RebuildTestUser.new(user_id: "user_2", email: "user2@test.com", username: "user2")
61
+ @user2.save
62
+ @user3 = RebuildTestUser.new(user_id: "user_3", email: "user3@test.com", username: "user3")
63
+ @user3.save
64
+
65
+ # Setup: Create test data for instance-scoped indexes
66
+ @company = RebuildTestCompany.new(company_id: "company_1", name: "Acme Corp")
67
+ @company.save
68
+
69
+ @emp1 = RebuildTestEmployee.new(emp_id: "emp_1", email: "emp1@acme.com", badge_number: "BADGE001", department: "engineering")
70
+ @emp1.save
71
+ @emp1.add_to_rebuild_test_company_employees(@company)
72
+
73
+ @emp2 = RebuildTestEmployee.new(emp_id: "emp_2", email: "emp2@acme.com", badge_number: "BADGE002", department: "sales")
74
+ @emp2.save
75
+ @emp2.add_to_rebuild_test_company_employees(@company)
76
+
77
+ @emp3 = RebuildTestEmployee.new(emp_id: "emp_3", email: "emp3@acme.com", badge_number: "BADGE003", department: "engineering")
78
+ @emp3.save
79
+ @emp3.add_to_rebuild_test_company_employees(@company)
80
+
81
+ # =============================================
82
+ # 1. Class-Level Unique Index Rebuild Tests
83
+ # =============================================
84
+
85
+ ## Class-level rebuild method exists
86
+ RebuildTestUser.respond_to?(:rebuild_email_lookup)
87
+ #=> true
88
+
89
+ ## Index starts empty before rebuild
90
+ RebuildTestUser.email_lookup.clear
91
+ RebuildTestUser.email_lookup.size
92
+ #=> 0
93
+
94
+ ## Rebuild returns count of indexed objects
95
+ count = RebuildTestUser.rebuild_email_lookup
96
+ count
97
+ #=> 3
98
+
99
+ ## Index size matches object count after rebuild
100
+ RebuildTestUser.email_lookup.size
101
+ #=> 3
102
+
103
+ ## Find by email works after rebuild
104
+ found = RebuildTestUser.find_by_email("user1@test.com")
105
+ found&.user_id
106
+ #=> "user_1"
107
+
108
+ ## All users are findable after rebuild
109
+ found = RebuildTestUser.find_by_email("user2@test.com")
110
+ found&.user_id
111
+ #=> "user_2"
112
+
113
+ ## Third user is findable after rebuild
114
+ found = RebuildTestUser.find_by_email("user3@test.com")
115
+ found&.user_id
116
+ #=> "user_3"
117
+
118
+ ## Bulk query works after rebuild
119
+ emails = ["user1@test.com", "user3@test.com"]
120
+ found_users = RebuildTestUser.find_all_by_email(emails)
121
+ found_users.map(&:user_id).sort
122
+ #=> ["user_1", "user_3"]
123
+
124
+ ## Rebuild can be called multiple times
125
+ count = RebuildTestUser.rebuild_email_lookup
126
+ count
127
+ #=> 3
128
+
129
+ ## Index remains consistent after multiple rebuilds
130
+ RebuildTestUser.email_lookup.size
131
+ #=> 3
132
+
133
+ ## Rebuild for second unique index works independently
134
+ RebuildTestUser.username_lookup.clear
135
+ count = RebuildTestUser.rebuild_username_lookup
136
+ count
137
+ #=> 3
138
+
139
+ ## Second index is populated correctly
140
+ found = RebuildTestUser.find_by_username("user2")
141
+ found&.user_id
142
+ #=> "user_2"
143
+
144
+ # =============================================
145
+ # 2. Class-Level Index Rebuild Edge Cases
146
+ # =============================================
147
+
148
+ ## Rebuild with nil field values skips those objects
149
+ @user_nil = RebuildTestUser.new(user_id: "user_nil", email: nil, username: "user_nil")
150
+ @user_nil.save
151
+ RebuildTestUser.email_lookup.clear
152
+ count = RebuildTestUser.rebuild_email_lookup
153
+ count
154
+ #=> 3
155
+
156
+ ## Nil field values are not in index
157
+ RebuildTestUser.find_by_email("")
158
+ #=> nil
159
+
160
+ ## Non-nil fields are still indexed for object with nil field
161
+ found = RebuildTestUser.find_by_username("user_nil")
162
+ found&.user_id
163
+ #=> "user_nil"
164
+
165
+ ## Rebuild with empty string field values skips those objects
166
+ @user_empty = RebuildTestUser.new(user_id: "user_empty", email: "", username: "user_empty")
167
+ @user_empty.save
168
+ RebuildTestUser.email_lookup.clear
169
+ count = RebuildTestUser.rebuild_email_lookup
170
+ count
171
+ #=> 3
172
+
173
+ ## Empty string field values are not in index
174
+ RebuildTestUser.find_by_email("")
175
+ #=> nil
176
+
177
+ ## Rebuild with whitespace-only field values skips those objects
178
+ @user_whitespace = RebuildTestUser.new(user_id: "user_ws", email: " ", username: "user_ws")
179
+ @user_whitespace.save
180
+ RebuildTestUser.email_lookup.clear
181
+ count = RebuildTestUser.rebuild_email_lookup
182
+ count
183
+ #=> 3
184
+
185
+ ## Whitespace-only field values are not in index
186
+ RebuildTestUser.find_by_email(" ")
187
+ #=> nil
188
+
189
+ ## Rebuild handles stale object IDs gracefully
190
+ RebuildTestUser.instances.add("stale_user_id")
191
+ RebuildTestUser.email_lookup.clear
192
+ count = RebuildTestUser.rebuild_email_lookup
193
+ count
194
+ #=> 3
195
+
196
+ ## Index size is correct despite stale ID
197
+ RebuildTestUser.email_lookup.size
198
+ #=> 3
199
+
200
+ ## Rebuild with no instances returns zero
201
+ RebuildTestUser.instances.clear
202
+ RebuildTestUser.email_lookup.clear
203
+ count = RebuildTestUser.rebuild_email_lookup
204
+ count
205
+ #=> 0
206
+
207
+ ## Index is empty after rebuilding with no instances
208
+ RebuildTestUser.email_lookup.size
209
+ #=> 0
210
+
211
+ # Restore instances for remaining tests
212
+ ## Check instances key
213
+ RebuildTestUser.instances.dbkey
214
+ #=> "rebuild_test_user:instances"
215
+
216
+ ## Manually populate instances for testing
217
+ result1 = RebuildTestUser.instances.add("user_1")
218
+ result2 = RebuildTestUser.instances.add("user_2")
219
+ result3 = RebuildTestUser.instances.add("user_3")
220
+ [result1, result2, result3]
221
+ #=> [true, true, true]
222
+
223
+ ## Instances restored successfully
224
+ RebuildTestUser.instances.size
225
+ #=> 3
226
+
227
+ # =============================================
228
+ # 3. Instance-Scoped Unique Index Rebuild
229
+ # =============================================
230
+
231
+ ## Instance-scoped rebuild method exists
232
+ @company.respond_to?(:rebuild_badge_index)
233
+ #=> true
234
+
235
+ ## Instance-scoped index starts empty before rebuild
236
+ @company.badge_index.clear
237
+ @company.badge_index.size
238
+ #=> 0
239
+
240
+ ## Instance-scoped rebuild returns count
241
+ count = @company.rebuild_badge_index
242
+ count
243
+ #=> 3
244
+
245
+ ## Instance-scoped index size matches after rebuild
246
+ @company.badge_index.size
247
+ #=> 3
248
+
249
+ ## Find by badge works after instance-scoped rebuild
250
+ found = @company.find_by_badge_number("BADGE001")
251
+ found&.emp_id
252
+ #=> "emp_1"
253
+
254
+ ## All employees findable after instance-scoped rebuild
255
+ found = @company.find_by_badge_number("BADGE002")
256
+ found&.emp_id
257
+ #=> "emp_2"
258
+
259
+ ## Third employee findable after instance-scoped rebuild
260
+ found = @company.find_by_badge_number("BADGE003")
261
+ found&.emp_id
262
+ #=> "emp_3"
263
+
264
+ ## Bulk query works after instance-scoped rebuild
265
+ badges = ["BADGE001", "BADGE003"]
266
+ found_emps = @company.find_all_by_badge_number(badges)
267
+ found_emps.map(&:emp_id).sort
268
+ #=> ["emp_1", "emp_3"]
269
+
270
+ ## Instance-scoped rebuild only indexes employees in that company
271
+ @company2 = RebuildTestCompany.new(company_id: "company_2", name: "TechCo")
272
+ @company2.save
273
+ @company2.badge_index.clear
274
+ count = @company2.rebuild_badge_index
275
+ count
276
+ #=> 0
277
+
278
+ ## Second company has empty index after rebuild
279
+ @company2.badge_index.size
280
+ #=> 0
281
+
282
+ ## First company still has correct index
283
+ @company.badge_index.size
284
+ #=> 3
285
+
286
+ # =============================================
287
+ # 4. Multi-Value Index Rebuild
288
+ # =============================================
289
+
290
+ ## Multi-index rebuild method exists
291
+ @company.respond_to?(:rebuild_dept_index)
292
+ #=> true
293
+
294
+ ## Multi-index starts empty before rebuild
295
+ engineering_set = @company.dept_index_for("engineering")
296
+ engineering_set.clear
297
+ sales_set = @company.dept_index_for("sales")
298
+ sales_set.clear
299
+ engineering_set.size
300
+ #=> 0
301
+
302
+ ## Multi-index rebuild returns processed count
303
+ count = @company.rebuild_dept_index
304
+ count
305
+ #=> 3
306
+
307
+ ## Manually populate departments to test structure
308
+ @emp1.add_to_rebuild_test_company_dept_index(@company)
309
+ @emp2.add_to_rebuild_test_company_dept_index(@company)
310
+ @emp3.add_to_rebuild_test_company_dept_index(@company)
311
+ engineering_count = @company.dept_index_for("engineering").size
312
+ engineering_count
313
+ #=> 2
314
+
315
+ ## Sales department has correct count
316
+ sales_count = @company.dept_index_for("sales").size
317
+ sales_count
318
+ #=> 1
319
+
320
+ ## Find all by department works for engineering
321
+ eng_emps = @company.find_all_by_department("engineering")
322
+ eng_emps.map(&:emp_id).sort
323
+ #=> ["emp_1", "emp_3"]
324
+
325
+ ## Find all by department works for sales
326
+ sales_emps = @company.find_all_by_department("sales")
327
+ sales_emps.map(&:emp_id)
328
+ #=> ["emp_2"]
329
+
330
+ ## Sample from department returns employees
331
+ sample = @company.sample_from_department("engineering", 1)
332
+ ["emp_1", "emp_3"].include?(sample.first&.emp_id)
333
+ #=> true
334
+
335
+ # =============================================
336
+ # 5. Rebuild Progress Callbacks
337
+ # =============================================
338
+
339
+ ## Instances collection has users before rebuild
340
+ RebuildTestUser.instances.size
341
+ #=> 3
342
+
343
+ ## Rebuild accepts batch_size parameter
344
+ RebuildTestUser.email_lookup.clear
345
+ count = RebuildTestUser.rebuild_email_lookup(batch_size: 1)
346
+ count
347
+ #=> 3
348
+
349
+ ## Index works correctly with small batch size
350
+ found = RebuildTestUser.find_by_email("user1@test.com")
351
+ found&.user_id
352
+ #=> "user_1"
353
+
354
+ ## Rebuild accepts large batch_size parameter
355
+ RebuildTestUser.email_lookup.clear
356
+ count = RebuildTestUser.rebuild_email_lookup(batch_size: 1000)
357
+ count
358
+ #=> 3
359
+
360
+ ## Index works correctly with large batch size
361
+ found = RebuildTestUser.find_by_email("user2@test.com")
362
+ found&.user_id
363
+ #=> "user_2"
364
+
365
+ ## Rebuild accepts progress callback block
366
+ @progress_updates = []
367
+ RebuildTestUser.email_lookup.clear
368
+ count = RebuildTestUser.rebuild_email_lookup { |progress| @progress_updates << progress }
369
+ count
370
+ #=> 3
371
+
372
+ ## Progress callback receives updates
373
+ @progress_updates.size > 0
374
+ #=> true
375
+
376
+ ## Progress callback includes completed count
377
+ @progress_updates.last[:completed]
378
+ #=> 3
379
+
380
+ ## Progress callback includes total count
381
+ @progress_updates.last[:total]
382
+ #=> 3
383
+
384
+ ## Progress callback includes rate information
385
+ @progress_updates.last.key?(:rate)
386
+ #=> true
387
+
388
+ ## Progress updates are incremental
389
+ completed_values = @progress_updates.map { |p| p[:completed] }
390
+ completed_values.sort == completed_values
391
+ #=> true
392
+
393
+ # =============================================
394
+ # 6. Rebuild with Modified Data
395
+ # =============================================
396
+
397
+ ## Rebuild reflects updated field values
398
+ @user1.email = "updated1@test.com"
399
+ @user1.save
400
+ RebuildTestUser.email_lookup.clear
401
+ RebuildTestUser.rebuild_email_lookup
402
+ found = RebuildTestUser.find_by_email("updated1@test.com")
403
+ found&.user_id
404
+ #=> "user_1"
405
+
406
+ ## Old email is not in index after rebuild
407
+ RebuildTestUser.find_by_email("user1@test.com")
408
+ #=> nil
409
+
410
+ ## Rebuild after deleting object removes from index
411
+ @user3.destroy if @user3.respond_to?(:destroy)
412
+ RebuildTestUser.instances.remove(@user3.identifier)
413
+ RebuildTestUser.email_lookup.clear
414
+ count = RebuildTestUser.rebuild_email_lookup
415
+ count
416
+ #=> 2
417
+
418
+ ## Deleted object not findable after rebuild
419
+ RebuildTestUser.find_by_email("user3@test.com")
420
+ #=> nil
421
+
422
+ ## Remaining objects still findable
423
+ found = RebuildTestUser.find_by_email("user2@test.com")
424
+ found&.user_id
425
+ #=> "user_2"
426
+
427
+ # =============================================
428
+ # 7. Instance-Scoped Rebuild with Batch Sizes
429
+ # =============================================
430
+
431
+ ## Instance-scoped rebuild accepts batch_size
432
+ @company.badge_index.clear
433
+ count = @company.rebuild_badge_index(batch_size: 1)
434
+ count
435
+ #=> 3
436
+
437
+ ## Instance-scoped index works with small batch
438
+ found = @company.find_by_badge_number("BADGE001")
439
+ found&.emp_id
440
+ #=> "emp_1"
441
+
442
+ ## Instance-scoped rebuild with large batch_size
443
+ @company.badge_index.clear
444
+ count = @company.rebuild_badge_index(batch_size: 100)
445
+ count
446
+ #=> 3
447
+
448
+ ## Instance-scoped index works with large batch
449
+ found = @company.find_by_badge_number("BADGE002")
450
+ found&.emp_id
451
+ #=> "emp_2"
452
+
453
+ # =============================================
454
+ # 8. Concurrent Rebuilds (Thread Safety)
455
+ # =============================================
456
+
457
+ ## Multiple rebuilds don't corrupt index
458
+ RebuildTestUser.email_lookup.clear
459
+ counts = 3.times.map { RebuildTestUser.rebuild_email_lookup }
460
+ counts
461
+ #=> [2, 2, 2]
462
+
463
+ ## Index remains consistent after concurrent rebuilds
464
+ RebuildTestUser.email_lookup.size
465
+ #=> 2
466
+
467
+ ## All expected objects findable after concurrent rebuilds
468
+ found1 = RebuildTestUser.find_by_email("updated1@test.com")
469
+ found2 = RebuildTestUser.find_by_email("user2@test.com")
470
+ [found1&.user_id, found2&.user_id].sort
471
+ #=> ["user_1", "user_2"]
472
+
473
+ # =============================================
474
+ # 9. Orphaned Data Cleanup (SCAN-based)
475
+ # =============================================
476
+
477
+ ## Clear all dept indexes from earlier tests
478
+ ["engineering", "sales", "marketing", "finance"].each do |dept|
479
+ @company.dept_index_for(dept).clear rescue nil
480
+ end
481
+
482
+ ## Manually create orphaned stale data in finance dept
483
+ @company.dept_index_for("finance").add("emp_1")
484
+ @company.dept_index_for("finance").add("emp_2")
485
+ @company.dept_index_for("finance").size
486
+ #=> 2
487
+
488
+ ## Also add some marketing entries (will be orphaned)
489
+ @company.dept_index_for("marketing").add("emp_1")
490
+ @company.dept_index_for("marketing").add("emp_3")
491
+ @company.dept_index_for("marketing").size
492
+ #=> 2
493
+
494
+ ## Rebuild via participation collection processes 3 employees
495
+ @company.rebuild_dept_index
496
+ #=> 3
497
+
498
+ ## After rebuild: Current engineering dept correctly has both emp1 and emp3
499
+ @company.dept_index_for("engineering").size
500
+ #=> 2
501
+
502
+ ## After rebuild: Current sales dept correctly has emp2
503
+ @company.dept_index_for("sales").size
504
+ #=> 1
505
+
506
+ ## SCAN cleanup removes orphaned finance keys
507
+ @company.dept_index_for("finance").size
508
+ #=> 0
509
+
510
+ ## SCAN cleanup removes orphaned marketing keys
511
+ @company.dept_index_for("marketing").size
512
+ #=> 0
513
+
514
+ # =============================================
515
+ # 10. Scope Filtering (SCAN Strategy)
516
+ # =============================================
517
+
518
+ ## Company 1 has 3 employees
519
+ @company.employees.size
520
+ #=> 3
521
+
522
+ ## Clear company 1 badge index to force SCAN strategy
523
+ @company.badge_index.clear
524
+ @company.badge_index.size
525
+ #=> 0
526
+
527
+ ## Company has 3 employees participating
528
+ @company.employees.size
529
+ #=> 3
530
+
531
+ ## Rebuild company 1 index via SCAN - should filter to only company 1's employees
532
+ count = @company.rebuild_badge_index
533
+ count
534
+ #=> 3
535
+
536
+ ## Company 1 index only has its own employees (scope filtering verified)
537
+ @company.badge_index.size
538
+ #=> 3
539
+
540
+ ## All expected employees found via index
541
+ found1 = @company.find_by_badge_number("BADGE001")
542
+ found2 = @company.find_by_badge_number("BADGE002")
543
+ found3 = @company.find_by_badge_number("BADGE003")
544
+ [found1&.emp_id, found2&.emp_id, found3&.emp_id]
545
+ #=> ["emp_1", "emp_2", "emp_3"]
546
+
547
+ # =============================================
548
+ # 11. Cardinality Guard Protection
549
+ # =============================================
550
+
551
+ ## Cardinality guard prevents multi-index corruption
552
+ # Note: This would require manually calling the private method with wrong cardinality
553
+ # The architecture prevents this via factory pattern, but guard provides explicit protection
554
+ begin
555
+ # Simulate calling rebuild_via_participation with multi-index cardinality
556
+ # Note: dept_index is a multi-index, so we need to use dept_index_for to get a DataType instance
557
+ # But for this test, we just need any HashKey-like object to pass for serialization
558
+ index_hashkey = @company.badge_index # Use a valid HashKey index
559
+ Familia::Features::Relationships::Indexing::RebuildStrategies.rebuild_via_participation(
560
+ @company,
561
+ RebuildTestEmployee,
562
+ :department,
563
+ :add_to_rebuild_test_company_dept_index,
564
+ @company.employees,
565
+ :multi, # Wrong cardinality!
566
+ index_hashkey, # Added required parameter
567
+ batch_size: 100
568
+ )
569
+ "should have raised"
570
+ rescue ArgumentError => e
571
+ e.message.include?("only supports unique indexes")
572
+ end
573
+ #=> true
574
+
575
+ ## Guard accepts correct cardinality (:unique)
576
+ begin
577
+ index_config = RebuildTestEmployee.indexing_relationships.find { |r| r.index_name == :badge_index }
578
+ index_hashkey = @company.badge_index # Get the index HashKey for serialization
579
+ Familia::Features::Relationships::Indexing::RebuildStrategies.rebuild_via_participation(
580
+ @company,
581
+ RebuildTestEmployee,
582
+ :badge_number,
583
+ :add_to_rebuild_test_company_badge_index,
584
+ @company.employees,
585
+ :unique, # Correct cardinality
586
+ index_hashkey, # Added required parameter
587
+ batch_size: 100
588
+ )
589
+ "no error"
590
+ rescue ArgumentError
591
+ "should not raise"
592
+ end
593
+ #=> "no error"
594
+
595
+ # Teardown
596
+ RebuildTestUser.email_lookup.clear
597
+ RebuildTestUser.username_lookup.clear
598
+ RebuildTestUser.instances.clear
599
+ @company.badge_index.clear
600
+ @company.employees.clear
601
+ # Clear all department index keys
602
+ ["engineering", "sales", "marketing", "finance"].each do |dept|
603
+ @company.dept_index_for(dept).clear rescue nil
604
+ end
605
+ RebuildTestCompany.instances.clear
606
+ RebuildTestEmployee.instances.clear
@@ -1,5 +1,7 @@
1
1
  # try/features/relationships/indexing_try.rb
2
2
  #
3
+ # frozen_string_literal: true
4
+
3
5
  # Comprehensive tests for Familia indexing relationships functionality
4
6
  # Tests both multi_index (parent-context) and unique_index (class-level) indexing
5
7
  #