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
@@ -1,5 +1,7 @@
1
1
  # try/integration/relationships_persistence_round_trip_try.rb
2
2
  #
3
+ # frozen_string_literal: true
4
+
3
5
  # CRITICAL PRIORITY: Full Persistence Round-Trip Testing for Relationships
4
6
  #
5
7
  # PURPOSE:
@@ -113,22 +115,23 @@ class ::RTPDomain < Familia::Horreum
113
115
  end
114
116
 
115
117
  # Setup - create test data with known values
116
- @test_user_id = "rtp_user_#{Familia.now.to_i}"
117
- @test_email = "roundtrip@example.com"
118
- @test_name = "Alice Roundtrip"
118
+ @test_run_id = Familia.now.to_i
119
+ @test_user_id = "rtp_user_#{@test_run_id}"
120
+ @test_email = "roundtrip-#{@test_run_id}@example.com"
121
+ @test_name = "Alice Roundtrip #{@test_run_id}"
119
122
  @test_age = 30
120
123
 
121
- @test_company_id = "rtp_comp_#{Familia.now.to_i}"
124
+ @test_company_id = "rtp_comp_#{@test_run_id}"
122
125
  @test_company_name = "Acme Corp"
123
126
  @test_industry = "Technology"
124
127
 
125
- @test_emp_id = "rtp_emp_#{Familia.now.to_i}"
126
- @test_emp_email = "employee@acme.com"
128
+ @test_emp_id = "rtp_emp_#{@test_run_id}"
129
+ @test_emp_email = "employee-#{@test_run_id}@acme.com"
127
130
  @test_department = "engineering"
128
- @test_badge = "BADGE_RTP_001"
131
+ @test_badge = "BADGE_RTP_#{@test_run_id}"
129
132
  @test_hire_date = Time.now.to_i
130
133
 
131
- @test_domain_id = "rtp_dom_#{Familia.now.to_i}"
134
+ @test_domain_id = "rtp_dom_#{@test_run_id}"
132
135
  @test_domain_name = "example.com"
133
136
  @test_domain_created = Familia.now.to_i
134
137
 
@@ -291,10 +294,10 @@ RTPEmployee.exists?(@test_emp_id)
291
294
 
292
295
  ## Multiple employees in same department
293
296
  @emp2_id = "rtp_emp2_#{Familia.now.to_i}"
294
- @emp2_badge = "BADGE_RTP_002"
297
+ @emp2_badge = "BADGE_RTP_002_#{@test_run_id}"
295
298
  @emp2 = RTPEmployee.new(
296
299
  emp_id: @emp2_id,
297
- email: "emp2@acme.com",
300
+ email: "emp2-#{@test_run_id}@acme.com",
298
301
  department: @test_department,
299
302
  badge_number: @emp2_badge
300
303
  )
@@ -323,7 +326,7 @@ RTPDomain.exists?(@test_domain_id)
323
326
  #=> true
324
327
 
325
328
  ## Add domain to company participation collection
326
- @company.add_domain(@domain)
329
+ @company.add_domains_instance(@domain)
327
330
  @company.domains.size
328
331
  #=> 1
329
332
 
@@ -377,7 +380,7 @@ RTPDomain.all_domains.size
377
380
  #=> @new_age
378
381
 
379
382
  ## Update email and verify index updates
380
- @new_email = "newemail@example.com"
383
+ @new_email = "newemail-#{@test_run_id}@example.com"
381
384
  @old_email = @user.email
382
385
  @user.email = @new_email
383
386
  @user.save
@@ -396,8 +399,8 @@ RTPUser.find_by_email(@old_email)
396
399
  ## User with nil field saves correctly
397
400
  @user_nil_age = RTPUser.new(
398
401
  user_id: "rtp_nil_#{Familia.now.to_i}",
399
- email: "nil@example.com",
400
- name: "Nil Tester",
402
+ email: "nil-#{@test_run_id}@example.com",
403
+ name: "Nil Tester #{@test_run_id}",
401
404
  age: nil
402
405
  )
403
406
  @user_nil_age.save
