familia 2.0.0.pre14 → 2.0.0.pre16

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 (276) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/code-quality.yml +138 -0
  3. data/.github/workflows/code-smellage.yml +145 -0
  4. data/.github/workflows/docs.yml +31 -8
  5. data/.gitignore +1 -1
  6. data/.pre-commit-config.yaml +7 -1
  7. data/.reek.yml +98 -0
  8. data/.rubocop.yml +48 -10
  9. data/.talismanrc +9 -0
  10. data/.yardopts +18 -13
  11. data/CHANGELOG.rst +66 -6
  12. data/CLAUDE.md +1 -1
  13. data/Gemfile +6 -5
  14. data/Gemfile.lock +99 -23
  15. data/LICENSE.txt +1 -1
  16. data/README.md +285 -85
  17. data/changelog.d/README.md +2 -2
  18. data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
  19. data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
  20. data/docs/archive/FAMILIA_UPDATE.md +3 -3
  21. data/docs/archive/README.md +3 -2
  22. data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
  23. data/docs/conf.py +29 -0
  24. data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
  25. data/docs/guides/feature-encrypted-fields.md +785 -0
  26. data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
  27. data/docs/guides/feature-external-identifiers.md +637 -0
  28. data/docs/guides/feature-object-identifiers.md +435 -0
  29. data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
  30. data/docs/guides/feature-relationships-methods.md +684 -0
  31. data/docs/guides/feature-relationships.md +200 -0
  32. data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
  33. data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
  34. data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
  35. data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
  36. data/docs/guides/index.md +176 -0
  37. data/docs/guides/{Security-Model.md → security-model.md} +1 -1
  38. data/docs/migrating/v2.0.0-pre.md +1 -1
  39. data/docs/migrating/v2.0.0-pre11.md +4 -4
  40. data/docs/migrating/v2.0.0-pre12.md +2 -2
  41. data/docs/migrating/v2.0.0-pre13.md +1 -1
  42. data/docs/migrating/v2.0.0-pre5.md +33 -12
  43. data/docs/migrating/v2.0.0-pre6.md +2 -2
  44. data/docs/migrating/v2.0.0-pre7.md +8 -8
  45. data/docs/overview.md +623 -19
  46. data/docs/reference/api-technical.md +1365 -0
  47. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  48. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  49. data/examples/autoloader/mega_customer.rb +3 -1
  50. data/examples/encrypted_fields.rb +378 -0
  51. data/examples/json_usage_patterns.rb +144 -0
  52. data/examples/relationships.rb +13 -13
  53. data/examples/safe_dump.rb +6 -6
  54. data/examples/single_connection_transaction_confusions.rb +379 -0
  55. data/lib/familia/base.rb +49 -10
  56. data/lib/familia/connection/handlers.rb +223 -0
  57. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  58. data/lib/familia/connection/middleware.rb +75 -0
  59. data/lib/familia/connection/operation_core.rb +93 -0
  60. data/lib/familia/connection/operations.rb +277 -0
  61. data/lib/familia/connection/pipeline_core.rb +87 -0
  62. data/lib/familia/connection/transaction_core.rb +100 -0
  63. data/lib/familia/connection.rb +60 -186
  64. data/lib/familia/data_type/commands.rb +53 -51
  65. data/lib/familia/data_type/serialization.rb +108 -107
  66. data/lib/familia/data_type/types/counter.rb +1 -1
  67. data/lib/familia/data_type/types/hashkey.rb +13 -10
  68. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  69. data/lib/familia/data_type/types/lock.rb +3 -2
  70. data/lib/familia/data_type/types/sorted_set.rb +26 -15
  71. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
  72. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  73. data/lib/familia/data_type.rb +75 -47
  74. data/lib/familia/distinguisher.rb +85 -0
  75. data/lib/familia/encryption/encrypted_data.rb +15 -24
  76. data/lib/familia/encryption/manager.rb +6 -4
  77. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  78. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  79. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  80. data/lib/familia/encryption/request_cache.rb +7 -7
  81. data/lib/familia/encryption.rb +2 -3
  82. data/lib/familia/errors.rb +9 -3
  83. data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
  84. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  85. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  86. data/lib/familia/features/encrypted_fields.rb +68 -66
  87. data/lib/familia/features/expiration/extensions.rb +61 -0
  88. data/lib/familia/features/expiration.rb +35 -87
  89. data/lib/familia/features/external_identifier.rb +11 -12
  90. data/lib/familia/features/object_identifier.rb +58 -20
  91. data/lib/familia/features/quantization.rb +17 -22
  92. data/lib/familia/features/relationships/README.md +97 -0
  93. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  94. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  95. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
  96. data/lib/familia/features/relationships/indexing.rb +176 -256
  97. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  98. data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  100. data/lib/familia/features/relationships/participation.rb +656 -0
  101. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  102. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  103. data/lib/familia/features/relationships.rb +69 -271
  104. data/lib/familia/features/safe_dump.rb +127 -132
  105. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  106. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  107. data/lib/familia/features/transient_fields.rb +5 -5
  108. data/lib/familia/features.rb +21 -21
  109. data/lib/familia/field_type.rb +24 -4
  110. data/lib/familia/horreum/core/connection.rb +229 -26
  111. data/lib/familia/horreum/core/database_commands.rb +27 -17
  112. data/lib/familia/horreum/core/serialization.rb +40 -20
  113. data/lib/familia/horreum/core/utils.rb +2 -1
  114. data/lib/familia/horreum/shared/settings.rb +2 -1
  115. data/lib/familia/horreum/subclass/definition.rb +33 -45
  116. data/lib/familia/horreum/subclass/management.rb +72 -24
  117. data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
  118. data/lib/familia/horreum.rb +196 -114
  119. data/lib/familia/json_serializer.rb +0 -1
  120. data/lib/familia/logging.rb +11 -114
  121. data/lib/familia/refinements/dear_json.rb +122 -0
  122. data/lib/familia/refinements/logger_trace.rb +20 -17
  123. data/lib/familia/refinements/stylize_words.rb +65 -0
  124. data/lib/familia/refinements/time_literals.rb +60 -52
  125. data/lib/familia/refinements.rb +2 -1
  126. data/lib/familia/secure_identifier.rb +60 -28
  127. data/lib/familia/settings.rb +83 -7
  128. data/lib/familia/utils.rb +5 -87
  129. data/lib/familia/verifiable_identifier.rb +4 -4
  130. data/lib/familia/version.rb +1 -1
  131. data/lib/familia.rb +72 -15
  132. data/lib/middleware/database_middleware.rb +56 -14
  133. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  134. data/try/configuration/scenarios_try.rb +1 -1
  135. data/try/connection/fiber_context_preservation_try.rb +250 -0
  136. data/try/connection/handler_constraints_try.rb +59 -0
  137. data/try/connection/operation_mode_guards_try.rb +208 -0
  138. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  139. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  140. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  141. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  142. data/try/connection/transaction_mode_strict_try.rb +98 -0
  143. data/try/connection/transaction_mode_warn_try.rb +131 -0
  144. data/try/connection/transaction_modes_try.rb +249 -0
  145. data/try/core/autoloader_try.rb +129 -11
  146. data/try/core/connection_try.rb +7 -7
  147. data/try/core/conventional_inheritance_try.rb +130 -0
  148. data/try/core/create_method_try.rb +15 -23
  149. data/try/core/database_consistency_try.rb +10 -10
  150. data/try/core/errors_try.rb +8 -11
  151. data/try/core/familia_extended_try.rb +2 -2
  152. data/try/core/familia_members_methods_try.rb +76 -0
  153. data/try/core/isolated_dbclient_try.rb +165 -0
  154. data/try/core/middleware_try.rb +16 -16
  155. data/try/core/persistence_operations_try.rb +4 -4
  156. data/try/core/pools_try.rb +42 -26
  157. data/try/core/secure_identifier_try.rb +28 -24
  158. data/try/core/time_utils_try.rb +10 -10
  159. data/try/core/tools_try.rb +1 -1
  160. data/try/core/utils_try.rb +2 -2
  161. data/try/data_types/boolean_try.rb +4 -4
  162. data/try/data_types/datatype_base_try.rb +0 -2
  163. data/try/data_types/list_try.rb +10 -10
  164. data/try/data_types/sorted_set_try.rb +5 -5
  165. data/try/data_types/string_try.rb +12 -12
  166. data/try/data_types/unsortedset_try.rb +33 -0
  167. data/try/debugging/cache_behavior_tracer.rb +7 -7
  168. data/try/debugging/debug_aad_process.rb +1 -1
  169. data/try/debugging/debug_concealed_internal.rb +1 -1
  170. data/try/debugging/debug_cross_context.rb +1 -1
  171. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  172. data/try/debugging/encryption_method_tracer.rb +10 -10
  173. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  174. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  175. data/try/encryption/config_persistence_try.rb +2 -2
  176. data/try/encryption/encryption_core_try.rb +19 -19
  177. data/try/encryption/instance_variable_scope_try.rb +1 -1
  178. data/try/encryption/module_loading_try.rb +2 -2
  179. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  180. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  181. data/try/encryption/secure_memory_handling_try.rb +1 -1
  182. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  183. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  184. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  185. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  186. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  187. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  188. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  189. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  190. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  191. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  192. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  193. data/try/features/feature_dependencies_try.rb +3 -3
  194. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  195. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  196. data/try/features/quantization/quantization_try.rb +1 -1
  197. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  198. data/try/features/relationships/indexing_try.rb +433 -0
  199. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  200. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  201. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  202. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  203. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  204. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  205. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  206. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  207. data/try/features/relationships/relationships_performance_try.rb +20 -20
  208. data/try/features/relationships/relationships_try.rb +27 -38
  209. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  210. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  211. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  212. data/try/helpers/test_cleanup.rb +86 -0
  213. data/try/helpers/test_helpers.rb +3 -3
  214. data/try/horreum/base_try.rb +3 -2
  215. data/try/horreum/commands_try.rb +1 -1
  216. data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
  217. data/try/horreum/initialization_try.rb +11 -7
  218. data/try/horreum/relations_try.rb +21 -13
  219. data/try/horreum/serialization_try.rb +12 -11
  220. data/try/integration/cross_component_try.rb +3 -3
  221. data/try/memory/memory_basic_test.rb +1 -1
  222. data/try/memory/memory_docker_ruby_dump.sh +1 -1
  223. data/try/models/customer_safe_dump_try.rb +1 -1
  224. data/try/models/customer_try.rb +8 -10
  225. data/try/models/datatype_base_try.rb +3 -3
  226. data/try/models/familia_object_try.rb +9 -8
  227. data/try/performance/benchmarks_try.rb +2 -2
  228. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  229. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  230. data/try/prototypes/atomic_saves_v4.rb +1 -1
  231. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  232. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  233. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  234. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  235. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  236. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  237. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  238. data/try/prototypes/pooling/pool_siege.rb +11 -11
  239. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  240. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  241. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  242. data/try/refinements/logger_trace_methods_try.rb +44 -0
  243. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  244. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  245. metadata +77 -45
  246. data/.rubocop_todo.yml +0 -208
  247. data/docs/connection_pooling.md +0 -192
  248. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  249. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  250. data/docs/guides/Feature-System-Autoloading.md +0 -228
  251. data/docs/guides/Home.md +0 -116
  252. data/docs/guides/Relationships-Guide.md +0 -737
  253. data/docs/guides/relationships-methods.md +0 -266
  254. data/docs/reference/auditing_database_commands.rb +0 -228
  255. data/examples/permissions.rb +0 -240
  256. data/lib/familia/features/autoloadable.rb +0 -113
  257. data/lib/familia/features/relationships/cascading.rb +0 -437
  258. data/lib/familia/features/relationships/membership.rb +0 -497
  259. data/lib/familia/features/relationships/permission_management.rb +0 -264
  260. data/lib/familia/features/relationships/querying.rb +0 -615
  261. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  262. data/lib/familia/features/relationships/tracking.rb +0 -418
  263. data/lib/familia/refinements/snake_case.rb +0 -40
  264. data/lib/familia/validation/command_recorder.rb +0 -336
  265. data/lib/familia/validation/expectations.rb +0 -519
  266. data/lib/familia/validation/validation_helpers.rb +0 -443
  267. data/lib/familia/validation/validator.rb +0 -412
  268. data/lib/familia/validation.rb +0 -140
  269. data/try/data_types/set_try.rb +0 -33
  270. data/try/features/autoloadable/autoloadable_try.rb +0 -61
  271. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  272. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
  273. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  274. data/try/validation/command_validation_try.rb.disabled +0 -207
  275. data/try/validation/performance_validation_try.rb.disabled +0 -324
  276. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -24,13 +24,10 @@ class EdgeTestDomain < Familia::Horreum
