familia 2.0.0.pre19 → 2.0.0.pre21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) 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/CHANGELOG.rst +177 -112
  8. data/CLAUDE.md +28 -1
  9. data/Gemfile +1 -1
  10. data/Gemfile.lock +20 -17
  11. data/bin/try +16 -0
  12. data/bin/tryouts +16 -0
  13. data/changelog.d/20251105_flexible_external_identifier_format.rst +66 -0
  14. data/changelog.d/20251107_112554_delano_179_participation_asymmetry.rst +44 -0
  15. data/changelog.d/20251107_213121_delano_fix_thread_safety_races_011CUumCP492Twxm4NLt2FvL.rst +20 -0
  16. data/changelog.d/20251107_fix_participates_in_symbol_resolution.rst +91 -0
  17. data/changelog.d/20251107_optimized_redis_exists_checks.rst +94 -0
  18. data/changelog.d/20251108_frozen_string_literal_pragma.rst +44 -0
  19. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  20. data/docs/guides/encryption.md +486 -0
  21. data/docs/guides/feature-encrypted-fields.md +123 -7
  22. data/docs/guides/feature-expiration.md +161 -117
  23. data/docs/guides/feature-external-identifiers.md +415 -443
  24. data/docs/guides/feature-object-identifiers.md +400 -269
  25. data/docs/guides/feature-quantization.md +120 -6
  26. data/docs/guides/feature-relationships-indexing.md +318 -0
  27. data/docs/guides/feature-relationships-methods.md +146 -604
  28. data/docs/guides/feature-relationships-participation.md +263 -0
  29. data/docs/guides/feature-relationships.md +118 -136
  30. data/docs/guides/feature-system-devs.md +176 -693
  31. data/docs/guides/feature-system.md +119 -6
  32. data/docs/guides/feature-transient-fields.md +81 -0
  33. data/docs/guides/field-system.md +778 -0
  34. data/docs/guides/index.md +32 -15
  35. data/docs/guides/logging.md +187 -0
  36. data/docs/guides/optimized-loading.md +674 -0
  37. data/docs/guides/thread-safety-monitoring.md +61 -0
  38. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  39. data/docs/migrating/v2.0.0-pre22.md +241 -0
  40. data/docs/overview.md +7 -9
  41. data/docs/reference/api-technical.md +267 -320
  42. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  43. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  44. data/examples/autoloader/mega_customer.rb +2 -0
  45. data/examples/datatype_standalone.rb +4 -3
  46. data/examples/encrypted_fields.rb +2 -1
  47. data/examples/json_usage_patterns.rb +2 -0
  48. data/examples/relationships.rb +3 -0
  49. data/examples/safe_dump.rb +2 -1
  50. data/examples/sampling_demo.rb +53 -0
  51. data/examples/single_connection_transaction_confusions.rb +2 -1
  52. data/familia.gemspec +2 -1
  53. data/lib/familia/base.rb +2 -0
  54. data/lib/familia/connection/behavior.rb +2 -0
  55. data/lib/familia/connection/handlers.rb +2 -0
  56. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  57. data/lib/familia/connection/middleware.rb +34 -24
  58. data/lib/familia/connection/operation_core.rb +2 -0
  59. data/lib/familia/connection/operations.rb +2 -0
  60. data/lib/familia/connection/pipelined_core.rb +2 -0
  61. data/lib/familia/connection/transaction_core.rb +68 -0
  62. data/lib/familia/connection.rb +18 -3
  63. data/lib/familia/data_type/class_methods.rb +3 -1
  64. data/lib/familia/data_type/connection.rb +2 -0
  65. data/lib/familia/data_type/database_commands.rb +2 -0
  66. data/lib/familia/data_type/serialization.rb +6 -4
  67. data/lib/familia/data_type/settings.rb +2 -0
  68. data/lib/familia/data_type/types/counter.rb +2 -0
  69. data/lib/familia/data_type/types/hashkey.rb +7 -5
  70. data/lib/familia/data_type/types/listkey.rb +2 -0
  71. data/lib/familia/data_type/types/lock.rb +2 -0
  72. data/lib/familia/data_type/types/sorted_set.rb +2 -0
  73. data/lib/familia/data_type/types/stringkey.rb +2 -0
  74. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  75. data/lib/familia/data_type.rb +2 -0
  76. data/lib/familia/encryption/encrypted_data.rb +4 -2
  77. data/lib/familia/encryption/manager.rb +2 -0
  78. data/lib/familia/encryption/provider.rb +2 -0
  79. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  80. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  81. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  82. data/lib/familia/encryption/registry.rb +2 -0
  83. data/lib/familia/encryption/request_cache.rb +2 -0
  84. data/lib/familia/encryption.rb +9 -2
  85. data/lib/familia/errors.rb +2 -0
  86. data/lib/familia/features/autoloader.rb +2 -0
  87. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  88. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  89. data/lib/familia/features/encrypted_fields.rb +2 -2
  90. data/lib/familia/features/expiration/extensions.rb +3 -1
  91. data/lib/familia/features/expiration.rb +12 -4
  92. data/lib/familia/features/external_identifier.rb +33 -7
  93. data/lib/familia/features/object_identifier.rb +2 -0
  94. data/lib/familia/features/quantization.rb +3 -1
  95. data/lib/familia/features/relationships/README.md +3 -1
  96. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  97. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +138 -9
  98. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  99. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +89 -21
  100. data/lib/familia/features/relationships/indexing.rb +3 -0
  101. data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
  102. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  103. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  104. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  105. data/lib/familia/features/relationships/participation.rb +155 -69
  106. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  107. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  108. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  109. data/lib/familia/features/relationships.rb +5 -3
  110. data/lib/familia/features/safe_dump.rb +2 -0
  111. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  112. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  113. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  114. data/lib/familia/features/transient_fields.rb +2 -0
  115. data/lib/familia/features.rb +2 -0
  116. data/lib/familia/field_type.rb +3 -1
  117. data/lib/familia/horreum/connection.rb +17 -1
  118. data/lib/familia/horreum/database_commands.rb +2 -0
  119. data/lib/familia/horreum/definition.rb +16 -6
  120. data/lib/familia/horreum/management.rb +212 -42
  121. data/lib/familia/horreum/persistence.rb +176 -108
  122. data/lib/familia/horreum/related_fields.rb +2 -0
  123. data/lib/familia/horreum/serialization.rb +23 -4
  124. data/lib/familia/horreum/settings.rb +2 -0
  125. data/lib/familia/horreum/utils.rb +2 -0
  126. data/lib/familia/horreum.rb +15 -1
  127. data/lib/familia/identifier_extractor.rb +2 -0
  128. data/lib/familia/instrumentation.rb +156 -0
  129. data/lib/familia/json_serializer.rb +2 -0
  130. data/lib/familia/logging.rb +92 -32
  131. data/lib/familia/refinements/dear_json.rb +2 -0
  132. data/lib/familia/refinements/stylize_words.rb +2 -14
  133. data/lib/familia/refinements/time_literals.rb +2 -0
  134. data/lib/familia/refinements.rb +2 -0
  135. data/lib/familia/secure_identifier.rb +10 -2
  136. data/lib/familia/settings.rb +2 -0
  137. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  138. data/lib/familia/thread_safety/monitor.rb +328 -0
  139. data/lib/familia/utils.rb +13 -0
  140. data/lib/familia/verifiable_identifier.rb +3 -1
  141. data/lib/familia/version.rb +3 -1
  142. data/lib/familia.rb +31 -4
  143. data/lib/middleware/database_command_counter.rb +152 -0
  144. data/lib/middleware/database_logger.rb +295 -170
  145. data/lib/multi_result.rb +2 -0
  146. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  147. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  148. data/try/edge_cases/json_serialization_try.rb +2 -0
  149. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  150. data/try/edge_cases/race_conditions_try.rb +4 -0
  151. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  152. data/try/edge_cases/string_coercion_try.rb +2 -0
  153. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  154. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  155. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  156. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  157. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  158. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  159. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  160. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  161. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  162. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  163. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  164. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  165. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  166. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  167. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  168. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  169. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  170. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  171. data/try/features/encryption/config_persistence_try.rb +4 -0
  172. data/try/features/encryption/core_try.rb +4 -0
  173. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  174. data/try/features/encryption/module_loading_try.rb +4 -0
  175. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  176. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  177. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  178. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  179. data/try/features/expiration/expiration_try.rb +4 -0
  180. data/try/features/external_identifier/external_identifier_try.rb +171 -8
  181. data/try/features/feature_dependencies_try.rb +2 -0
  182. data/try/features/feature_improvements_try.rb +2 -0
  183. data/try/features/field_groups_try.rb +2 -0
  184. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  185. data/try/features/object_identifier/object_identifier_try.rb +2 -0
  186. data/try/features/quantization/quantization_try.rb +4 -0
  187. data/try/features/real_feature_integration_try.rb +2 -0
  188. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  189. data/try/features/relationships/indexing_rebuild_try.rb +600 -0
  190. data/try/features/relationships/indexing_try.rb +2 -0
  191. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  192. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  193. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  194. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  195. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  196. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  197. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  198. data/try/features/relationships/relationships_api_changes_try.rb +2 -0
  199. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  200. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  201. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  202. data/try/features/relationships/relationships_performance_try.rb +4 -0
  203. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  204. data/try/features/relationships/relationships_try.rb +6 -4
  205. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  206. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  207. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  208. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  209. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  210. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  211. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  212. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  213. data/try/integration/connection/fiber_context_preservation_try.rb +4 -0
  214. data/try/integration/connection/handler_constraints_try.rb +4 -0
  215. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  216. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  217. data/try/integration/connection/operation_mode_guards_try.rb +4 -0
  218. data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
  219. data/try/integration/connection/pools_try.rb +4 -0
  220. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  221. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  222. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  223. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  224. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  225. data/try/integration/connection/transaction_modes_try.rb +4 -0
  226. data/try/integration/conventional_inheritance_try.rb +4 -0
  227. data/try/integration/create_method_try.rb +4 -0
  228. data/try/integration/cross_component_try.rb +4 -0
  229. data/try/integration/data_types/datatype_pipelines_try.rb +4 -0
  230. data/try/integration/data_types/datatype_transactions_try.rb +4 -0
  231. data/try/integration/database_consistency_try.rb +4 -0
  232. data/try/integration/familia_extended_try.rb +4 -0
  233. data/try/integration/familia_members_methods_try.rb +4 -0
  234. data/try/integration/models/customer_safe_dump_try.rb +4 -0
  235. data/try/integration/models/customer_try.rb +4 -0
  236. data/try/integration/models/datatype_base_try.rb +4 -0
  237. data/try/integration/models/familia_object_try.rb +4 -0
  238. data/try/integration/persistence_operations_try.rb +4 -0
  239. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  240. data/try/integration/save_methods_consistency_try.rb +241 -0
  241. data/try/integration/scenarios_try.rb +4 -0
  242. data/try/integration/secure_identifier_try.rb +4 -0
  243. data/try/integration/transaction_safety_core_try.rb +176 -0
  244. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  245. data/try/integration/verifiable_identifier_try.rb +4 -0
  246. data/try/investigation/pipeline_routing/README.md +228 -0
  247. data/try/performance/benchmarks_try.rb +4 -0
  248. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  249. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  250. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  251. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  252. data/try/support/debugging/debug_aad_process.rb +3 -0
  253. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  254. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  255. data/try/support/debugging/debug_context_aad.rb +3 -0
  256. data/try/support/debugging/debug_context_simple.rb +3 -0
  257. data/try/support/debugging/debug_cross_context.rb +3 -0
  258. data/try/support/debugging/debug_database_load.rb +3 -0
  259. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  260. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  261. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  262. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  263. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  264. data/try/support/debugging/debug_load_path.rb +3 -0
  265. data/try/support/debugging/debug_method_definition.rb +3 -0
  266. data/try/support/debugging/debug_method_resolution.rb +3 -0
  267. data/try/support/debugging/debug_minimal.rb +3 -0
  268. data/try/support/debugging/debug_provider.rb +3 -0
  269. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  270. data/try/support/debugging/debug_string_class.rb +3 -0
  271. data/try/support/debugging/debug_test.rb +3 -0
  272. data/try/support/debugging/debug_test_design.rb +3 -0
  273. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  274. data/try/support/debugging/provider_diagnostics.rb +4 -0
  275. data/try/support/helpers/test_cleanup.rb +4 -0
  276. data/try/support/helpers/test_helpers.rb +5 -0
  277. data/try/support/memory/memory_basic_test.rb +4 -0
  278. data/try/support/memory/memory_detailed_test.rb +4 -0
  279. data/try/support/memory/memory_search_for_string.rb +4 -0
  280. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  281. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  282. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  283. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  284. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  285. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  286. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  287. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  288. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  289. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  290. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  291. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  292. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  293. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  294. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  295. data/try/thread_safety/README.md +496 -0
  296. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  297. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  298. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  299. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  300. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  301. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  302. data/try/thread_safety/field_registration_race_try.rb +222 -0
  303. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  304. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  305. data/try/thread_safety/module_config_race_try.rb +175 -0
  306. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  307. data/try/unit/core/autoloader_try.rb +4 -0
  308. data/try/unit/core/base_enhancements_try.rb +4 -0
  309. data/try/unit/core/connection_try.rb +4 -0
  310. data/try/unit/core/errors_try.rb +4 -0
  311. data/try/unit/core/extensions_try.rb +4 -0
  312. data/try/unit/core/familia_logger_try.rb +2 -0
  313. data/try/unit/core/familia_try.rb +4 -0
  314. data/try/unit/core/middleware_sampling_try.rb +335 -0
  315. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  316. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  317. data/try/unit/core/middleware_try.rb +4 -0
  318. data/try/unit/core/settings_try.rb +4 -0
  319. data/try/unit/core/time_utils_try.rb +4 -0
  320. data/try/unit/core/tools_try.rb +4 -0
  321. data/try/unit/core/utils_try.rb +37 -0
  322. data/try/unit/data_types/boolean_try.rb +4 -0
  323. data/try/unit/data_types/counter_try.rb +4 -0
  324. data/try/unit/data_types/datatype_base_try.rb +4 -0
  325. data/try/unit/data_types/hash_try.rb +4 -0
  326. data/try/unit/data_types/list_try.rb +4 -0
  327. data/try/unit/data_types/lock_try.rb +4 -0
  328. data/try/unit/data_types/sorted_set_try.rb +4 -0
  329. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  330. data/try/unit/data_types/string_try.rb +4 -0
  331. data/try/unit/data_types/unsortedset_try.rb +4 -0
  332. data/try/unit/familia_resolve_class_try.rb +116 -0
  333. data/try/unit/horreum/auto_indexing_on_save_try.rb +5 -1
  334. data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
  335. data/try/unit/horreum/base_try.rb +4 -0
  336. data/try/unit/horreum/class_methods_try.rb +4 -0
  337. data/try/unit/horreum/commands_try.rb +4 -0
  338. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  339. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +4 -0
  340. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  341. data/try/unit/horreum/field_categories_try.rb +4 -0
  342. data/try/unit/horreum/field_definition_try.rb +4 -0
  343. data/try/unit/horreum/initialization_try.rb +4 -0
  344. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  345. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  346. data/try/unit/horreum/relations_try.rb +4 -0
  347. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  348. data/try/unit/horreum/serialization_try.rb +4 -0
  349. data/try/unit/horreum/settings_try.rb +4 -0
  350. data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
  351. data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -0
  352. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  353. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  354. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  355. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  356. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  357. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  358. data/try/unit/thread_safety_monitor_try.rb +149 -0
  359. metadata +72 -17
  360. data/.github/workflows/code-quality.yml +0 -138
  361. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
  362. data/changelog.d/20251011_203905_delano_next.rst +0 -30
  363. data/changelog.d/20251011_212633_delano_next.rst +0 -13
  364. data/changelog.d/20251011_221253_delano_next.rst +0 -26
  365. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  366. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  367. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  368. data/docs/archive/README.md +0 -64
  369. data/docs/archive/api-reference.md +0 -333
  370. data/docs/guides/core-field-system.md +0 -806
  371. data/docs/guides/implementation.md +0 -276
  372. data/docs/guides/security-model.md +0 -183
@@ -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