@@ -0,0 +1,241 @@
1
+ # try/integration/save_methods_consistency_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Test coverage for save and save_if_not_exists consistency improvements
6
+ #
7
+ # This test verifies that both save and save_if_not_exists! produce identical
8
+ # results when creating new objects, including:
9
+ # - Timestamp updates (created/updated)
10
+ # - Unique index validation
11
+ # - Class-level index updates
12
+ # - Instance collection tracking
13
+
14
+ require_relative '../support/helpers/test_helpers'
15
+
16
+ # Model with timestamps to verify timestamp handling
17
+ class TimestampedModel < Familia::Horreum
18
+ identifier_field :id
19
+ field :id
20
+ field :name
21
+ field :created
22
+ field :updated
23
+
24
+ zset :instances
25
+ end
26
+
27
+ # Model with unique indexes to verify validation
28
+ class UniqueIndexModel < Familia::Horreum
29
+ feature :relationships
30
+ include Familia::Features::Relationships::Indexing
31
+
32
+ identifier_field :id
33
+ field :id
34
+ field :email
35
+
36
+ unique_index :email, :email_lookup
37
+ zset :instances
38
+ end
39
+
40
+ # Clean up any existing test data
41
+ cleanup_keys = Familia.dbclient.keys('timestampedmodel:*') +
42
+ Familia.dbclient.keys('uniqueindexmodel:*') +
43
+ Familia.dbclient.keys('*:email_lookup:*')
44
+ Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
45
+
46
+ @test_counter = 0
47
+ def next_id
48
+ @test_counter += 1
49
+ "test-#{Familia.now.to_i}-#{@test_counter}"
50
+ end
51
+
52
+ # =============================================
53
+ # 1. Timestamp Consistency Tests
54
+ # =============================================
55
+
56
+ ## save sets created and updated timestamps
57
+ @save_model = TimestampedModel.new(id: next_id, name: 'Save Test')
58
+ @save_model.save
59
+ [@save_model.created.nil?, @save_model.updated.nil?]
60
+ #=> [false, false]
61
+
62
+ ## save_if_not_exists! sets created and updated timestamps
63
+ @sine_model = TimestampedModel.new(id: next_id, name: 'SINE Test')
64
+ @sine_model.save_if_not_exists!
65
+ [@sine_model.created.nil?, @sine_model.updated.nil?]
66
+ #=> [false, false]
67
+
68
+ ## Both methods set timestamps to the same approximate time
69
+ time_diff = (@save_model.created - @save_model.updated).abs
70
+ time_diff < 1 # Should be within 1 second
71
+ #=> true
72
+
73
+ ## save_if_not_exists! timestamps match save behavior
74
+ sine_time_diff = (@sine_model.created - @sine_model.updated).abs
75
+ sine_time_diff < 1
76
+ #=> true
77
+
78
+ # =============================================
79
+ # 2. Instance Collection Consistency
80
+ # =============================================
81
+
82
+ ## save adds object to instances collection
83
+ @inst_save = TimestampedModel.new(id: next_id, name: 'Instance Save')
84
+ @inst_save.save
85
+ TimestampedModel.instances.members.include?(@inst_save.identifier)
86
+ #=> true
87
+
88
+ ## save_if_not_exists! adds object to instances collection
89
+ @inst_sine = TimestampedModel.new(id: next_id, name: 'Instance SINE')
90
+ @inst_sine.save_if_not_exists!
91
+ TimestampedModel.instances.members.include?(@inst_sine.identifier)
92
+ #=> true
93
+
94
+ ## Both methods produce identical instance collection state
95
+ [@inst_save, @inst_sine].all? { |obj| TimestampedModel.instances.members.include?(obj.identifier) }
96
+ #=> true
97
+
98
+ # =============================================
99
+ # 3. Unique Index Validation Consistency
100
+ # =============================================
101
+
102
+ ## save validates unique indexes
103
+ @unique_email_1 = "save-#{next_id}@test.com"
104
+ @unique_save = UniqueIndexModel.new(id: next_id, email: @unique_email_1)
105
+ @unique_save.save
106
+ @unique_save.exists?
107
+ #=> true
108
+
109
+ ## save raises RecordExistsError for duplicate unique index
110
+ @unique_dup_save = UniqueIndexModel.new(id: next_id, email: @unique_email_1)
111
+ @unique_dup_save.save
112
+ #=!> Familia::RecordExistsError
113
+
114
+ ## save_if_not_exists! validates unique indexes
115
+ @unique_email_2 = "sine-#{next_id}@test.com"
116
+ @unique_sine = UniqueIndexModel.new(id: next_id, email: @unique_email_2)
117
+ @unique_sine.save_if_not_exists!
118
+ @unique_sine.exists?
119
+ #=> true
120
+
121
+ ## save_if_not_exists! raises RecordExistsError for duplicate unique index
122
+ @unique_dup_sine = UniqueIndexModel.new(id: next_id, email: @unique_email_2)
123
+ @unique_dup_sine.save_if_not_exists!
124
+ #=!> Familia::RecordExistsError
125
+
126
+ # =============================================
127
+ # 4. Return Value Consistency
128
+ # =============================================
129
+
130
+ ## save returns true for successful save
131
+ @ret_save = TimestampedModel.new(id: next_id, name: 'Return Test Save')
132
+ result_save = @ret_save.save
133
+ result_save
134
+ #=> true
135
+
136
+ ## save_if_not_exists! returns true for successful save
137
+ @ret_sine = TimestampedModel.new(id: next_id, name: 'Return Test SINE')
138
+ result_sine = @ret_sine.save_if_not_exists!
139
+ result_sine
140
+ #=> true
141
+
142
+ ## save_if_not_exists returns true for new object
143
+ @ret_sine_safe = TimestampedModel.new(id: next_id, name: 'Return Safe SINE')
144
+ result = @ret_sine_safe.save_if_not_exists
145
+ result
146
+ #=> true
147
+
148
+ ## save_if_not_exists returns false for existing object
149
+ @ret_existing = TimestampedModel.new(id: next_id, name: 'Existing')
150
+ @ret_existing.save
151
+ @ret_dup = TimestampedModel.new(id: @ret_existing.identifier, name: 'Duplicate')
152
+ result = @ret_dup.save_if_not_exists
153
+ result
154
+ #=> false
155
+
156
+ # =============================================
157
+ # 5. Data Persistence Consistency
158
+ # =============================================
159
+
160
+ ## save persists all fields
161
+ @data_save = TimestampedModel.new(id: next_id, name: 'Data Save Test')
162
+ @data_save.save
163
+ @data_save.refresh
164
+ @data_save.name
165
+ #=> 'Data Save Test'
166
+
167
+ ## save_if_not_exists! persists all fields
168
+ @data_sine = TimestampedModel.new(id: next_id, name: 'Data SINE Test')
169
+ @data_sine.save_if_not_exists!
170
+ @data_sine.refresh
171
+ @data_sine.name
172
+ #=> 'Data SINE Test'
173
+
174
+ ## Both methods produce identical persistence
175
+ [@data_save.name, @data_sine.name]
176
+ #=> ['Data Save Test', 'Data SINE Test']
177
+
178
+ # =============================================
179
+ # 6. Expiration Handling Consistency
180
+ # =============================================
181
+
182
+ ## save with update_expiration: true handles TTL
183
+ @exp_save = TimestampedModel.new(id: next_id, name: 'Exp Save')
184
+ @exp_save.save(update_expiration: true)
185
+ # No default expiration set, so TTL is -1 (no expiration)
186
+ @exp_save.ttl == -1
187
+ #=> true
188
+
189
+ ## save_if_not_exists! with update_expiration: true handles TTL
190
+ @exp_sine = TimestampedModel.new(id: next_id, name: 'Exp SINE')
191
+ @exp_sine.save_if_not_exists!(update_expiration: true)
192
+ # No default expiration set, so TTL is -1 (no expiration)
193
+ @exp_sine.ttl == -1
194
+ #=> true
195
+
196
+ # =============================================
197
+ # 7. OptimisticLockError Behavior
198
+ # =============================================
199
+
200
+ ## save_if_not_exists allows OptimisticLockError to propagate
201
+ # Note: This is difficult to test reliably without mocking, but we can
202
+ # verify the method signature and rescue clause structure through the API
203
+
204
+ ## save_if_not_exists rescues only RecordExistsError
205
+ @opt_test = TimestampedModel.new(id: next_id, name: 'Opt Test')
206
+ @opt_test.save
207
+ @opt_dup = TimestampedModel.new(id: @opt_test.identifier, name: 'Opt Dup')
208
+ # This should return false, not raise
209
+ result = @opt_dup.save_if_not_exists
210
+ result
211
+ #=> false
212
+
213
+ # =============================================
214
+ # 8. Edge Cases
215
+ # =============================================
216
+
217
+ ## save works with nil field values
218
+ @nil_save = TimestampedModel.new(id: next_id, name: nil)
219
+ @nil_save.save
220
+ @nil_save.exists?
221
+ #=> true
222
+
223
+ ## save_if_not_exists! works with nil field values
224
+ @nil_sine = TimestampedModel.new(id: next_id, name: nil)
225
+ @nil_sine.save_if_not_exists!
226
+ @nil_sine.exists?
227
+ #=> true
228
+
229
+ ## Both methods handle empty strings
230
+ @empty_save = TimestampedModel.new(id: next_id, name: '')
231
+ @empty_save.save
232
+ @empty_sine = TimestampedModel.new(id: next_id, name: '')
233
+ @empty_sine.save_if_not_exists!
234
+ [@empty_save.exists?, @empty_sine.exists?]
235
+ #=> [true, true]
236
+
237
+ # Cleanup
238
+ cleanup_keys = Familia.dbclient.keys('timestampedmodel:*') +
239
+ Familia.dbclient.keys('uniqueindexmodel:*') +
240
+ Familia.dbclient.keys('*:email_lookup:*')
241
+ Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
@@ -1,3 +1,7 @@
1
+ # try/integration/scenarios_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # Comprehensive configuration scenarios
2
6
 