24
24
  field :created_at
25
25
  field :score_value
26
26
 
27
- # Test different score calculation methods - simplified
28
- tracked_in EdgeTestCustomer, :domains, score: :created_at, on_destroy: :remove
29
-
30
- # Test different collection types for membership
31
- member_of EdgeTestCustomer, :domains, type: :sorted_set
32
- member_of EdgeTestCustomer, :simple_domains, type: :set
33
- member_of EdgeTestCustomer, :domain_list, type: :list
27
+ # Test different score calculation methods and collection types - simplified
28
+ participates_in EdgeTestCustomer, :domains, score: :created_at, type: :sorted_set
29
+ participates_in EdgeTestCustomer, :simple_domains, type: :set
30
+ participates_in EdgeTestCustomer, :domain_list, type: :list
34
31
 
35
32
  def calculated_score
36
33
  (score_value || 0) * 2
@@ -58,19 +55,19 @@ end
58
55
  # Score encoding edge cases
59
56
 
60
57
  ## Score encoding handles maximum metadata value
61
- max_score = @domain1.encode_score(Time.now, 255)
58
+ max_score = @domain1.encode_score(Familia.now, 255)
62
59
  decoded = @domain1.decode_score(max_score)
63
60
  decoded[:permissions]
64
61
  #=> 255
