familia 2.0.0.pre17 → 2.0.0.pre19

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rst +118 -6
  3. data/CLAUDE.md +43 -11
  4. data/Gemfile +2 -2
  5. data/Gemfile.lock +9 -47
  6. data/README.md +52 -0
  7. data/bin/irb +1 -1
  8. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +91 -0
  9. data/changelog.d/20251011_203905_delano_next.rst +30 -0
  10. data/changelog.d/20251011_212633_delano_next.rst +13 -0
  11. data/changelog.d/20251011_221253_delano_next.rst +26 -0
  12. data/docs/guides/core-field-system.md +48 -26
  13. data/docs/guides/feature-expiration.md +18 -18
  14. data/docs/migrating/v2.0.0-pre18.md +58 -0
  15. data/docs/migrating/v2.0.0-pre19.md +197 -0
  16. data/docs/qodo-merge-compliance.md +96 -0
  17. data/examples/datatype_standalone.rb +281 -0
  18. data/lib/familia/base.rb +0 -2
  19. data/lib/familia/connection/behavior.rb +252 -0
  20. data/lib/familia/connection/handlers.rb +95 -0
  21. data/lib/familia/connection/middleware.rb +58 -4
  22. data/lib/familia/connection/operation_core.rb +1 -1
  23. data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +2 -2
  24. data/lib/familia/connection/transaction_core.rb +7 -9
  25. data/lib/familia/connection.rb +2 -1
  26. data/lib/familia/data_type/connection.rb +151 -7
  27. data/lib/familia/data_type/{commands.rb → database_commands.rb} +9 -6
  28. data/lib/familia/data_type/serialization.rb +9 -5
  29. data/lib/familia/data_type/types/hashkey.rb +1 -1
  30. data/lib/familia/data_type.rb +2 -2
  31. data/lib/familia/encryption/encrypted_data.rb +12 -2
  32. data/lib/familia/encryption/manager.rb +11 -4
  33. data/lib/familia/errors.rb +51 -14
  34. data/lib/familia/features/autoloader.rb +3 -1
  35. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +11 -3
  36. data/lib/familia/features/expiration/extensions.rb +8 -10
  37. data/lib/familia/features/expiration.rb +19 -19
  38. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +45 -44
  39. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +151 -65
  40. data/lib/familia/features/relationships/indexing.rb +37 -42
  41. data/lib/familia/features/relationships/indexing_relationship.rb +14 -4
  42. data/lib/familia/features/safe_dump.rb +2 -3
  43. data/lib/familia/field_type.rb +2 -1
  44. data/lib/familia/horreum/connection.rb +11 -35
  45. data/lib/familia/horreum/database_commands.rb +130 -11
  46. data/lib/familia/horreum/definition.rb +8 -38
  47. data/lib/familia/horreum/management.rb +38 -27
  48. data/lib/familia/horreum/persistence.rb +191 -67
  49. data/lib/familia/horreum/serialization.rb +94 -73
  50. data/lib/familia/horreum/utils.rb +0 -8
  51. data/lib/familia/horreum.rb +41 -18
  52. data/lib/familia/identifier_extractor.rb +60 -0
  53. data/lib/familia/logging.rb +268 -112
  54. data/lib/familia/refinements.rb +0 -1
  55. data/lib/familia/settings.rb +7 -7
  56. data/lib/familia/version.rb +1 -1
  57. data/lib/familia.rb +2 -2
  58. data/lib/middleware/{database_middleware.rb → database_logger.rb} +118 -14
  59. data/pr_agent.toml +31 -0
  60. data/pr_compliance_checklist.yaml +45 -0
  61. data/try/edge_cases/empty_identifiers_try.rb +1 -1
  62. data/try/edge_cases/hash_symbolization_try.rb +31 -31
  63. data/try/edge_cases/json_serialization_try.rb +2 -2
  64. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +170 -0
  65. data/try/edge_cases/race_conditions_try.rb +1 -1
  66. data/try/edge_cases/reserved_keywords_try.rb +1 -1
  67. data/try/edge_cases/string_coercion_try.rb +5 -5
  68. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  69. data/try/features/encrypted_fields/aad_protection_try.rb +1 -1
  70. data/try/features/encrypted_fields/concealed_string_core_try.rb +1 -1
  71. data/try/features/encrypted_fields/context_isolation_try.rb +1 -1
  72. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  73. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +1 -1
  74. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +1 -1
  75. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +1 -1
  76. data/try/features/encrypted_fields/error_conditions_try.rb +1 -1
  77. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +1 -1
  78. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  79. data/try/features/encrypted_fields/key_rotation_try.rb +1 -1
  80. data/try/features/encrypted_fields/memory_security_try.rb +1 -1
  81. data/try/features/encrypted_fields/missing_current_key_version_try.rb +1 -1
  82. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  83. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +1 -1
  84. data/try/features/encrypted_fields/thread_safety_try.rb +1 -1
  85. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +1 -1
  86. data/try/{encryption → features/encryption}/config_persistence_try.rb +1 -1
  87. data/try/{encryption/encryption_core_try.rb → features/encryption/core_try.rb} +2 -2
  88. data/try/{encryption → features/encryption}/instance_variable_scope_try.rb +1 -1
  89. data/try/{encryption → features/encryption}/module_loading_try.rb +1 -1
  90. data/try/{encryption → features/encryption}/providers/aes_gcm_provider_try.rb +1 -1
  91. data/try/{encryption → features/encryption}/providers/xchacha20_poly1305_provider_try.rb +1 -1
  92. data/try/{encryption → features/encryption}/roundtrip_validation_try.rb +1 -1
  93. data/try/{encryption → features/encryption}/secure_memory_handling_try.rb +2 -2
  94. data/try/features/expiration/expiration_try.rb +2 -2
  95. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  96. data/try/features/feature_dependencies_try.rb +1 -1
  97. data/try/features/feature_improvements_try.rb +1 -1
  98. data/try/features/object_identifier/object_identifier_integration_try.rb +1 -1
  99. data/try/features/object_identifier/object_identifier_try.rb +1 -1
  100. data/try/features/quantization/quantization_try.rb +1 -1
  101. data/try/features/real_feature_integration_try.rb +17 -14
  102. data/try/features/relationships/indexing_commands_verification_try.rb +8 -3
  103. data/try/features/relationships/indexing_try.rb +34 -5
  104. data/try/features/relationships/participation_commands_verification_spec.rb +1 -1
  105. data/try/features/relationships/participation_commands_verification_try.rb +4 -4
  106. data/try/features/relationships/participation_performance_improvements_try.rb +1 -1
  107. data/try/features/relationships/participation_reverse_index_try.rb +1 -1
  108. data/try/features/relationships/relationships_api_changes_try.rb +5 -5
  109. data/try/features/relationships/relationships_edge_cases_try.rb +3 -3
  110. data/try/features/relationships/relationships_performance_minimal_try.rb +1 -1
  111. data/try/features/relationships/relationships_performance_simple_try.rb +1 -1
  112. data/try/features/relationships/relationships_performance_try.rb +1 -1
  113. data/try/features/relationships/relationships_performance_working_try.rb +1 -1
  114. data/try/features/relationships/relationships_try.rb +1 -1
  115. data/try/features/safe_dump/safe_dump_advanced_try.rb +1 -1
  116. data/try/features/safe_dump/safe_dump_try.rb +1 -1
  117. data/try/features/transient_fields/redacted_string_try.rb +1 -1
  118. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  119. data/try/features/transient_fields/single_use_redacted_string_try.rb +1 -1
  120. data/try/features/transient_fields/transient_fields_core_try.rb +1 -1
  121. data/try/features/transient_fields/transient_fields_integration_try.rb +1 -1
  122. data/try/{connection → integration/connection}/fiber_context_preservation_try.rb +4 -4
  123. data/try/{connection → integration/connection}/handler_constraints_try.rb +1 -1
  124. data/try/{core → integration/connection}/isolated_dbclient_try.rb +1 -1
  125. data/try/integration/connection/middleware_reconnect_try.rb +87 -0
  126. data/try/{connection → integration/connection}/operation_mode_guards_try.rb +2 -2
  127. data/try/{connection → integration/connection}/pipeline_fallback_integration_try.rb +13 -13
  128. data/try/{core → integration/connection}/pools_try.rb +1 -1
  129. data/try/{connection → integration/connection}/responsibility_chain_tracking_try.rb +1 -1
  130. data/try/{connection → integration/connection}/transaction_fallback_integration_try.rb +1 -1
  131. data/try/{connection → integration/connection}/transaction_mode_permissive_try.rb +1 -1
  132. data/try/{connection → integration/connection}/transaction_mode_strict_try.rb +1 -1
  133. data/try/{connection → integration/connection}/transaction_mode_warn_try.rb +1 -1
  134. data/try/{connection → integration/connection}/transaction_modes_try.rb +1 -1
  135. data/try/{core → integration}/conventional_inheritance_try.rb +1 -1
  136. data/try/{core → integration}/create_method_try.rb +23 -23
  137. data/try/integration/cross_component_try.rb +1 -1
  138. data/try/integration/data_types/datatype_pipelines_try.rb +104 -0
  139. data/try/integration/data_types/datatype_transactions_try.rb +247 -0
  140. data/try/{core → integration}/database_consistency_try.rb +11 -8
  141. data/try/{core → integration}/familia_extended_try.rb +1 -1
  142. data/try/{core → integration}/familia_members_methods_try.rb +1 -1
  143. data/try/{models → integration/models}/customer_safe_dump_try.rb +6 -2
  144. data/try/{models → integration/models}/customer_try.rb +1 -1
  145. data/try/{models → integration/models}/datatype_base_try.rb +1 -1
  146. data/try/{models → integration/models}/familia_object_try.rb +2 -2
  147. data/try/{core → integration}/persistence_operations_try.rb +163 -11
  148. data/try/integration/relationships_persistence_round_trip_try.rb +441 -0
  149. data/try/{configuration → integration}/scenarios_try.rb +1 -1
  150. data/try/{core → integration}/secure_identifier_try.rb +1 -1
  151. data/try/{core → integration}/verifiable_identifier_try.rb +1 -1
  152. data/try/performance/benchmarks_try.rb +2 -2
  153. data/try/support/benchmarks/deserialization_benchmark.rb +180 -0
  154. data/try/support/benchmarks/deserialization_correctness_test.rb +237 -0
  155. data/try/{helpers → support/helpers}/test_helpers.rb +12 -3
  156. data/try/{core → unit/core}/autoloader_try.rb +1 -1
  157. data/try/{core → unit/core}/base_enhancements_try.rb +1 -9
  158. data/try/{core → unit/core}/connection_try.rb +1 -1
  159. data/try/{core → unit/core}/errors_try.rb +1 -1
  160. data/try/{core → unit/core}/extensions_try.rb +1 -1
  161. data/try/unit/core/familia_logger_try.rb +110 -0
  162. data/try/{core → unit/core}/familia_try.rb +1 -1
  163. data/try/{core → unit/core}/middleware_try.rb +41 -1
  164. data/try/{core → unit/core}/settings_try.rb +1 -1
  165. data/try/{core → unit/core}/time_utils_try.rb +1 -1
  166. data/try/{core → unit/core}/tools_try.rb +1 -1
  167. data/try/{core → unit/core}/utils_try.rb +17 -14
  168. data/try/{data_types → unit/data_types}/boolean_try.rb +2 -2
  169. data/try/{data_types → unit/data_types}/counter_try.rb +1 -1
  170. data/try/{data_types → unit/data_types}/datatype_base_try.rb +1 -1
  171. data/try/{data_types → unit/data_types}/hash_try.rb +1 -1
  172. data/try/{data_types → unit/data_types}/list_try.rb +1 -1
  173. data/try/{data_types → unit/data_types}/lock_try.rb +1 -1
  174. data/try/{data_types → unit/data_types}/sorted_set_try.rb +1 -1
  175. data/try/{data_types → unit/data_types}/sorted_set_zadd_options_try.rb +1 -1
  176. data/try/{data_types → unit/data_types}/string_try.rb +2 -2
  177. data/try/{data_types → unit/data_types}/unsortedset_try.rb +1 -1
  178. data/try/{horreum → unit/horreum}/auto_indexing_on_save_try.rb +33 -17
  179. data/try/unit/horreum/automatic_index_validation_try.rb +253 -0
  180. data/try/{horreum → unit/horreum}/base_try.rb +4 -4
  181. data/try/{horreum → unit/horreum}/class_methods_try.rb +3 -3
  182. data/try/{horreum → unit/horreum}/commands_try.rb +1 -1
  183. data/try/{horreum → unit/horreum}/defensive_initialization_try.rb +1 -1
  184. data/try/{horreum → unit/horreum}/destroy_related_fields_cleanup_try.rb +1 -1
  185. data/try/{horreum → unit/horreum}/enhanced_conflict_handling_try.rb +1 -1
  186. data/try/{horreum → unit/horreum}/field_categories_try.rb +27 -18
  187. data/try/{horreum → unit/horreum}/field_definition_try.rb +1 -1
  188. data/try/{horreum → unit/horreum}/initialization_try.rb +3 -3
  189. data/try/unit/horreum/json_type_preservation_try.rb +248 -0
  190. data/try/{horreum → unit/horreum}/relations_try.rb +5 -5
  191. data/try/{horreum → unit/horreum}/serialization_persistent_fields_try.rb +24 -18
  192. data/try/{horreum → unit/horreum}/serialization_try.rb +6 -6
  193. data/try/{horreum → unit/horreum}/settings_try.rb +1 -1
  194. data/try/unit/horreum/unique_index_edge_cases_try.rb +376 -0
  195. data/try/unit/horreum/unique_index_guard_validation_try.rb +281 -0
  196. data/try/{refinements → unit/refinements}/dear_json_array_methods_try.rb +1 -1
  197. data/try/{refinements → unit/refinements}/dear_json_hash_methods_try.rb +1 -1
  198. data/try/{refinements → unit/refinements}/time_literals_numeric_methods_try.rb +1 -1
  199. data/try/{refinements → unit/refinements}/time_literals_string_methods_try.rb +1 -1
  200. metadata +147 -126
  201. data/lib/familia/distinguisher.rb +0 -85
  202. data/lib/familia/refinements/logger_trace.rb +0 -60
  203. data/try/refinements/logger_trace_methods_try.rb +0 -44
  204. /data/try/{debugging → support/debugging}/README.md +0 -0
  205. /data/try/{debugging → support/debugging}/cache_behavior_tracer.rb +0 -0
  206. /data/try/{debugging → support/debugging}/debug_aad_process.rb +0 -0
  207. /data/try/{debugging → support/debugging}/debug_concealed_internal.rb +0 -0
  208. /data/try/{debugging → support/debugging}/debug_concealed_reveal.rb +0 -0
  209. /data/try/{debugging → support/debugging}/debug_context_aad.rb +0 -0
  210. /data/try/{debugging → support/debugging}/debug_context_simple.rb +0 -0
  211. /data/try/{debugging → support/debugging}/debug_cross_context.rb +0 -0
  212. /data/try/{debugging → support/debugging}/debug_database_load.rb +0 -0
  213. /data/try/{debugging → support/debugging}/debug_encrypted_json_check.rb +0 -0
  214. /data/try/{debugging → support/debugging}/debug_encrypted_json_step_by_step.rb +0 -0
  215. /data/try/{debugging → support/debugging}/debug_exists_lifecycle.rb +0 -0
  216. /data/try/{debugging → support/debugging}/debug_field_decrypt.rb +0 -0
  217. /data/try/{debugging → support/debugging}/debug_fresh_cross_context.rb +0 -0
  218. /data/try/{debugging → support/debugging}/debug_load_path.rb +0 -0
  219. /data/try/{debugging → support/debugging}/debug_method_definition.rb +0 -0
  220. /data/try/{debugging → support/debugging}/debug_method_resolution.rb +0 -0
  221. /data/try/{debugging → support/debugging}/debug_minimal.rb +0 -0
  222. /data/try/{debugging → support/debugging}/debug_provider.rb +0 -0
  223. /data/try/{debugging → support/debugging}/debug_secure_behavior.rb +0 -0
  224. /data/try/{debugging → support/debugging}/debug_string_class.rb +0 -0
  225. /data/try/{debugging → support/debugging}/debug_test.rb +0 -0
  226. /data/try/{debugging → support/debugging}/debug_test_design.rb +0 -0
  227. /data/try/{debugging → support/debugging}/encryption_method_tracer.rb +0 -0
  228. /data/try/{debugging → support/debugging}/provider_diagnostics.rb +0 -0
  229. /data/try/{helpers → support/helpers}/test_cleanup.rb +0 -0
  230. /data/try/{memory → support/memory}/memory_basic_test.rb +0 -0
  231. /data/try/{memory → support/memory}/memory_detailed_test.rb +0 -0
  232. /data/try/{memory → support/memory}/memory_docker_ruby_dump.sh +0 -0
  233. /data/try/{memory → support/memory}/memory_search_for_string.rb +0 -0
  234. /data/try/{memory → support/memory}/test_actual_redactedstring_protection.rb +0 -0
  235. /data/try/{prototypes → support/prototypes}/atomic_saves_v1_context_proxy.rb +0 -0
  236. /data/try/{prototypes → support/prototypes}/atomic_saves_v2_connection_switching.rb +0 -0
  237. /data/try/{prototypes → support/prototypes}/atomic_saves_v3_connection_pool.rb +0 -0
  238. /data/try/{prototypes → support/prototypes}/atomic_saves_v4.rb +0 -0
  239. /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v2_connection_switching_helpers.rb +0 -0
  240. /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  241. /data/try/{prototypes → support/prototypes}/pooling/README.md +0 -0
  242. /data/try/{prototypes → support/prototypes}/pooling/configurable_stress_test.rb +0 -0
  243. /data/try/{prototypes → support/prototypes}/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  244. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_metrics.rb +0 -0
  245. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_stress_test.rb +0 -0
  246. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_threading_models.rb +0 -0
  247. /data/try/{prototypes → support/prototypes}/pooling/lib/visualize_stress_results.rb +0 -0
  248. /data/try/{prototypes → support/prototypes}/pooling/pool_siege.rb +0 -0
  249. /data/try/{prototypes → support/prototypes}/pooling/run_stress_tests.rb +0 -0