3
7
  require_relative '../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/integration/secure_identifier_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/secure_identifier_try.rb
2
6
 
3
7
  # Test Familia::SecureIdentifier methods
@@ -0,0 +1,176 @@
1
+ # try/integration/transaction_safety_core_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Core Transaction Safety Tests
6
+ #
7
+ # Tests the fundamental transaction safety rules from docs/transaction_safety.md
8
+ # Focuses on the most critical safety mechanisms.
9
+ #
10
+
11
+ require_relative '../support/helpers/test_helpers'
12
+
13
+ # Simple test model for transaction safety
14
+ class ::SafetyTestCustomer < Familia::Horreum
15
+ identifier_field :email
16
+ field :email
17
+ field :login_count
18
+ field :status
19
+
20
+ list :orders
21
+ end
22
+
23
+ # Helper for unique test IDs
24
+ def safety_test_id(prefix = 'test')
25
+ "#{prefix}_#{Time.now.to_i}_#{rand(1000000)}"
26
+ end
27
+
28
+ ## save raises OperationModeError inside transaction
29
+ error_raised = false
30
+ SafetyTestCustomer.transaction do
31
+ customer = SafetyTestCustomer.new(email: "#{safety_test_id}@example.com")
32
+ begin
33
+ customer.save
34
+ rescue Familia::OperationModeError => e
35
+ error_raised = true
36
+ end
37
+ end
38
+ error_raised
39
+ #=> true
40
+
41
+ ## save_if_not_exists! raises OperationModeError inside transaction
42
+ error_raised_conditional = false
43
+ SafetyTestCustomer.transaction do
44
+ customer = SafetyTestCustomer.new(email: "#{safety_test_id}@example.com")
45
+ begin
46
+ customer.save_if_not_exists!
47
+ rescue Familia::OperationModeError => e
48
+ error_raised_conditional = true
49
+ end
50
+ end
51
+ error_raised_conditional
52
+ #=> true
53
+
54
+ ## create! raises OperationModeError inside transaction
55
+ error_raised_create = false
56
+ SafetyTestCustomer.transaction do
57
+ begin
58
+ SafetyTestCustomer.create!(email: "#{safety_test_id}@example.com")
59
+ rescue Familia::OperationModeError => e
60
+ error_raised_create = true
61
+ end
62
+ end
63
+ error_raised_create
64
+ #=> true
65
+
66
+ ## correct pattern save before transaction works
67
+ @correct_customer = SafetyTestCustomer.new(email: "#{safety_test_id('correct')}@example.com")
68
+ @correct_customer.save
69
+ #=> true
70
+
71
+ ## operations work inside transaction after save
72
+ @correct_customer.transaction do
73
+ @correct_customer.hset(:login_count, '1')
74
+ @correct_customer.hset(:status, 'active')
75
+ end
76
+ @correct_customer.hget(:login_count).to_i >= 1
77
+ #=> true
78
+
79
+ ## write-only operations work inside transactions
80
+ @write_customer = SafetyTestCustomer.new(email: "#{safety_test_id('write')}@example.com")
81
+ @write_customer.save
82
+
83
+ @write_result = @write_customer.transaction do |conn|
84
+ conn.hset(@write_customer.dbkey, 'status', 'premium')
85
+ conn.hset(@write_customer.dbkey, 'login_count', '5')
86
+ conn.expire(@write_customer.dbkey, 3600)
87
+ end
88
+ @write_result.class.name
89
+ #=> "MultiResult"
90
+
91
+ ## nested transactions reuse same connection
92
+ @nested_customer = SafetyTestCustomer.new(email: "#{safety_test_id('nested')}@example.com")
93
+ @nested_customer.save
94
+
95
+ @outer_conn_id = nil
96
+ @inner_conn_id = nil
97
+
98
+ SafetyTestCustomer.transaction do |outer_conn|
99
+ @outer_conn_id = outer_conn.object_id
100
+ @nested_customer.hset(:login_count, '1')
101
+
102
+ @nested_customer.transaction do |inner_conn|
103
+ @inner_conn_id = inner_conn.object_id
104
+ @nested_customer.hset(:status, 'nested')
105
+ end
106
+ end
107
+
108
+ @outer_conn_id == @inner_conn_id
109
+ #=> true
110
+
111
+ ## read operations return Future objects inside transaction
112
+ @read_customer = SafetyTestCustomer.new(email: "#{safety_test_id('read')}@example.com")
113
+ @read_customer.save
114
+
115
+ @future_object = nil
116
+ @read_customer.transaction do |conn|
117
+ @future_object = conn.hget(@read_customer.dbkey, 'email')
118
+ end
119
+ @future_object.class.name.include?('Future')
120
+ #=> true
121
+
122
+ ## exists check returns Future inside transaction always truthy
123
+ @pitfall_customer = SafetyTestCustomer.new(email: "#{safety_test_id('pitfall')}@example.com")
124
+
125
+ @wrong_result = nil
126
+ SafetyTestCustomer.transaction do |conn|
127
+ existence = conn.exists?(@pitfall_customer.dbkey)
128
+ @wrong_result = if existence
129
+ 'always_executed'
130
+ else
131
+ 'never_executed'
132
+ end
133
+ end
134
+ @wrong_result
135
+ #=> 'always_executed'
136
+
137
+ ## create with success callback works
138
+ @callback_executed = false
139
+ SafetyTestCustomer.create!(email: "#{safety_test_id('callback')}@example.com") do |customer|
140
+ @callback_executed = true
141
+ customer.hset(:login_count, '1')
142
+ end
143
+ @callback_executed
144
+ #=> true
145
+
146
+ ## multi-object atomic updates work
147
+ @order_customer = SafetyTestCustomer.new(email: "#{safety_test_id('order')}@example.com")
148
+ @order_customer.save
149
+
150
+ @multi_result = SafetyTestCustomer.transaction do
151
+ @order_customer.hset(:status, 'confirmed')
152
+ @order_customer.orders.push('order123')
153
+ @order_customer.hset(:login_count, '1')
154
+ end
155
+ @multi_result.successful?
156
+ #=> true
157
+
158
+ ## watch pattern for optimistic locking works
159
+ @watch_customer = SafetyTestCustomer.new(email: "#{safety_test_id('watch')}@example.com")
160
+ @watch_customer.save
161
+ @watch_customer.hset(:balance, '1000')
162
+
163
+ @success = @watch_customer.watch do
164
+ current_balance = @watch_customer.hget(:balance).to_i
165
+
166
+ if current_balance >= 100
167
+ @watch_customer.transaction do
168
+ @watch_customer.hset(:balance, (current_balance - 100).to_s)
169
+ @watch_customer.hset(:purchases, '1')
170
+ end
171
+ end
172
+ end
173
+
174
+ @new_balance = @watch_customer.hget(:balance).to_i
175
+ @new_balance <= 900
176
+ #=> true