65
62
 
66
63
  ## Score encoding handles zero metadata
67
- zero_score = @domain1.encode_score(Time.now, 0)
64
+ zero_score = @domain1.encode_score(Familia.now, 0)
68
65
  decoded_zero = @domain1.decode_score(zero_score)
69
66
  decoded_zero[:permissions]
70
67
  #=> 0
71
68
 
72
69
  ## Permission encoding handles unknown permission levels
73
- unknown_perm_score = @domain1.permission_encode(Time.now, :unknown_permission)
70
+ unknown_perm_score = @domain1.permission_encode(Familia.now, :unknown_permission)
74
71
  decoded_unknown = @domain1.permission_decode(unknown_perm_score)
75
72
  decoded_unknown[:permission_list]
76
73
  #=> []
@@ -90,7 +87,7 @@ decoded_large[:permissions]
90
87
  #=> 123
91
88
 
92
89
  ## Permission encoding maps correctly
93
- read_score = @domain1.permission_encode(Time.now, :read)
90
+ read_score = @domain1.permission_encode(Familia.now, :read)
94
91
  decoded_read = @domain1.permission_decode(read_score)
95
92
  decoded_read[:permission_list].include?(:read)
96
93
  #=> true
@@ -102,7 +99,7 @@ decoded_epoch[:permissions]
102
99
  #=> 42
