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,156 @@
1
+ # try/unit/horreum/optimized_loading_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Test optimized loading methods with check_exists parameter and pipelined bulk loading
6
+
7
+ require_relative '../../support/helpers/test_helpers'
8
+
9
+ OptimizedUser = Class.new(Familia::Horreum) do
10
+ identifier_field :user_id
11
+ field :user_id
12
+ field :name
13
+ field :email
14
+ field :age
15
+ end
16
+
17
+ # Setup: Create test users
18
+ setup_user1 = OptimizedUser.new(user_id: 'opt_user_1', name: 'Alice', email: 'alice@example.com', age: 30)
19
+ setup_user1.save
20
+
21
+ setup_user2 = OptimizedUser.new(user_id: 'opt_user_2', name: 'Bob', email: 'bob@example.com', age: 25)
22
+ setup_user2.save
23
+
24
+ setup_user3 = OptimizedUser.new(user_id: 'opt_user_3', name: 'Charlie', email: 'charlie@example.com', age: 35)
25
+ setup_user3.save
26
+
27
+ ## find_by_dbkey with check_exists: true (default) returns object for existing key
28
+ found_user = OptimizedUser.find_by_dbkey(OptimizedUser.dbkey('opt_user_1'))
29
+ found_user.name
30
+ #=> 'Alice'
31
+
32
+ ## find_by_dbkey with check_exists: true (default) returns nil for non-existent key
33
+ OptimizedUser.find_by_dbkey(OptimizedUser.dbkey('nonexistent'))
34
+ #=> nil
35
+
36
+ ## find_by_dbkey with check_exists: false returns object for existing key
37
+ found_user_fast = OptimizedUser.find_by_dbkey(OptimizedUser.dbkey('opt_user_1'), check_exists: false)
38
+ found_user_fast.name
39
+ #=> 'Alice'
40
+
41
+ ## find_by_dbkey with check_exists: false returns nil for non-existent key
42
+ OptimizedUser.find_by_dbkey(OptimizedUser.dbkey('nonexistent'), check_exists: false)
43
+ #=> nil
44
+
45
+ ## find_by_dbkey with check_exists: false correctly deserializes all fields
46
+ fast_loaded = OptimizedUser.find_by_dbkey(OptimizedUser.dbkey('opt_user_2'), check_exists: false)
47
+ [fast_loaded.user_id, fast_loaded.name, fast_loaded.email, fast_loaded.age]
48
+ #=> ['opt_user_2', 'Bob', 'bob@example.com', 25]
49
+
50
+ ## find_by_identifier with check_exists: true (default) returns object
51
+ OptimizedUser.find_by_identifier('opt_user_1').name
52
+ #=> 'Alice'
53
+
54
+ ## find_by_identifier with check_exists: false returns object
55
+ OptimizedUser.find_by_identifier('opt_user_1', check_exists: false).name
56
+ #=> 'Alice'
57
+
58
+ ## find_by_identifier with check_exists: false returns nil for non-existent
59
+ OptimizedUser.find_by_identifier('nonexistent', check_exists: false)
60
+ #=> nil
61
+
62
+ ## find_by_id alias works with check_exists parameter
63
+ OptimizedUser.find_by_id('opt_user_2', check_exists: false).email
64
+ #=> 'bob@example.com'
65
+
66
+ ## find alias works with check_exists parameter
67
+ OptimizedUser.find('opt_user_3', check_exists: false).age
68
+ #=> 35
69
+
70
+ ## load alias works with check_exists parameter
71
+ OptimizedUser.load('opt_user_1', check_exists: false).user_id
72
+ #=> 'opt_user_1'
73
+
74
+ ## load_multi loads multiple existing objects
75
+ users = OptimizedUser.load_multi(['opt_user_1', 'opt_user_2', 'opt_user_3'])
76
+ users.map(&:name)
77
+ #=> ['Alice', 'Bob', 'Charlie']
78
+
79
+ ## load_multi returns nils for non-existent objects in correct positions
80
+ users_mixed = OptimizedUser.load_multi(['opt_user_1', 'nonexistent', 'opt_user_3'])
81
+ [users_mixed[0]&.name, users_mixed[1], users_mixed[2]&.name]
82
+ #=> ['Alice', nil, 'Charlie']
83
+
84
+ ## load_multi with compact filters out nils
85
+ users_compact = OptimizedUser.load_multi(['opt_user_1', 'nonexistent', 'opt_user_2']).compact
86
+ users_compact.map(&:name)
87
+ #=> ['Alice', 'Bob']
88
+
89
+ ## load_multi preserves order of identifiers
90
+ users_ordered = OptimizedUser.load_multi(['opt_user_3', 'opt_user_1', 'opt_user_2'])
91
+ users_ordered.map(&:user_id)
92
+ #=> ['opt_user_3', 'opt_user_1', 'opt_user_2']
93
+
94
+ ## load_multi handles empty array
95
+ OptimizedUser.load_multi([])
96
+ #=> []
97
+
98
+ ## load_multi handles all non-existent identifiers
99
+ all_missing = OptimizedUser.load_multi(['missing1', 'missing2'])
100
+ all_missing.compact
101
+ #=> []
102
+
103
+ ## load_multi correctly deserializes all field types
104
+ multi_loaded = OptimizedUser.load_multi(['opt_user_2']).first
105
+ [multi_loaded.user_id, multi_loaded.name, multi_loaded.email, multi_loaded.age]
106
+ #=> ['opt_user_2', 'Bob', 'bob@example.com', 25]
107
+
108
+ ## load_batch alias works
109
+ batch_users = OptimizedUser.load_batch(['opt_user_1', 'opt_user_2'])
110
+ batch_users.map(&:name)
111
+ #=> ['Alice', 'Bob']
112
+
113
+ ## load_multi_by_keys loads by full dbkeys
114
+ keys = [OptimizedUser.dbkey('opt_user_1'), OptimizedUser.dbkey('opt_user_2')]
115
+ keyed_users = OptimizedUser.load_multi_by_keys(keys)
116
+ keyed_users.map(&:name)
117
+ #=> ['Alice', 'Bob']
118
+
119
+ ## load_multi_by_keys returns nil for non-existent keys
120
+ keys_mixed = [OptimizedUser.dbkey('opt_user_1'), OptimizedUser.dbkey('nonexistent')]
121
+ keyed_mixed = OptimizedUser.load_multi_by_keys(keys_mixed)
122
+ [keyed_mixed[0]&.name, keyed_mixed[1]]
123
+ #=> ['Alice', nil]
124
+
125
+ ## load_multi_by_keys handles empty array
126
+ OptimizedUser.load_multi_by_keys([])
127
+ #=> []
128
+
129
+ ## load_multi_by_keys handles empty/nil keys and maintains position alignment
130
+ keys_with_empty = [OptimizedUser.dbkey('opt_user_1'), '', OptimizedUser.dbkey('opt_user_2'), nil]
131
+ mixed_keys = OptimizedUser.load_multi_by_keys(keys_with_empty)
132
+ [mixed_keys[0]&.name, mixed_keys[1], mixed_keys[2]&.name, mixed_keys[3]]
133
+ #=> ['Alice', nil, 'Bob', nil]
134
+
135
+ ## load_multi handles nil identifiers gracefully
136
+ users_with_nils = OptimizedUser.load_multi(['opt_user_1', nil, 'opt_user_2'])
137
+ [users_with_nils[0]&.name, users_with_nils[1], users_with_nils[2]&.name]
138
+ #=> ['Alice', nil, 'Bob']
139
+
140
+ ## load_multi handles empty string identifiers
141
+ users_with_empty = OptimizedUser.load_multi(['opt_user_1', '', 'opt_user_2'])
142
+ [users_with_empty[0]&.name, users_with_empty[1], users_with_empty[2]&.name]
143
+ #=> ['Alice', nil, 'Bob']
144
+
145
+ ## find_by_identifier works with suffix as keyword parameter
146
+ OptimizedUser.find_by_identifier('opt_user_1', suffix: :object)&.name
147
+ #=> 'Alice'
148
+
149
+ ## find_by_identifier works with both keyword parameters
150
+ OptimizedUser.find_by_identifier('opt_user_1', suffix: :object, check_exists: false)&.name
151
+ #=> 'Alice'
152
+
153
+ # Teardown: Clean up test data
154
+ OptimizedUser.destroy!('opt_user_1')
155
+ OptimizedUser.destroy!('opt_user_2')
156
+ OptimizedUser.destroy!('opt_user_3')
@@ -1,3 +1,7 @@
1
+ # try/unit/horreum/relations_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/horreum/relations_try.rb
2
6
  # Test Horreum Database type relations functionality