@@ -3,7 +3,7 @@
3
3
  # Security tests for the no-cache encryption strategy
4
4
  # These tests verify that we maintain security properties by NOT caching derived keys
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  test_keys = {
9
9
  v1: Base64.strict_encode64('a' * 32),
@@ -1,6 +1,6 @@
1
1
  # try/features/encrypted_fields_security_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
  require 'base64'
5
5
 
6
6
  # Define all test classes up front to avoid tryouts retry conflicts
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'base64'
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  # Setup encryption keys for error testing
8
8
  @test_keys = {
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'base64'
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  test_keys = {
8
8
  v1: Base64.strict_encode64('a' * 32),
@@ -1,4 +1,4 @@
1
- require_relative '../../helpers/test_helpers'
1
+ require_relative '../../support/helpers/test_helpers'
2
2
  require 'base64'
3
3
 
4
4
  # Setup encryption configuration
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'base64'
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  # Setup multiple key versions for rotation testing
8
8
  @test_keys = {
@@ -1,7 +1,7 @@
1
1
  # try/features/encryption_fields/memory_security_try.rb
2
2
 
3
3
  require 'base64'
4
- require_relative '../../helpers/test_helpers'
4
+ require_relative '../../support/helpers/test_helpers'
5
5
 
6
6
  test_keys = {
7
7
  v1: Base64.strict_encode64('a' * 32),
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'base64'
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  # This tryouts file is based on the premise that there is no current key
8
8
  # version set. This is a global setting so if other tryouts rely on
@@ -3,7 +3,7 @@
3
3
  require 'base64'
4
4
  require 'set'
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  test_keys = {
9
9
  v1: Base64.strict_encode64('a' * 32),
@@ -1,6 +1,6 @@
1
1
  # try/features/encryption_fields/secure_by_default_behavior_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
  require 'base64'
5
5
 
6
6
  Familia.debug = false
@@ -3,7 +3,7 @@
3
3
  require 'concurrent'
4
4
  require 'base64'
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  # Setup encryption keys for testing
9
9
  test_keys = {
@@ -1,6 +1,6 @@
1
1
  # try/features/encryption_fields/universal_serialization_safety_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
  require 'base64'
5
5
 
6
6
  Familia.debug = false
@@ -5,7 +5,7 @@
5
5
 
6
6
  require 'base64'
7
7
 
8
- require_relative '../helpers/test_helpers'
8
+ require_relative '../../support/helpers/test_helpers'
9
9
  require 'familia/encryption/providers/xchacha20_poly1305_provider'
10
10
 
11
11
  # SETUP
@@ -1,7 +1,7 @@
1
1
  # try/encryption/encryption_core_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
4
- require_relative '../../lib/familia/encryption'
3
+ require_relative '../../support/helpers/test_helpers'
4
+ require_relative '../../../lib/familia/encryption'
5
5
  require 'base64'
6
6
 
7
7
  # Test constants will be redefined in each test since variables don't persist
@@ -6,7 +6,7 @@
6
6
 
7
7
  require 'base64'
8
8
 
9
- require_relative '../helpers/test_helpers'
9
+ require_relative '../../support/helpers/test_helpers'
10
10
 
11
11
  @test_keys = {
12
12
  v1: Base64.strict_encode64('a' * 32)
@@ -5,7 +5,7 @@
5
5
 
6
6
  require 'base64'
7
7
 
8
- require_relative '../helpers/test_helpers'
8
+ require_relative '../../support/helpers/test_helpers'
9
9
 
10
10
  # Test basic functionality
11
11
 
@@ -1,6 +1,6 @@
1
1
  # try/encryption/providers/aes_gcm_provider_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../../support/helpers/test_helpers'
4
4
  require 'base64'
5
5
 
6
6
  ## AES-GCM provider availability check (always available with OpenSSL)
@@ -1,6 +1,6 @@
1
1
  # try/encryption/providers/xchacha20_poly1305_provider_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../../support/helpers/test_helpers'
4
4
  require 'base64'
5
5
 
6
6
  ## XChaCha20Poly1305 provider availability check
@@ -5,7 +5,7 @@
5
5
 
6
6
  require 'base64'
7
7
 
8
- require_relative '../helpers/test_helpers'
8
+ require_relative '../../support/helpers/test_helpers'
9
9
 
10
10
  ## Test successful encryption
11
11
  test_keys = { v1: Base64.strict_encode64('a' * 32) }
@@ -1,7 +1,7 @@
1
1
  # try/encryption/secure_memory_handling_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
4
- require_relative '../../lib/familia/encryption/providers/secure_xchacha20_poly1305_provider'
3
+ require_relative '../../support/helpers/test_helpers'
4
+ require_relative '../../../lib/familia/encryption/providers/secure_xchacha20_poly1305_provider'
5
5
  require 'base64'
6
6
 
7
7
  # SETUP
@@ -1,6 +1,6 @@
1
1
  # try/features/expiration_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -58,7 +58,7 @@ ExpiringTest.default_expiration
58
58
  #=> true
59
59
 
60
60
  ## Can call update_expiration method
61
- result = @test_obj.update_expiration(default_expiration: 180)
61
+ result = @test_obj.update_expiration(expiration: 180)
62
62
  [result.class, result]
63
63
  #=> [FalseClass, false]
64
64
 
@@ -1,6 +1,6 @@
1
1
  # try/features/external_identifier/external_identifier_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/feature_dependencies_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/feature_improvements_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../support/helpers/test_helpers'
4
4
 
5
5
  # Test hierarchical feature registration
6
6
  class ::TestClass
@@ -1,6 +1,6 @@
1
1
  # try/features/object_identifier/object_identifier_integration_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/object_identifier/object_identifier_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/quantization_try.rb
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/real_feature_integration_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -12,15 +12,17 @@ class ExpirationIntegrationTest < Familia::Horreum
12
12
  feature :expiration
13
13
  end
14
14
 
15
- # Safe dump feature integration with field categories
15
+ # Safe dump feature integration with field types
16
16
  class SafeDumpCategoryTest < Familia::Horreum
17
+ feature :encrypted_fields
18
+ feature :transient_fields
19
+ feature :safe_dump
20
+
17
21
  identifier_field :id
18
22
  field :id
19
- field :public_name, category: :persistent
20
- field :email, category: :encrypted
21
- field :tryouts_cache_data, category: :transient
22
-
23
- feature :safe_dump
23
+ field :public_name
24
+ encrypted_field :email
25
+ transient_field :tryouts_cache_data
24
26
 
25
27
  # Use new SafeDump DSL
26
28
  safe_dump_field :id
@@ -30,14 +32,15 @@ end
30
32
 
31
33
  # Combined features work together
32
34
  class CombinedFeaturesTest < Familia::Horreum
33
- identifier_field :id
34
- field :id
35
- field :name, category: :persistent
36
- field :temp_data, category: :transient
37
-
35
+ feature :transient_fields
38
36
  feature :expiration
39
37
  feature :safe_dump
40
38
 
39
+ identifier_field :id
40
+ field :id
41
+ field :name
42
+ transient_field :temp_data
43
+
41
44
  # Use new SafeDump DSL
42
45
  safe_dump_field :id
43
46
  safe_dump_field :name
@@ -123,11 +126,11 @@ CombinedFeaturesTest.features_enabled.include?(:safe_dump)
123
126
 
124
127
  ## Test that feature() method returns current features when called with no args
125
128
  CombinedFeaturesTest.feature
126
- #=> [:expiration, :safe_dump]
129
+ #=> [:transient_fields, :expiration, :safe_dump]
127
130
 
128
131
  ## Test that features_enabled() method returns the same results as feature() method
129
132
  CombinedFeaturesTest.feature
130
- #=> [:expiration, :safe_dump]
133
+ #=> [:transient_fields, :expiration, :safe_dump]
131
134
 
132
135
  ## Features list is accessible
133
136
  QueryFeaturesTest.feature
@@ -4,7 +4,7 @@
4
4
  # This test ensures the indexing system uses proper DataType methods instead of direct Redis calls
5
5
  #
6
6
 
7
- require_relative '../../helpers/test_helpers'
7
+ require_relative '../../support/helpers/test_helpers'
8
8
 
9
9
  # Enable database command logging for command verification tests
10
10
  Familia.enable_database_logging = true
@@ -44,8 +44,11 @@ end
44
44
 
45
45
  # Test data
46
46
  @user = TestIndexedUser.new(user_id: 'test_user_123', email: 'test@example.com', department: 'engineering')
47
+ @user.save
47
48
  @company = TestIndexedCompany.new(company_id: 'test_company_456', name: 'Test Corp')
49
+ @company.save
48
50
  @employee = TestIndexedEmployee.new(emp_id: 'test_emp_789', email: 'emp@example.com', department: 'sales')
51
+ @employee.save
49
52
 
50
53
  ## Class-level indexing creates proper DataType field
51
54
  TestIndexedUser.respond_to?(:email_index)
@@ -91,6 +94,7 @@ end
91
94
  #=> false
92
95
 
93
96
  ## Instance-level indexing works with parent context
97
+ @employee.save
94
98
  @employee.add_to_test_indexed_company_dept_index(@company)
95
99
  sample = @company.sample_from_department('sales')
96
100
  sample.first&.emp_id == @employee.emp_id
@@ -103,15 +107,16 @@ dept_index.class.name
103
107
 
104
108
  ## Multiple employees in same department
105
109
  @employee2 = TestIndexedEmployee.new(emp_id: 'test_emp_999', email: 'emp2@example.com', department: 'sales')
110
+ @employee2.save
106
111
  @employee2.add_to_test_indexed_company_dept_index(@company)
107
112
  employees_in_sales = @company.find_all_by_department('sales')
108
- employees_in_sales.map(&:emp_id).sort
113
+ employees_in_sales&.map(&:emp_id).sort
109
114
  #=> ["test_emp_789", "test_emp_999"]
110
115
 
111
116
  ## Removing from instance-level index works
112
117
  @employee.remove_from_test_indexed_company_dept_index(@company)
113
118
  remaining_employees = @company.find_all_by_department('sales')
114
- remaining_employees.map(&:emp_id)
119
+ remaining_employees&.map(&:emp_id)
115
120
  #=> ["test_emp_999"]
116
121
 
117
122
  ## Index update methods work correctly
@@ -4,7 +4,7 @@
4
4
  # Tests both multi_index (parent-context) and unique_index (class-level) indexing
5
5
  #
6
6
 
7
- require_relative '../../helpers/test_helpers'
7
+ require_relative '../../support/helpers/test_helpers'
8
8
 
9
9
  # Test classes for indexing functionality
10
10
  class ::TestUser < Familia::Horreum
@@ -55,13 +55,18 @@ end
55
55
 
56
56
  # Setup
57
57
  @user1 = TestUser.new(user_id: 'user_001', email: 'alice@example.com', username: 'alice', department: 'engineering', role: 'developer')
58
+ @user1.save
58
59
  @user2 = TestUser.new(user_id: 'user_002', email: 'bob@example.com', username: 'bob', department: 'marketing', role: 'manager')
60
+ @user2.save
59
61
  @user3 = TestUser.new(user_id: 'user_003', email: 'charlie@example.com', username: 'charlie', department: 'engineering', role: 'lead')
62
+ @user3.save
60
63
 
61
64
  @company_id = "comp_#{rand(10000000)}"
62
- @company = TestCompany.create(company_id: @company_id, name: 'Acme Corp')
65
+ @company = TestCompany.create!(company_id: @company_id, name: 'Acme Corp')
63
66
  @emp1 = TestEmployee.new(emp_id: 'emp_001', email: 'alice@acme.com', department: 'engineering', manager_id: 'mgr_001', badge_number: 'BADGE001')
67
+ @emp1.save
64
68
  @emp2 = TestEmployee.new(emp_id: 'emp_002', email: 'bob@acme.com', department: 'sales', manager_id: 'mgr_002', badge_number: 'BADGE002')
69
+ @emp2.save
65
70
 
66
71
 
67
72
  ## Context-scoped methods require context parameter
@@ -81,7 +86,7 @@ sample = @company.sample_from_department(@emp2.department)
81
86
 
82
87
  ## First indexing relationship has correct configuration
83
88
  config = @user1.class.indexing_relationships.first
84
- [config.field, config.index_name, config.target_class == TestUser, config.query]
89
+ [config.field, config.index_name, config.scope_class == TestUser, config.query]
85
90
  #=> [:email, :email_lookup, true, true]
86
91
 
87
92
  ## Second indexing relationship has query disabled
@@ -184,7 +189,7 @@ TestUser.respond_to?(:find_by_username)
184
189
 
185
190
  ## Instance-scoped unique index has correct configuration
186
191
  config = @emp1.class.indexing_relationships.find { |r| r.field == :badge_number }
187
- [config.index_name, config.target_class, config.cardinality]
192
+ [config.index_name, config.scope_class, config.cardinality]
188
193
  #=> [:badge_index, TestCompany, :unique]
189
194
 
190
195
  ## Target class gets finder method for unique index
@@ -246,6 +251,16 @@ found_emps = @company.find_all_by_badge_number('BADGE002')
246
251
  found_emps.map(&:emp_id)
247
252
  #=> ["emp_002"]
248
253
 
254
+ ## Instance-scoped bulk query filters nil inputs
255
+ badges_with_nil = [nil, 'BADGE001', nil]
256
+ found_emps = @company.find_all_by_badge_number(badges_with_nil)
257
+ found_emps.map(&:emp_id)
258
+ #=> ["emp_001"]
259
+
260
+ ## Instance-scoped bulk query with only nil returns empty
261
+ @company.find_all_by_badge_number([nil, nil]).length
262
+ #=> 0
263
+
249
264
  ## Update badge index entry
250
265
  old_badge = @emp1.badge_number
251
266
  @emp1.badge_number = 'BADGE001_NEW'
@@ -272,7 +287,7 @@ old_badge = @emp1.badge_number
272
287
 
273
288
  ## Context-scoped multi_index relationship has correct configuration
274
289
  config = @emp1.class.indexing_relationships.find { |r| r.field == :department }
275
- [config.field, config.index_name, config.target_class]
290
+ [config.field, config.index_name, config.scope_class]
276
291
  #=> [:department, :dept_index, TestCompany]
277
292
 
278
293
  ## Context-scoped methods are generated with collision-free naming
@@ -415,6 +430,20 @@ found = TestUser.find_all_by_email(emails)
415
430
  found.map(&:user_id)
416
431
  #=> ["user_002"]
417
432
 
433
+ ## Bulk query filters nil inputs before querying
434
+ emails_with_nil = [nil, 'bob@example.com', nil]
435
+ found = TestUser.find_all_by_email(emails_with_nil)
436
+ found.map(&:user_id)
437
+ #=> ["user_002"]
438
+
439
+ ## Bulk query with only nil inputs returns empty array
440
+ TestUser.find_all_by_email([nil, nil]).length
441
+ #=> 0
442
+
443
+ ## Bulk query with nil as single value returns empty array
444
+ TestUser.find_all_by_email(nil).length
445
+ #=> 0
446
+
418
447
  ## Adding to index with nil field value does nothing
419
448
  @user_nil = TestUser.new(user_id: 'user_nil', email: nil)
420
449
  @user_nil.add_to_class_email_lookup
@@ -7,7 +7,7 @@ RSpec.describe 'participation_commands_verification_try' do
7
7
  Timecop.freeze(Time.parse("2024-01-15 10:30:00"))
8
8
  puts Time.now # Always returns 2024-01-15 10:30:00
9
9
  puts Date.today # Always returns 2024-01-15
10
- require_relative '../../../lib/middleware/database_middleware'
10
+ require_relative '../../../lib/middleware/database_logger'
11
11
  require_relative '../../../lib/familia'
12
12
  Familia.enable_database_logging = true
13
13
  Familia.enable_database_counter = true
@@ -17,7 +17,7 @@ puts Date.today # Always returns 2024-01-15
17
17
 
18
18
 
19
19
  # Load middleware first
20
- require_relative '../../../lib/middleware/database_middleware'
20
+ require_relative '../../../lib/middleware/database_logger'
21
21
 
22
22
  # Load Familia
23
23
  require_relative '../../../lib/familia'
@@ -83,7 +83,7 @@ instantiation_commands.empty?
83
83
  database_commands = DatabaseLogger.capture_commands do
84
84
  @customer.save
85
85
  end
86
- database_commands.map { |cmd| cmd[:command] } if database_commands
86
+ database_commands.map { |cmd| cmd.command } if database_commands
87
87
  ##=> [["hmset", "reverse_index_customer:ri_cust_123:object", "customer_id", "ri_cust_123", "name", "Reverse Index Test Customer"], ["zadd", "reverse_index_customer:instances", "1705343400.0", "ri_cust_123"]]
88
88
 
89
89
 
@@ -91,14 +91,14 @@ database_commands.map { |cmd| cmd[:command] } if database_commands
91
91
  database_commands = DatabaseLogger.capture_commands do
92
92
  @domain1.save
93
93
  end
94
- database_commands[0][:command] if database_commands && database_commands[0]
94
+ database_commands[0].command if database_commands && database_commands[0]
95
95
  ##=> ["hmset", "reverse_index_domain:ri_dom_1:object", "domain_id", "ri_dom_1", "display_domain", "example1.com", "created_at", "1705343400.0"]
96
96
 
97
97
  ## Domain2 save functionality
98
98
  database_commands = DatabaseLogger.capture_commands do
99
99
  @domain2.save
100
100
  end
101
- database_commands[0][:command]if database_commands && database_commands[0]
101
+ database_commands[0].command if database_commands && database_commands[0]
102
102
  ##=> ["hmset", "reverse_index_domain:ri_dom_2:object", "domain_id", "ri_dom_2", "display_domain", "example2.com", "created_at", "1705343401.0"]
103
103
 
104
104
 
@@ -3,7 +3,7 @@
3
3
  # Tests for performance improvements in participation functionality
4
4
  # Verifies reverse index functionality and robust type comparison
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  # Test classes for performance improvements
9
9
  class PerfTestCustomer < Familia::Horreum
@@ -3,7 +3,7 @@
3
3
  # Tests for participation reverse index functionality
4
4
  # Verifies performance improvements and correct behavior
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  # Test classes for reverse index functionality
9
9
  class ReverseIndexCustomer < Familia::Horreum
@@ -4,7 +4,7 @@
4
4
  # Testing new class_participates_in and unique_index methods
5
5
  # Testing breaking changes and argument validation
6
6
 
7
- require_relative '../../helpers/test_helpers'
7
+ require_relative '../../support/helpers/test_helpers'
8
8
 
9
9
  # Test classes for new API
10
10
  class ::ApiTestUser < Familia::Horreum
@@ -264,17 +264,17 @@ participation_meta.target_class.familia_name
264
264
 
265
265
  ## unique_index stores correct target_class
266
266
  indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
267
- indexing_meta.target_class
267
+ indexing_meta.scope_class
268
268
  #=> ApiTestUser
269
269
 
270
- ## unique_index stores correct target_class via familia_name
270
+ ## unique_index stores correct scope_class via familia_name
271
271
  indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
272
- indexing_meta.target_class.familia_name
272
+ indexing_meta.scope_class.familia_name
273
273
  #=> 'ApiTestUser'
274
274
 
275
275
  ## multi_index with within: stores correct metadata
276
276
  membership_meta = ApiTestMembership.indexing_relationships.find { |r| r.index_name == :user_memberships }
277
- membership_meta.target_class
277
+ membership_meta.scope_class
278
278
  #=> ApiTestUser
279
279
 
280
280
  # =============================================
@@ -1,6 +1,6 @@
1
1
  # Simplified edge case testing for Relationships v2 - focusing on core functionality
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  # Test classes for edge case testing
6
6
  class EdgeTestCustomer < Familia::Horreum
@@ -41,14 +41,14 @@ end
41
41
  @domain1 = EdgeTestDomain.new(
42
42
  domain_id: 'edge_dom_1',
43
43
  display_domain: 'edge1.example.com',
44
- created_at: Time.new(2025, 6, 15, 12, 0, 0),
44
+ created_at: Time.new(2025, 6, 15, 12, 0, 0).to_i, # Convert Time to Integer for JSON serialization
45
45
  score_value: 10
46
46
  )
47
47
 
48
48
  @domain2 = EdgeTestDomain.new(
49
49
  domain_id: 'edge_dom_2',
50
50
  display_domain: 'edge2.example.com',
51
- created_at: Time.new(2025, 7, 20, 15, 30, 0),
51
+ created_at: Time.new(2025, 7, 20, 15, 30, 0).to_i, # Convert Time to Integer for JSON serialization
52
52
  score_value: 25
53
53
  )
54
54
 
@@ -1,6 +1,6 @@
1
1
  # Minimal performance testing focusing on core Familia functionality
2
2
 
3
- require_relative '../../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
  require 'benchmark'
5
5
 
6
6
  # Simple test classes without relationships feature
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Simplified performance testing for the Relationships feature
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
  require 'benchmark'
7
7
 
8
8
  # Test classes for performance testing
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Performance and integration testing for the Relationships feature
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
  require 'benchmark'
7
7
 
8
8
  # Test classes for performance testing
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Working performance test focusing on basic functionality
4
4
 
5
- require_relative '../../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
  require 'benchmark'
7
7
 
8
8
  # Simple test class using only basic Familia features
@@ -3,7 +3,7 @@
3
3
  # Simplified Familia v2 relationship functionality tests - focusing on core working features
4
4
  #
5
5
 
6
- require_relative '../../helpers/test_helpers'
6
+ require_relative '../../support/helpers/test_helpers'
7
7
 
8
8
  # Test classes for Familia v2 relationship functionality
9
9
  class TestCustomer < Familia::Horreum