103
100
 
104
101
  ## Boundary score values work correctly
105
- boundary_score = @domain1.encode_score(Time.now, 255)
102
+ boundary_score = @domain1.encode_score(Familia.now, 255)
106
103
  decoded_boundary = @domain1.decode_score(boundary_score)
107
104
  decoded_boundary[:permissions] <= 255
108
105
  #=> true
@@ -112,22 +109,22 @@ decoded_boundary[:permissions] <= 255
112
109
  ## Method score calculation works with saved objects
113
110
  @customer1.save
114
111
  @domain1.save
115
- @domain1.add_to_edgetestcustomer_domains(@customer1)
116
- method_score = @domain1.score_in_edgetestcustomer_domains(@customer1)
112
+ @domain1.add_to_edge_test_customer_domains(@customer1)
113
+ method_score = @domain1.score_in_edge_test_customer_domains(@customer1)
117
114
  method_score.is_a?(Float) && method_score > 0
118
115
  #=> true
119
116
 
120
117
  ## Sorted set membership works
121
- @domain1.in_edgetestcustomer_domains?(@customer1)
118
+ @domain1.in_edge_test_customer_domains?(@customer1)
122
119
  #=> true
123
120
 
124
121
  ## Score methods respond correctly
125
- @domain1.respond_to?(:score_in_edgetestcustomer_domains)
122
+ @domain1.respond_to?(:score_in_edge_test_customer_domains)
126
123
  #=> true
127
124
 
128
125
  ## Basic relationship cleanup works
129
- @domain1.remove_from_edgetestcustomer_domains(@customer1)
130
- @domain1.in_edgetestcustomer_domains?(@customer1)
126
+ @domain1.remove_from_edge_test_customer_domains(@customer1)
127
+ @domain1.in_edge_test_customer_domains?(@customer1)
131
128
  #=> false
132
129
 
133
130
  # Clean up test data
@@ -44,7 +44,7 @@ save_time = Benchmark.realtime do
44
44
  @domains.each do |domain|
45
45
  domain.save
46
46
  # Use numeric scores for sorted sets
47
- MinimalDomain.all_domains.add(domain.priority_score.to_f, domain.identifier)
47
+ MinimalDomain.all_domains.add(domain.identifier, domain.priority_score.to_f)
48
48
  MinimalDomain.active_domains.add(domain.identifier)
49
49
  MinimalDomain.domain_history.push(domain.identifier)
50
50
  MinimalDomain.domain_lookup[domain.display_domain] = domain.identifier
@@ -105,7 +105,7 @@ end
105
105
  large_time = Benchmark.realtime do
106
106
  @large_domains.each do |domain|
107
107
  domain.save
108
- MinimalDomain.all_domains.add(domain.priority_score.to_f, domain.identifier)
108
+ MinimalDomain.all_domains.add(domain.identifier, domain.priority_score.to_f)
109
109
  end
110
110
  end
111
111
 
@@ -30,7 +30,7 @@ class SimplePerfDomain < Familia::Horreum
30
30
  class_hashkey :domain_lookup
31
31
 
32
32
  # Define tracking relationships for testing
33
- tracked_in SimplePerfCustomer, :simple_domains, score: :created_at
33
+ participates_in SimplePerfCustomer, :simple_domains, score: :created_at
34
34
  end
35
35
 
36
36
  # =============================================
@@ -46,7 +46,7 @@ end
46
46
  SimplePerfDomain.new(
47
47
  domain_id: "simple_perf_domain_#{i}",
48
48
  display_domain: "simple#{i}.example.com",
49
- created_at: Time.now.to_i + i,
49
+ created_at: Familia.now.to_i + i,
50
50
  priority_score: rand(100)
51
51
  )
52
52
  end
@@ -58,15 +58,15 @@ save_time = Benchmark.realtime do
58
58
  # Manually add to collections for testing (ensure numeric score)
59
59
  # Convert created_at to float, handling nil case
60
60
  score = if domain.created_at.nil?
61
- Time.now.to_f
61
+ Familia.now
62
62
  elsif domain.created_at.is_a?(Time)
63
63
  domain.created_at.to_f
64
64
  else
65
- domain.created_at.to_f
66
- end
67
- SimplePerfDomain.all_domains.add(score, domain.identifier)
68
- SimplePerfDomain.domain_lookup[domain.display_domain] = domain.identifier
69
- end
65
+ domain.created_at.to_f
66
+ end
67
+ SimplePerfDomain.all_domains.add(domain.identifier, score)
68
+ SimplePerfDomain.domain_lookup[domain.display_domain] = domain.identifier
69
+ end
70
70
  end
71
71
 
72
72
  # Should complete in reasonable time
@@ -39,7 +39,7 @@ class PerfDomain < Familia::Horreum
39
39
  class_hashkey :id_lookup
40
40
 
41
41
  # Define tracking relationships for testing
42
- tracked_in PerfCustomer, :domains, score: :created_at
42
+ participates_in PerfCustomer, :domains, score: :created_at
43
43
  end