3
7
 
@@ -1,3 +1,7 @@
1
+ # try/unit/horreum/serialization_persistent_fields_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/horreum/serialization_persistent_fields_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/horreum/serialization_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/horreum/serialization_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/horreum/settings_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/horreum/settings_try.rb
2
6
 
3
7
  # Test Horreum settings
@@ -1,3 +1,7 @@
1
+ # try/unit/horreum/unique_index_edge_cases_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # Testing edge cases for unique index validation
2
6
  # lib/familia/features/relationships/indexing/unique_index_generators.rb
3
7
  # lib/familia/horreum/persistence.rb
@@ -1,4 +1,6 @@
1
1
  # try/unit/horreum/unique_index_guard_validation_try.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  #
4
6
  # Unique index guard validation tests
@@ -0,0 +1,139 @@
1
+ # try/unit/middleware/database_command_counter_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Test DatabaseCommandCounter non-command methods and utilities
6
+ #
7
+ # This test file covers utility methods, configuration methods,
8
+ # and state management methods that don't involve command execution.
9
+ #
10
+ # Covers:
11
+ # - Counter methods (count, reset, increment, count_commands)
12
+ # - Configuration (skip_commands)
13
+ # - Utility methods (skip_command?)
14
+ # - Thread safety of atomic counter
15
+
16
+ require_relative '../../support/helpers/test_helpers'
17
+ require 'concurrent-ruby'
18
+
19
+ @original_count = DatabaseCommandCounter.count
20
+
21
+ # Clear initial state
22
+ DatabaseCommandCounter.reset
23
+
24
+ ## count returns the current command count
25
+ DatabaseCommandCounter.reset
26
+ DatabaseCommandCounter.count
27
+ #=> 0
28
+
29
+ ## reset sets count to zero
30
+ DatabaseCommandCounter.increment
31
+ DatabaseCommandCounter.increment
32
+ DatabaseCommandCounter.reset
33
+ DatabaseCommandCounter.count
34
+ #=> 0
35
+
36
+ ## reset returns zero
37
+ result = DatabaseCommandCounter.reset
38
+ result
39
+ #=> 0
40
+
41
+ ## increment increases the count by 1
42
+ DatabaseCommandCounter.reset
43
+ DatabaseCommandCounter.increment
44
+ DatabaseCommandCounter.count
45
+ #=> 1
46
+
47
+ ## increment returns the new count
48
+ DatabaseCommandCounter.reset
49
+ result = DatabaseCommandCounter.increment
50
+ result
51
+ #=> 1
52
+
53
+ ## increment can be called multiple times
54
+ DatabaseCommandCounter.reset
55
+ 3.times { DatabaseCommandCounter.increment }
56
+ DatabaseCommandCounter.count
57
+ #=> 3
58
+
59
+ ## skip_commands returns a Set with default skipped commands
60
+ DatabaseCommandCounter.skip_commands.class
61
+ #=> Set
62
+
63
+ ## skip_commands includes SELECT by default
64
+ DatabaseCommandCounter.skip_commands.include?("SELECT")
65
+ #=> true
66
+
67
+ ## skip_commands is frozen for immutability
68
+ DatabaseCommandCounter.skip_commands.frozen?
69
+ #=> true
70
+
71
+ ## skip_command? returns true for commands in skip_commands
72
+ DatabaseCommandCounter.skip_command?(["SELECT", "0"])
73
+ #=> true
74
+
75
+ ## skip_command? returns false for commands not in skip_commands
76
+ DatabaseCommandCounter.skip_command?(["SET", "key", "value"])
77
+ #=> false
78
+
79
+ ## skip_command? handles command array properly
80
+ DatabaseCommandCounter.skip_command?(["GET", "key"])
81
+ #=> false
82
+
83
+ ## skip_command? is case insensitive
84
+ DatabaseCommandCounter.skip_command?(["select", "0"])
85
+ #=> true
86
+
87
+ ## count_commands captures count difference in block
88
+ DatabaseCommandCounter.reset
89
+ initial_count = DatabaseCommandCounter.count
90
+ commands_executed = DatabaseCommandCounter.count_commands do
91
+ 5.times { DatabaseCommandCounter.increment }
92
+ end
93
+ commands_executed
94
+ #=> 5
95
+
96
+ ## count_commands works with empty block
97
+ DatabaseCommandCounter.reset
98
+ commands_executed = DatabaseCommandCounter.count_commands do
99
+ # No commands
100
+ end
101
+ commands_executed
102
+ #=> 0
103
+
104
+ ## count_commands captures count difference with existing count
105
+ DatabaseCommandCounter.reset
106
+ 5.times { DatabaseCommandCounter.increment } # Existing commands
107
+ commands_executed = DatabaseCommandCounter.count_commands do
108
+ 3.times { DatabaseCommandCounter.increment } # New commands in block
109
+ end
110
+ commands_executed
111
+ #=> 3
112
+
113
+ ## Thread safety: increment is thread-safe with concurrent access
114
+ DatabaseCommandCounter.reset
115
+ threads = 10.times.map do |i|
116
+ Thread.new do
117
+ 10.times { DatabaseCommandCounter.increment }
118
+ end
119
+ end
120
+
121
+ threads.each(&:join)
122
+ DatabaseCommandCounter.count
123
+ #=> 100
124
+
125
+ ## Thread safety: count_commands is thread-safe
126
+ DatabaseCommandCounter.reset
127
+ result = DatabaseCommandCounter.count_commands do
128
+ threads = 5.times.map do
129
+ Thread.new do
130
+ 10.times { DatabaseCommandCounter.increment }
131
+ end
132
+ end
133
+ threads.each(&:join)
134
+ end
135
+ result
136
+ #=> 50
137
+
138
+ # Restore original state (if needed)
139
+ DatabaseCommandCounter.reset
@@ -0,0 +1,251 @@
1
+ # try/unit/middleware/database_logger_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Test DatabaseLogger non-command methods and utilities
6
+ #
7
+ # This test file covers utility methods, configuration methods,
8
+ # and state management methods that don't involve command execution.
9
+ #
10
+ # Covers:
11
+ # - Configuration getters/setters (logger, max_commands, structured_logging, sample_rate)
12
+ # - Command storage methods (clear_commands, append_command, capture_commands)
13
+ # - Utility methods (index, now_in_μs, should_log?)
14
+ # - State introspection (commands, process_start)
15
+ # - Thread safety of command storage
16
+
17
+ require_relative '../../support/helpers/test_helpers'
18
+ require 'logger'
19
+ require 'stringio'
20
+ require 'concurrent-ruby'
21
+
22
+ @original_logger = DatabaseLogger.logger
23
+ @original_max_commands = DatabaseLogger.max_commands
24
+ @original_structured_logging = DatabaseLogger.structured_logging
25
+ @original_sample_rate = DatabaseLogger.sample_rate
26
+
27
+ # Clear initial state
28
+ DatabaseLogger.clear_commands
29
+ DatabaseLogger.logger = nil
30
+ DatabaseLogger.max_commands = 10_000
31
+ DatabaseLogger.structured_logging = false
32
+ DatabaseLogger.sample_rate = nil
33
+
34
+ ## logger getter returns the current logger instance
35
+ test_logger = Logger.new(StringIO.new)
36
+ DatabaseLogger.logger = test_logger
37
+ DatabaseLogger.logger == test_logger
38
+ #=> true
39
+
40
+ ## logger can be set to nil
41
+ DatabaseLogger.logger = nil
42
+ DatabaseLogger.logger
43
+ #=> nil
44
+
45
+ ## max_commands getter returns the current max commands value
46
+ DatabaseLogger.max_commands = 5_000
47
+ DatabaseLogger.max_commands
48
+ #=> 5000
49
+
50
+ ## max_commands can be set to different values
51
+ DatabaseLogger.max_commands = 1_000
52
+ DatabaseLogger.max_commands
53
+ #=> 1000
54
+
55
+ ## structured_logging getter returns the current structured logging mode
56
+ DatabaseLogger.structured_logging = true
57
+ DatabaseLogger.structured_logging
58
+ #=> true
59
+
60
+ ## structured_logging can be toggled
61
+ DatabaseLogger.structured_logging = false
62
+ DatabaseLogger.structured_logging
63
+ #=> false
64
+
65
+ ## sample_rate getter returns the current sample rate
66
+ DatabaseLogger.sample_rate = 0.5
67
+ DatabaseLogger.sample_rate
68
+ #=> 0.5
69
+
70
+ ## sample_rate can be set to nil
71
+ DatabaseLogger.sample_rate = nil
72
+ DatabaseLogger.sample_rate
73
+ #=> nil
74
+
75
+ ## commands getter returns the captured commands array
76
+ DatabaseLogger.clear_commands
77
+ commands = DatabaseLogger.commands
78
+ commands.class
79
+ #=> Concurrent::Array
80
+
81
+ ## commands array is initially empty after clear
82
+ DatabaseLogger.clear_commands
83
+ DatabaseLogger.commands.empty?
84
+ #=> true
85
+
86
+ ## process_start returns a float timestamp
87
+ DatabaseLogger.process_start.class
88
+ #=> Float
89
+
90
+ ## process_start is frozen
91
+ DatabaseLogger.process_start.frozen?
92
+ #=> true
93
+
94
+ ## clear_commands returns nil and empties the commands array
95
+ DatabaseLogger.instance_variable_get(:@commands) << "test"
96
+ result = DatabaseLogger.clear_commands
97
+ [result, DatabaseLogger.commands.empty?]
98
+ #=> [nil, true]
99
+
100
+ ## index returns the current count of commands
101
+ DatabaseLogger.clear_commands
102
+ DatabaseLogger.index
103
+ #=> 0
104
+
105
+ ## index increases as commands are added
106
+ DatabaseLogger.clear_commands
107
+ msg = DatabaseLogger::CommandMessage.new("TEST", 100, 0.001)
108
+ DatabaseLogger.append_command(msg)
109
+ DatabaseLogger.index
110
+ #=> 1
111
+
112
+ ## append_command adds a message to the commands array
113
+ DatabaseLogger.clear_commands
114
+ msg = DatabaseLogger::CommandMessage.new("SET key value", 500, 0.002)
115
+ result = DatabaseLogger.append_command(msg)
116
+ [DatabaseLogger.commands.size, result.last == msg]
117
+ #=> [1, true]
118
+
119
+ ## append_command respects max_commands limit by shifting oldest
120
+ DatabaseLogger.max_commands = 3
121
+ DatabaseLogger.clear_commands
122
+ msg1 = DatabaseLogger::CommandMessage.new("CMD1", 100, 0.001)
123
+ msg2 = DatabaseLogger::CommandMessage.new("CMD2", 200, 0.002)
124
+ msg3 = DatabaseLogger::CommandMessage.new("CMD3", 300, 0.003)
125
+ msg4 = DatabaseLogger::CommandMessage.new("CMD4", 400, 0.004)
126
+
127
+ DatabaseLogger.append_command(msg1)
128
+ DatabaseLogger.append_command(msg2)
129
+ DatabaseLogger.append_command(msg3)
130
+ DatabaseLogger.append_command(msg4)
131
+
132
+ [DatabaseLogger.commands.size, DatabaseLogger.commands.first == msg2]
133
+ #=> [3, true]
134
+
135
+ ## now_in_μs returns current time in microseconds
136
+ time1 = DatabaseLogger.now_in_μs
137
+ sleep(0.001)
138
+ time2 = DatabaseLogger.now_in_μs
139
+ time2 > time1
140
+ #=> true
141
+
142
+ ## now_in_microseconds is an alias for now_in_μs
143
+ DatabaseLogger.method(:now_in_microseconds) == DatabaseLogger.method(:now_in_μs)
144
+ #=> true
145
+
146
+ ## capture_commands yields and returns captured commands
147
+ DatabaseLogger.clear_commands
148
+ commands = DatabaseLogger.capture_commands do
149
+ msg = DatabaseLogger::CommandMessage.new("GET key", 150, 0.001)
150
+ DatabaseLogger.append_command(msg)
151
+ msg2 = DatabaseLogger::CommandMessage.new("SET key2 value", 200, 0.002)
152
+ DatabaseLogger.append_command(msg2)
153
+ end
154
+ commands.size
155
+ #=> 2
156
+
157
+ ## capture_commands clears commands before capturing
158
+ DatabaseLogger.clear_commands
159
+ msg_before = DatabaseLogger::CommandMessage.new("BEFORE", 100, 0.001)
160
+ DatabaseLogger.append_command(msg_before)
161
+
162
+ commands = DatabaseLogger.capture_commands do
163
+ msg = DatabaseLogger::CommandMessage.new("DURING", 150, 0.002)
164
+ DatabaseLogger.append_command(msg)
165
+ end
166
+
167
+ [commands.size, commands.first.command]
168
+ #=> [1, "DURING"]
169
+
170
+ ## capture_commands returns array snapshot, not live reference
171
+ DatabaseLogger.clear_commands
172
+ commands = DatabaseLogger.capture_commands do
173
+ msg = DatabaseLogger::CommandMessage.new("TEST", 100, 0.001)
174
+ DatabaseLogger.append_command(msg)
175
+ end
176
+
177
+ # Add another command after capture
178
+ msg2 = DatabaseLogger::CommandMessage.new("AFTER", 200, 0.002)
179
+ DatabaseLogger.append_command(msg2)
180
+
181
+ [commands.size, DatabaseLogger.commands.size]
182
+ #=> [1, 2]
183
+
184
+ ## should_log? returns true when sample_rate is nil
185
+ DatabaseLogger.sample_rate = nil
186
+ DatabaseLogger.logger = Logger.new(StringIO.new)
187
+ DatabaseLogger.should_log?
188
+ #=> true
189
+
190
+ ## should_log? returns false when logger is nil regardless of sample_rate
191
+ DatabaseLogger.sample_rate = 1.0
192
+ DatabaseLogger.logger = nil
193
+ DatabaseLogger.should_log?
194
+ #=> false
195
+
196
+ ## should_log? uses atomic counter for thread-safe sampling
197
+ DatabaseLogger.sample_rate = 0.5 # 50% sampling
198
+ DatabaseLogger.logger = Logger.new(StringIO.new)
199
+ DatabaseLogger.instance_variable_set(:@sample_counter, Concurrent::AtomicFixnum.new(0))
200
+
201
+ # Test deterministic sampling pattern
202
+ results = 10.times.map { DatabaseLogger.should_log? }
203
+ results.count(true)
204
+ #=> 5
205
+
206
+ ## should_log? sampling is deterministic and consistent
207
+ DatabaseLogger.sample_rate = 0.25 # 25% sampling (every 4th)
208
+ DatabaseLogger.instance_variable_set(:@sample_counter, Concurrent::AtomicFixnum.new(0))
209
+
210
+ results = 12.times.map { DatabaseLogger.should_log? }
211
+ results.count(true)
212
+ #=> 3
213
+
214
+ ## CommandMessage can be created with command, duration, timeline
215
+ msg = DatabaseLogger::CommandMessage.new("SET key value", 1500, 0.123456)
216
+ [msg.command, msg.μs, msg.timeline]
217
+ #=> ["SET key value", 1500, 0.123456]
218
+
219
+ ## CommandMessage.inspect formats nicely
220
+ msg = DatabaseLogger::CommandMessage.new("GET key", 2500, 1.234567)
221
+ msg.inspect
222
+ #=> "1.234567 2500μs > GET key"
223
+
224
+ ## CommandMessage.to_a returns deconstructed array
225
+ msg = DatabaseLogger::CommandMessage.new("DEL key1 key2", 750, 2.345678)
226
+ msg.to_a
227
+ #=> ["DEL key1 key2", 750, 2.345678]
228
+
229
+ ## Thread safety: append_command is thread-safe with concurrent access
230
+ # Reset max_commands to allow all 100 commands
231
+ DatabaseLogger.max_commands = 1000
232
+ DatabaseLogger.clear_commands
233
+ threads = 10.times.map do |i|
234
+ Thread.new do
235
+ 10.times do |j|
236
+ msg = DatabaseLogger::CommandMessage.new("THREAD#{i}_CMD#{j}", 100, 0.001)
237
+ DatabaseLogger.append_command(msg)
238
+ end
239
+ end
240
+ end
241
+
242
+ threads.each(&:join)
243
+ DatabaseLogger.commands.size
244
+ #=> 100
245
+
246
+ # Restore original state
247
+ DatabaseLogger.logger = @original_logger
248
+ DatabaseLogger.max_commands = @original_max_commands
249
+ DatabaseLogger.structured_logging = @original_structured_logging
250
+ DatabaseLogger.sample_rate = @original_sample_rate
251
+ DatabaseLogger.clear_commands
@@ -1,3 +1,7 @@
1
+ # try/unit/refinements/dear_json_array_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/refinements/dear_json_array_methods_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/refinements/dear_json_hash_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/refinements/dear_json_hash_methods_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/refinements/time_literals_numeric_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/refinements/time_literals_numeric_methods_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/refinements/time_literals_string_methods_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/refinements/time_literals_string_methods_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'