44
44
 
45
45
  # Integration test with other features
@@ -73,7 +73,7 @@ end
73
73
  # =============================================
74
74
 
75
75
  ## Create test objects for performance testing
76
- @customer = PerfCustomer.new(custid: 'perf_customer', name: 'Performance Test', created_at: Time.now.to_i)
76
+ @customer = PerfCustomer.new(custid: 'perf_customer', name: 'Performance Test', created_at: Familia.now.to_i)
77
77
  @customer.save
78
78
 
79
79
  # Create multiple domains for bulk operations
@@ -81,7 +81,7 @@ end
81
81
  PerfDomain.new(
82
82
  domain_id: "perf_domain_#{i}",
83
83
  display_domain: "perf#{i}.example.com",
84
- created_at: Time.now.to_i + i,
84
+ created_at: Familia.now.to_i + i,
85
85
  priority_score: rand(100),
86
86
  customer_id: @customer.custid
87
87
  )
@@ -93,8 +93,8 @@ save_time = Benchmark.realtime do
93
93
  domain.save
94
94
  # Manually populate collections for testing
95
95
  score = domain.created_at.is_a?(Time) ? domain.created_at.to_f : domain.created_at.to_f
96
- PerfDomain.all_domains.add(score, domain.identifier)
97
- PerfDomain.priority_queue.add(domain.priority_score, domain.identifier)
96
+ PerfDomain.all_domains.add(domain.identifier, score)
97
+ PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
98
98
  PerfDomain.active_domains.add(domain.identifier)
99
99
  PerfDomain.domain_history.push(domain.identifier)
100
100
  PerfDomain.domain_lookup[domain.display_domain] = domain.identifier
@@ -153,7 +153,7 @@ instances = 100.times.map { |i| PerfDomain.new(domain_id: "mem_test_#{i}") }
153
153
  after_instances = ObjectSpace.count_objects[:T_OBJECT]
154
154
 
155
155
  # Should not have created excessive objects (relationship metadata is shared)
156
- (after_instances - before_instances) < 200 # Allow for reasonable overhead
156
+ (after_instances - before_instances) < 250 # Allow for reasonable, 2.5x overhead
157
157
  #=> true
158
158
 
159
159
  ## Class-level relationship metadata should be constant size
@@ -203,19 +203,19 @@ operation_threads = 3.times.map do |i|
203
203
  domain = PerfDomain.new(
204
204
  domain_id: "thread_test_#{i}",
205
205
  display_domain: "thread#{i}.test.com",
206
- created_at: Time.now.to_i + i,
206
+ created_at: Familia.now.to_i + i,
207
207
  priority_score: i * 10
208
208
  )
209
209
  domain.save
210
210
 
211
211
  # Add to collections manually
212
- PerfDomain.all_domains.add(domain.created_at.to_f, domain.identifier)
213
- PerfDomain.priority_queue.add(domain.priority_score, domain.identifier)
212
+ PerfDomain.all_domains.add(domain.identifier, domain.created_at.to_f)
213
+ PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
214
214
  domain.save
215
215
 
216
216
  # Manually add to collections for thread test
217
- PerfDomain.all_domains.add(domain.created_at.to_f, domain.identifier)
218
- PerfDomain.priority_queue.add(domain.priority_score, domain.identifier)
217
+ PerfDomain.all_domains.add(domain.identifier, domain.created_at.to_f)
218
+ PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
219
219
 
220
220
  # Verify the domain was added to collections
221
221
  in_all = PerfDomain.all_domains.member?(domain.identifier)
@@ -244,7 +244,7 @@ thread_results.all? { |result| result == [true, true] }
244
244
  # =============================================
245
245
 
246
246
  ## Integration with safe_dump (if available)
247
- integration_model = IntegrationTestModel.new(id: 'integration_test', data: 'test_data', created_at: Time.now.to_i)
247
+ integration_model = IntegrationTestModel.new(id: 'integration_test', data: 'test_data', created_at: Familia.now.to_i)
248
248
 
249
249
  if integration_model.respond_to?(:safe_dump)
250
250
  integration_model.save
@@ -260,7 +260,7 @@ end
260
260
  #=:> String
261
261
 
262
262
  ## Integration with expiration (if available)
263
- integration_model2 = IntegrationTestModel.new(id: 'integration_test2', data: 'test_data2', created_at: Time.now.to_i)
263
+ integration_model2 = IntegrationTestModel.new(id: 'integration_test2', data: 'test_data2', created_at: Familia.now.to_i)
264
264
  if integration_model2.respond_to?(:default_expiration)
265
265
  # Test that expiration works with relationship collections
266
266
  if integration_model2.class.respond_to?(:all_items)
@@ -315,15 +315,15 @@ stress_time < 0.1
315
315
  test_domain = PerfDomain.new(
316
316
  domain_id: 'atomic_test',
317
317
  display_domain: 'atomic.test.com',
318
- created_at: Time.now.to_i,
318
+ created_at: Familia.now.to_i,
319
319
  priority_score: 50
320
320
  )
321
321
 
322
322
  # Save should succeed and add to all collections
323
323
  test_domain.save
324
324
  # Manually add to collections
325
- PerfDomain.all_domains.add(test_domain.created_at.to_f, test_domain.identifier)
326
- PerfDomain.priority_queue.add(test_domain.priority_score, test_domain.identifier)
325
+ PerfDomain.all_domains.add(test_domain.identifier, test_domain.created_at.to_f)
326
+ PerfDomain.priority_queue.add(test_domain.identifier, test_domain.priority_score)
327
327
  PerfDomain.active_domains.add(test_domain.identifier)
328
328
  PerfDomain.domain_history.push(test_domain.identifier)
329
329
 
@@ -337,14 +337,14 @@ all_collections_have_domain
337
337
  #=> true
338
338
 
339
339
  ## Partial failures should not leave inconsistent state
340
- # This would require more complex testing infrastructure to simulate Redis failures
340
+ # This would require more complex testing infrastructure to simulate Valkey/Redis failures
341
341
 
342
- ## Recovery from Redis connection issues
342
+ ## Recovery from Valkey/Redis connection issues
343
343
  begin
344
- # Test graceful handling of Redis errors during relationship operations
344
+ # Test graceful handling of Valkey/Redis errors during relationship operations
345
345
  dead_domain = PerfDomain.new(domain_id: 'dead_test')
346
346
 
347
- # This test would require mocking Redis to fail, which is complex in this context
347
+ # This test would require mocking Valkey/Redis to fail, which is complex in this context
348
348
  # For now, just verify that normal operations work
349
349
  dead_domain.save
350
350
  dead_domain.destroy!
@@ -25,14 +25,11 @@ class TestDomain < Familia::Horreum
25
25
  field :created_at
26
26
  field :permission_level
27
27
 
28
- # Basic tracking with simplified score
29
- tracked_in TestCustomer, :domains, score: :created_at
30
- class_tracked_in :all_domains, score: :created_at
28
+ # Basic participation with simplified score
29
+ participates_in TestCustomer, :domains, score: :created_at
30
+ class_participates_in :all_domains, score: :created_at
31
31
 
32
32
  # Note: Indexing features removed for stability
33
-
34
- # Basic membership
35
- member_of TestCustomer, :domains
36
33
  end
37
34
 
38
35
  class TestTag < Familia::Horreum
@@ -42,8 +39,8 @@ class TestTag < Familia::Horreum
42
39
  field :name
43
40
  field :created_at
44
41
 
45
- # Global tracking
46
- class_tracked_in :all_tags, score: :created_at
42
+ # Global participation
43
+ class_participates_in :all_tags, score: :created_at
47
44
  end
48
45
 
49
46
  # Setup
@@ -51,10 +48,10 @@ end
51
48
  @domain = TestDomain.new(
52
49
  domain_id: 'dom_789',
53
50
  display_domain: 'example.com',
54
- created_at: Time.now.to_i,
51
+ created_at: Familia.now.to_i,
55
52
  permission_level: :write
56
53
  )
57
- @tag = TestTag.new(name: 'important', created_at: Time.now.to_i)
54
+ @tag = TestTag.new(name: 'important', created_at: Familia.now.to_i)
58
55
 
59
56
  # =============================================
60
57
  # 1. V2 Feature Integration Tests
@@ -72,10 +69,6 @@ TestDomain.included_modules.map(&:name).include?('Familia::Features::Relationshi
72
69
  @domain.respond_to?(:permission_encode)
73
70
  #=> true
74
71
 
75
- ## Redis operations functionality is available
76
- @domain.respond_to?(:atomic_operation)
77
- #=> true
78
-
79
72
  ## Identifier method works (wraps identifier_field)
80
73
  TestDomain.identifier_field
81
74
  #=> :domain_id
@@ -89,7 +82,7 @@ TestDomain.identifier_field
89
82
  # =============================================
90
83
 
91
84
  ## Permission encoding creates proper score
92
- @score = @domain.permission_encode(Time.now, :write)
85
+ @score = @domain.permission_encode(Familia.now, :write)
93
86
  @score.to_s.match?(/\d+\.\d+/)
94
87
  #=> true
95
88
 
@@ -99,16 +92,16 @@ decoded[:permission_list].include?(:write)
99
92
  #=> true
100
93
 
101
94
  ## Score encoding preserves timestamp ordering
102
- @early_score = @domain.encode_score(Time.now - 3600, 100) # 1 hour ago
103
- @late_score = @domain.encode_score(Time.now, 100)
95
+ @early_score = @domain.encode_score(Familia.now - 3600, 100) # 1 hour ago
96
+ @late_score = @domain.encode_score(Familia.now, 100)
104
97
  @late_score > @early_score
105
98
  #=> true
106
99
 
107
100
  # =============================================
108
- # 3. Tracking Relationships (tracked_in)
101
+ # 3. Participation Relationships (participates_in)
109
102
  # =============================================
110
103
 
111
- ## Save operation manages tracking relationships
104
+ ## Save operation manages participation relationships
112
105
  @customer.save
113
106
  @domain.save
114
107
 
@@ -129,24 +122,24 @@ decoded[:permission_list].include?(:write)
129
122
  #=> true
130
123
 
131
124
  ## Domain can check membership in customer domains (collision-free naming)
132
- @domain.respond_to?(:in_testcustomer_domains?)
125
+ @domain.respond_to?(:in_test_customer_domains?)
133
126
  #=> true
134
127
 
135
128
  ## Domain can add itself to customer domains (collision-free naming)
136
- @domain.respond_to?(:add_to_testcustomer_domains)
129
+ @domain.respond_to?(:add_to_test_customer_domains)
137
130
  #=> true
138
131
 
139
132
  ## Domain can remove itself from customer domains (collision-free naming)
140
- @domain.respond_to?(:remove_from_testcustomer_domains)
133
+ @domain.respond_to?(:remove_from_test_customer_domains)
141
134
  #=> true
142
135
 
143
136
  ## Add domain to customer collection
144
- @domain.add_to_testcustomer_domains(@customer)
145
- @domain.in_testcustomer_domains?(@customer)
137
+ @domain.add_to_test_customer_domains(@customer)
138
+ @domain.in_test_customer_domains?(@customer)
146
139
  #=> true
147
140
 
148
141
  ## Score is properly encoded
149
- score = @domain.score_in_testcustomer_domains(@customer)
142
+ score = @domain.score_in_test_customer_domains(@customer)
150
143
  score.is_a?(Float) && score > 0
151
144
  #=> true
152
145
 
@@ -154,8 +147,8 @@ score.is_a?(Float) && score > 0
154
147
  # 4. Basic Functionality Verification
155
148
  # =============================================
156
149
 
157
- ## Domain tracking methods work correctly
158
- @domain.respond_to?(:score_in_testcustomer_domains)
150
+ ## Domain participation methods work correctly
151
+ @domain.respond_to?(:score_in_test_customer_domains)
159
152
  #=> true
160
153
 
161
154
  ## Score calculation methods are available
@@ -163,23 +156,23 @@ score.is_a?(Float) && score > 0
163
156
  #=> true
164
157
 
165
158
  # =============================================
166
- # 5. Basic Membership Relationships (member_of)
159
+ # 5. Bidirectional Participation Methods
167
160
  # =============================================
168
161
 
169
- ## Member_of generates collision-free methods with collection names
170
- @domain.respond_to?(:add_to_testcustomer_domains)
162
+ ## participates_in generates collision-free bidirectional methods with collection names
163
+ @domain.respond_to?(:add_to_test_customer_domains)
171
164
  #=> true
172
165
 
173
- ## Basic membership operations work
174
- @domain.remove_from_testcustomer_domains(@customer)
175
- @domain.in_testcustomer_domains?(@customer)
166
+ ## Basic bidirectional participation operations work
167
+ @domain.remove_from_test_customer_domains(@customer)
168
+ @domain.in_test_customer_domains?(@customer)
176
169
  #=> false
177
170
 
178
171
  # =============================================
179
172
  # 6. Basic Global Tag Tracking Test
180
173
  # =============================================
181
174
 
182
- ## Tag can be tracked globally
175
+ ## Tag can be participating globally
183
176
  @tag.save
184
177
  @tag.respond_to?(:add_to_class_all_tags)
185
178
  #=> true
@@ -213,10 +206,6 @@ temp_key = @domain.create_temp_key("test_operation", 60)
213
206
  temp_key.start_with?("temp:")
214
207
  #=> true
215
208
 
216
- ## Batch operations are available
217
- @domain.respond_to?(:batch_zadd)
218
- #=> true
219
-
220
209
  ## Score range queries work
221
210
  @domain.respond_to?(:score_range)
222
211
  #=> true
@@ -17,8 +17,8 @@ Customer.safe_dump_fields
17
17
  @cust.custid = 'test@example.com'
18
18
  @cust.role = 'user'
19
19
  @cust.verified = true
20
- @cust.created = Time.now.to_i
21
- @cust.updated = Time.now.to_i
20
+ @cust.created = Familia.now.to_i
21
+ @cust.updated = Familia.now.to_i
22
22
  @safe_dump = @cust.safe_dump
23
23
  @safe_dump.keys.sort
24
24
  #=> [:active, :created, :custid, :role, :secrets_created, :updated, :verified]
@@ -94,7 +94,7 @@ SecretService.fields.sort
94
94
  @service.endpoint_url
95
95
  #=> "https://api.example.com"
96
96
 
97
- ## Set transient fields again after refresh
97
+ ## UnsortedSet transient fields again after refresh
98
98
  @service.api_key = 'new-api-key-after-refresh'
99
99
  @service.password = 'new-password-after-refresh'
100
100
  @service.token = 'new-token-after-refresh'
@@ -14,7 +14,7 @@ class Customer
14
14
  transient_field :temp_data
15
15
  end
16
16
 
17
- # Set some values
17
+ # UnsortedSet some values
18
18
  service.name = 'Test Customer'
19
19
  service.temp_data = 'secret-info'
20
20
 
@@ -0,0 +1,86 @@
1
+ # Test cleanup helper for managing anonymous classes and test isolation
2
+ #
3
+ # This module provides utilities to create and cleanup test classes that
4
+ # inherit from Familia classes. Without proper cleanup, anonymous classes
5
+ # pollute the Familia.members registry causing test failures.
6
+
7
+ module TestCleanup
8
+ @test_classes = []
9
+
10
+ class << self
11
+ attr_reader :test_classes
12
+
13
+ # Create a test class that inherits from the given base class.
14
+ # The created class is automatically tracked for cleanup.
15
+ #
16
+ # @param base_class [Class] The class to inherit from (e.g., Familia::Horreum)
17
+ # @param block [Proc] Block to define the class body
18
+ # @return [Class] The created test class
19
+ def create_test_class(base_class, &block)
20
+ test_class = Class.new(base_class, &block)
21
+ track_test_class(test_class)
22
+ test_class
23
+ end
24
+
25
+ # Track a test class for cleanup. Use this when you create test classes
26
+ # directly with Class.new instead of using create_test_class.
27
+ #
28
+ # @param klass [Class] The test class to track
29
+ # @return [Class] The tracked class
30
+ def track_test_class(klass)
31
+ @test_classes << klass unless @test_classes.include?(klass)
32
+ klass
33
+ end
34
+
35
+ # Remove all tracked test classes from Familia.members and clear
36
+ # the tracking array. This should be called in test teardown.
37
+ #
38
+ # @return [Array<Class>] The classes that were removed
39
+ def cleanup_test_classes
40
+ removed_classes = []
41
+
42
+ @test_classes.each do |test_class|
43
+ if Familia.members.include?(test_class)
44
+ Familia.unload_member(test_class)
45
+ removed_classes << test_class
46
+ end
47
+ end
48
+
49
+ @test_classes.clear
50
+ removed_classes
51
+ end
52
+
53
+ # Clean up all anonymous classes from Familia.members.
54
+ # This is a more aggressive cleanup that removes any class with nil name.
55
+ #
56
+ # @return [Array<Class>] The anonymous classes that were removed
57
+ def cleanup_anonymous_classes
58
+ Familia.clear_anonymous_members
59
+ end
60
+
61
+ # Perform complete test cleanup - both tracked and anonymous classes
62
+ #
63
+ # @return [Hash] Summary of cleanup performed
64
+ def complete_cleanup
65
+ tracked_removed = cleanup_test_classes
66
+ anonymous_removed = cleanup_anonymous_classes
67
+
68
+ {
69
+ tracked_classes_removed: tracked_removed.size,
70
+ anonymous_classes_removed: anonymous_removed.size,
71
+ total_removed: tracked_removed.size + anonymous_removed.size
72
+ }
73
+ end
74
+ end
75
+ end
76
+
77
+ # Automatically perform cleanup if we're in test mode
78
+ # This ensures cleanup happens even if tests don't explicitly call it
79
+ at_exit do
80
+ if Familia.test_mode? && TestCleanup.test_classes.any?
81
+ cleanup_result = TestCleanup.complete_cleanup
82
+ if cleanup_result[:total_removed] > 0
83
+ puts "[TestCleanup] Cleaned up #{cleanup_result[:total_removed]} test classes on exit"
84
+ end
85
+ end
86
+ end
@@ -65,7 +65,7 @@ class Customer < Familia::Horreum
65
65
  # We use a callable here since `:active?` is not a valid method symbol.
66
66
  safe_dump_field :active, ->(cust) { cust.active? }
67
67
 
68
- class_sorted_set :values, key: 'onetime:customer'
68
+ class_sorted_set :values, key: 'familia:customer'
69
69
  class_hashkey :domains
70
70
 
71
71
  hashkey :stripe_customer
@@ -91,9 +91,9 @@ class Customer < Familia::Horreum
91
91
  field :reset_requested #=> Boolean
92
92
 
93
93
  hashkey :password_reset #=> Familia::HashKey
94
- list :sessions #=> Familia::List
94
+ list :sessions #=> Familia::ListKey
95
95
 
96
- class_list :customers, suffix: []
96
+ class_list :all_customers, suffix: [] # no suffix
97
97
  class_string :message
98
98
 
99
99
  class_zset :instances, class: self, reference: true
@@ -52,7 +52,7 @@ Familia.debug = false
52
52
  #=> true
53
53
 
54
54
  ## Horreum object fields have a fast attribute method (1 of 2)
55
- Familia.trace :LOAD, @customer.dbclient, @customer.uri, caller if Familia.debug?
55
+ Familia.trace :LOAD, nil, @customer.uri if Familia.debug?
56
56
  @customer.name! 'Jane Doe'
57
57
  #=> true
58
58
 
@@ -69,7 +69,8 @@ Familia.trace :LOAD, @customer.dbclient, @customer.uri, caller if Familia.debug?
69
69
 
70
70
  ## Horreum objects can be destroyed
71
71
  @customer.destroy!
72
- #=> true
72
+ #=:> MultiResult
73
+ #==> result.successful?
73
74
 
74
75
  ## All horrerum objects have a key field
75
76
  @customer.identifier
@@ -1,4 +1,4 @@
1
- # Test Horreum Redis commands
1
+ # Test Horreum Valkey/Redis commands
2
2
 
3
3
  require_relative '../helpers/test_helpers'
4
4