familia 2.0.0.pre15 → 2.0.0.pre17

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 (288) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/.github/workflows/code-quality.yml +138 -0
  4. data/.github/workflows/code-smells.yml +85 -0
  5. data/.github/workflows/docs.yml +31 -8
  6. data/.gitignore +3 -1
  7. data/.pre-commit-config.yaml +7 -1
  8. data/.reek.yml +98 -0
  9. data/.rubocop.yml +54 -10
  10. data/.talismanrc +9 -0
  11. data/.yardopts +18 -13
  12. data/CHANGELOG.rst +86 -4
  13. data/CLAUDE.md +39 -1
  14. data/Gemfile +6 -5
  15. data/Gemfile.lock +99 -23
  16. data/LICENSE.txt +1 -1
  17. data/README.md +285 -85
  18. data/changelog.d/README.md +2 -2
  19. data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
  20. data/docs/archive/FAMILIA_TECHNICAL.md +42 -42
  21. data/docs/archive/FAMILIA_UPDATE.md +3 -3
  22. data/docs/archive/README.md +3 -2
  23. data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
  24. data/docs/conf.py +29 -0
  25. data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
  26. data/docs/guides/feature-encrypted-fields.md +785 -0
  27. data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
  28. data/docs/guides/feature-external-identifiers.md +637 -0
  29. data/docs/guides/feature-object-identifiers.md +435 -0
  30. data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
  31. data/docs/guides/feature-relationships-methods.md +684 -0
  32. data/docs/guides/feature-relationships.md +200 -0
  33. data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
  34. data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
  35. data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
  36. data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
  37. data/docs/guides/index.md +176 -0
  38. data/docs/guides/{Security-Model.md → security-model.md} +1 -1
  39. data/docs/migrating/v2.0.0-pre.md +1 -1
  40. data/docs/migrating/v2.0.0-pre11.md +2 -2
  41. data/docs/migrating/v2.0.0-pre12.md +2 -2
  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 +624 -20
  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 +7 -7
  54. data/examples/single_connection_transaction_confusions.rb +379 -0
  55. data/lib/familia/base.rb +51 -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/class_methods.rb +63 -0
  65. data/lib/familia/data_type/commands.rb +53 -51
  66. data/lib/familia/data_type/connection.rb +83 -0
  67. data/lib/familia/data_type/serialization.rb +108 -107
  68. data/lib/familia/data_type/settings.rb +96 -0
  69. data/lib/familia/data_type/types/counter.rb +1 -1
  70. data/lib/familia/data_type/types/hashkey.rb +15 -11
  71. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  72. data/lib/familia/data_type/types/lock.rb +3 -2
  73. data/lib/familia/data_type/types/sorted_set.rb +128 -14
  74. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -9
  75. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  76. data/lib/familia/data_type.rb +12 -171
  77. data/lib/familia/distinguisher.rb +85 -0
  78. data/lib/familia/encryption/encrypted_data.rb +15 -24
  79. data/lib/familia/encryption/manager.rb +6 -4
  80. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  81. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  82. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  83. data/lib/familia/encryption/request_cache.rb +7 -7
  84. data/lib/familia/encryption.rb +2 -3
  85. data/lib/familia/errors.rb +9 -3
  86. data/lib/familia/features/autoloader.rb +30 -12
  87. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  88. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  89. data/lib/familia/features/encrypted_fields.rb +71 -66
  90. data/lib/familia/features/expiration/extensions.rb +1 -1
  91. data/lib/familia/features/expiration.rb +31 -26
  92. data/lib/familia/features/external_identifier.rb +57 -19
  93. data/lib/familia/features/object_identifier.rb +134 -25
  94. data/lib/familia/features/quantization.rb +16 -21
  95. data/lib/familia/features/relationships/README.md +97 -0
  96. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  97. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  98. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +306 -0
  99. data/lib/familia/features/relationships/indexing.rb +182 -256
  100. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  101. data/lib/familia/features/relationships/participation/participant_methods.rb +164 -0
  102. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  103. data/lib/familia/features/relationships/participation.rb +656 -0
  104. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  105. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  106. data/lib/familia/features/relationships.rb +65 -266
  107. data/lib/familia/features/safe_dump.rb +127 -130
  108. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  109. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  110. data/lib/familia/features/transient_fields.rb +10 -7
  111. data/lib/familia/features.rb +10 -14
  112. data/lib/familia/field_type.rb +6 -4
  113. data/lib/familia/horreum/connection.rb +297 -0
  114. data/lib/familia/horreum/{core/database_commands.rb → database_commands.rb} +27 -17
  115. data/lib/familia/horreum/{subclass/definition.rb → definition.rb} +139 -74
  116. data/lib/familia/horreum/{subclass/management.rb → management.rb} +73 -27
  117. data/lib/familia/horreum/{core/serialization.rb → persistence.rb} +108 -185
  118. data/lib/familia/horreum/{subclass/related_fields_management.rb → related_fields.rb} +104 -23
  119. data/lib/familia/horreum/serialization.rb +172 -0
  120. data/lib/familia/horreum/{shared/settings.rb → settings.rb} +2 -1
  121. data/lib/familia/horreum/{core/utils.rb → utils.rb} +2 -1
  122. data/lib/familia/horreum.rb +222 -119
  123. data/lib/familia/json_serializer.rb +0 -1
  124. data/lib/familia/logging.rb +11 -114
  125. data/lib/familia/refinements/dear_json.rb +122 -0
  126. data/lib/familia/refinements/logger_trace.rb +20 -17
  127. data/lib/familia/refinements/stylize_words.rb +65 -0
  128. data/lib/familia/refinements/time_literals.rb +60 -52
  129. data/lib/familia/refinements.rb +2 -1
  130. data/lib/familia/secure_identifier.rb +60 -28
  131. data/lib/familia/settings.rb +83 -7
  132. data/lib/familia/utils.rb +5 -87
  133. data/lib/familia/verifiable_identifier.rb +4 -4
  134. data/lib/familia/version.rb +1 -1
  135. data/lib/familia.rb +72 -14
  136. data/lib/middleware/database_middleware.rb +56 -14
  137. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  138. data/try/configuration/scenarios_try.rb +2 -2
  139. data/try/connection/fiber_context_preservation_try.rb +250 -0
  140. data/try/connection/handler_constraints_try.rb +59 -0
  141. data/try/connection/operation_mode_guards_try.rb +208 -0
  142. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  143. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  144. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  145. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  146. data/try/connection/transaction_mode_strict_try.rb +98 -0
  147. data/try/connection/transaction_mode_warn_try.rb +131 -0
  148. data/try/connection/transaction_modes_try.rb +249 -0
  149. data/try/core/autoloader_try.rb +120 -2
  150. data/try/core/connection_try.rb +10 -10
  151. data/try/core/conventional_inheritance_try.rb +130 -0
  152. data/try/core/create_method_try.rb +15 -23
  153. data/try/core/database_consistency_try.rb +11 -10
  154. data/try/core/errors_try.rb +11 -14
  155. data/try/core/familia_extended_try.rb +2 -2
  156. data/try/core/familia_members_methods_try.rb +76 -0
  157. data/try/core/familia_try.rb +1 -1
  158. data/try/core/isolated_dbclient_try.rb +165 -0
  159. data/try/core/middleware_try.rb +16 -16
  160. data/try/core/persistence_operations_try.rb +4 -4
  161. data/try/core/pools_try.rb +42 -26
  162. data/try/core/secure_identifier_try.rb +28 -24
  163. data/try/core/time_utils_try.rb +10 -10
  164. data/try/core/tools_try.rb +3 -3
  165. data/try/core/utils_try.rb +2 -2
  166. data/try/data_types/boolean_try.rb +4 -4
  167. data/try/data_types/datatype_base_try.rb +0 -2
  168. data/try/data_types/list_try.rb +10 -10
  169. data/try/data_types/sorted_set_try.rb +5 -5
  170. data/try/data_types/sorted_set_zadd_options_try.rb +625 -0
  171. data/try/data_types/string_try.rb +12 -12
  172. data/try/data_types/unsortedset_try.rb +33 -0
  173. data/try/debugging/cache_behavior_tracer.rb +7 -7
  174. data/try/debugging/debug_aad_process.rb +1 -1
  175. data/try/debugging/debug_concealed_internal.rb +1 -1
  176. data/try/debugging/debug_cross_context.rb +1 -1
  177. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  178. data/try/debugging/encryption_method_tracer.rb +10 -10
  179. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  180. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  181. data/try/encryption/config_persistence_try.rb +2 -2
  182. data/try/encryption/encryption_core_try.rb +19 -19
  183. data/try/encryption/instance_variable_scope_try.rb +1 -1
  184. data/try/encryption/module_loading_try.rb +2 -2
  185. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  186. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  187. data/try/encryption/secure_memory_handling_try.rb +1 -1
  188. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  189. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  190. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  191. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  192. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  193. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  194. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  195. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  196. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  197. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  198. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  199. data/try/features/feature_dependencies_try.rb +3 -3
  200. data/try/features/field_groups_try.rb +244 -0
  201. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  202. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  203. data/try/features/quantization/quantization_try.rb +1 -1
  204. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  205. data/try/features/relationships/indexing_try.rb +443 -0
  206. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  207. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  208. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  209. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  210. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  211. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  212. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  213. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  214. data/try/features/relationships/relationships_performance_try.rb +20 -20
  215. data/try/features/relationships/relationships_try.rb +27 -38
  216. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  217. data/try/features/transient_fields/refresh_reset_try.rb +3 -1
  218. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  219. data/try/helpers/test_cleanup.rb +86 -0
  220. data/try/helpers/test_helpers.rb +6 -7
  221. data/try/horreum/auto_indexing_on_save_try.rb +212 -0
  222. data/try/horreum/base_try.rb +3 -2
  223. data/try/horreum/commands_try.rb +3 -1
  224. data/try/horreum/defensive_initialization_try.rb +86 -0
  225. data/try/horreum/destroy_related_fields_cleanup_try.rb +332 -0
  226. data/try/horreum/initialization_try.rb +11 -7
  227. data/try/horreum/relations_try.rb +21 -13
  228. data/try/horreum/serialization_try.rb +12 -11
  229. data/try/horreum/settings_try.rb +2 -0
  230. data/try/integration/cross_component_try.rb +3 -3
  231. data/try/memory/memory_basic_test.rb +1 -1
  232. data/try/memory/memory_docker_ruby_dump.sh +2 -2
  233. data/try/models/customer_safe_dump_try.rb +1 -1
  234. data/try/models/customer_try.rb +13 -15
  235. data/try/models/datatype_base_try.rb +3 -3
  236. data/try/models/familia_object_try.rb +9 -8
  237. data/try/performance/benchmarks_try.rb +2 -2
  238. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  239. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  240. data/try/prototypes/atomic_saves_v4.rb +1 -1
  241. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  242. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  243. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  244. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  245. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  246. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  247. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  248. data/try/prototypes/pooling/pool_siege.rb +11 -11
  249. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  250. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  251. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  252. data/try/refinements/logger_trace_methods_try.rb +44 -0
  253. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  254. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  255. data/try/valkey.conf +26 -0
  256. metadata +92 -52
  257. data/.rubocop_todo.yml +0 -208
  258. data/docs/connection_pooling.md +0 -192
  259. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  260. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  261. data/docs/guides/Feature-System-Autoloading.md +0 -198
  262. data/docs/guides/Home.md +0 -116
  263. data/docs/guides/Relationships-Guide.md +0 -737
  264. data/docs/guides/relationships-methods.md +0 -266
  265. data/docs/reference/auditing_database_commands.rb +0 -228
  266. data/examples/permissions.rb +0 -240
  267. data/lib/familia/features/relationships/cascading.rb +0 -437
  268. data/lib/familia/features/relationships/membership.rb +0 -497
  269. data/lib/familia/features/relationships/permission_management.rb +0 -264
  270. data/lib/familia/features/relationships/querying.rb +0 -615
  271. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  272. data/lib/familia/features/relationships/tracking.rb +0 -418
  273. data/lib/familia/horreum/core/connection.rb +0 -73
  274. data/lib/familia/horreum/core.rb +0 -21
  275. data/lib/familia/refinements/snake_case.rb +0 -40
  276. data/lib/familia/validation/command_recorder.rb +0 -336
  277. data/lib/familia/validation/expectations.rb +0 -519
  278. data/lib/familia/validation/validation_helpers.rb +0 -443
  279. data/lib/familia/validation/validator.rb +0 -412
  280. data/lib/familia/validation.rb +0 -140
  281. data/try/data_types/set_try.rb +0 -33
  282. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  283. data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
  284. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
  285. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  286. data/try/validation/command_validation_try.rb.disabled +0 -207
  287. data/try/validation/performance_validation_try.rb.disabled +0 -324
  288. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -1,13 +1,13 @@
1
1
  # try/features/relationships/relationships_api_changes_try.rb
2
2
  #
3
3
  # Test coverage for Familia v2 relationships API changes
4
- # Testing new class_tracked_in and class_indexed_by methods
4
+ # Testing new class_participates_in and unique_index methods
5
5
  # Testing breaking changes and argument validation
6
6
 
7
7
  require_relative '../../helpers/test_helpers'
8
8
 
9
9
  # Test classes for new API
10
- class ApiTestUser < Familia::Horreum
10
+ class ::ApiTestUser < Familia::Horreum
11
11
  feature :relationships
12
12
 
13
13
  identifier_field :user_id
@@ -17,16 +17,16 @@ class ApiTestUser < Familia::Horreum
17
17
  field :created_at
18
18
  field :status
19
19
 
20
- # New API: class_tracked_in for class-level tracking
21
- class_tracked_in :all_users, score: :created_at
22
- class_tracked_in :active_users, score: -> { status == 'active' ? Time.now.to_i : 0 }
20
+ # New API: class_participates_in for class-level participation
21
+ class_participates_in :all_users, score: :created_at
22
+ class_participates_in :active_users, score: -> { status == 'active' ? Familia.now.to_i : 0 }
23
23
 
24
- # New API: class_indexed_by for class-level indexing
25
- class_indexed_by :email, :email_lookup
26
- class_indexed_by :username, :username_lookup, finder: false
24
+ # New API: unique_index for class-level indexing
25
+ unique_index :email, :email_lookup
26
+ unique_index :username, :username_lookup, query: false
27
27
  end
28
28
 
29
- class ApiTestProject < Familia::Horreum
29
+ class ::ApiTestProject < Familia::Horreum
30
30
  feature :relationships
31
31
 
32
32
  identifier_field :project_id
@@ -35,7 +35,7 @@ class ApiTestProject < Familia::Horreum
35
35
  field :created_at
36
36
  end
37
37
 
38
- class ApiTestMembership < Familia::Horreum
38
+ class ::ApiTestMembership < Familia::Horreum
39
39
  feature :relationships
40
40
 
41
41
  identifier_field :membership_id
@@ -45,20 +45,21 @@ class ApiTestMembership < Familia::Horreum
45
45
  field :role
46
46
  field :created_at
47
47
 
48
- # New API: using parent: instead of context:
49
- indexed_by :user_id, :user_memberships, parent: ApiTestUser
50
- indexed_by :project_id, :project_memberships, parent: ApiTestProject
48
+ # New API: using target: parameter
49
+ multi_index :user_id, :user_memberships, within: ApiTestUser
50
+ multi_index :project_id, :project_memberships, within: ApiTestProject
51
51
 
52
- # Tracking with parent class
53
- tracked_in ApiTestProject, :memberships, score: :created_at
52
+ # Participation with parent class
53
+ participates_in ApiTestProject, :memberships, score: :created_at
54
54
  end
55
55
 
56
+
56
57
  # Setup test objects
57
58
  @user = ApiTestUser.new(
58
59
  user_id: 'user_123',
59
60
  email: 'test@example.com',
60
61
  username: 'testuser',
61
- created_at: Time.now.to_i,
62
+ created_at: Familia.now.to_i,
62
63
  status: 'active'
63
64
  )
64
65
 
@@ -66,14 +67,14 @@ end
66
67
  user_id: 'user_456',
67
68
  email: 'inactive@example.com',
68
69
  username: 'inactiveuser',
69
- created_at: Time.now.to_i - 3600,
70
+ created_at: Familia.now.to_i - 3600,
70
71
  status: 'inactive'
71
72
  )
72
73
 
73
74
  @project = ApiTestProject.new(
74
75
  project_id: 'proj_789',
75
76
  name: 'Test Project',
76
- created_at: Time.now.to_i
77
+ created_at: Familia.now.to_i
77
78
  )
78
79
 
79
80
  @membership = ApiTestMembership.new(
@@ -81,34 +82,34 @@ end
81
82
  user_id: @user.user_id,
82
83
  project_id: @project.project_id,
83
84
  role: 'admin',
84
- created_at: Time.now.to_i
85
+ created_at: Familia.now.to_i
85
86
  )
86
87
 
87
88
  # =============================================
88
- # 1. New API: class_tracked_in Method Tests
89
+ # 1. New API: class_participates_in Method Tests
89
90
  # =============================================
90
91
 
91
- ## class_tracked_in generates class-level collection class methods
92
+ ## class_participates_in generates class-level collection class methods
92
93
  ApiTestUser.respond_to?(:all_users)
93
94
  #=> true
94
95
 
95
- ## class_tracked_in generates class-level collection access methods
96
+ ## class_participates_in generates class-level collection access methods
96
97
  ApiTestUser.respond_to?(:active_users)
97
98
  #=> true
98
99
 
99
- ## class_tracked_in generates class methods for adding items
100
+ ## class_participates_in generates class methods for adding items
100
101
  ApiTestUser.respond_to?(:add_to_all_users)
101
102
  #=> true
102
103
 
103
- ## class_tracked_in generates class methods for removing items
104
+ ## class_participates_in generates class methods for removing items
104
105
  ApiTestUser.respond_to?(:remove_from_all_users)
105
106
  #=> true
106
107
 
107
- ## class_tracked_in generates membership check methods
108
+ ## class_participates_in generates membership check methods
108
109
  @user.respond_to?(:in_class_all_users?)
109
110
  #=> true
110
111
 
111
- ## class_tracked_in generates score retrieval methods
112
+ ## class_participates_in generates score retrieval methods
112
113
  @user.respond_to?(:score_in_class_all_users)
113
114
  #=> true
114
115
 
@@ -129,49 +130,49 @@ score.is_a?(Float) && score > 0
129
130
  #=> true
130
131
 
131
132
  ## Score calculation works for lambda scores with active user
132
- @user.save # Should automatically add to active_users
133
+ ApiTestUser.add_to_active_users(@user) # Explicit addition to active_users collection
133
134
  active_score = ApiTestUser.active_users.score(@user.identifier)
134
135
  active_score > 0
135
136
  #=> true
136
137
 
137
138
  ## Score calculation works for lambda scores with inactive user
138
- @inactive_user.save # Should automatically add to active_users
139
+ ApiTestUser.add_to_active_users(@inactive_user) # Explicit addition to active_users collection
139
140
  ApiTestUser.active_users.member?(@inactive_user.identifier)
140
141
  #=> true
141
142
 
142
143
  # =============================================
143
- # 2. New API: class_indexed_by Method Tests
144
+ # 2. New API: unique_index Method Tests
144
145
  # =============================================
145
146
 
146
- ## class_indexed_by with finder: true generates finder methods
147
+ ## unique_index with query: true generates query methods
147
148
  ApiTestUser.respond_to?(:find_by_email)
148
149
  #=> true
149
150
 
150
- ## class_indexed_by with finder: true generates bulk finder methods
151
+ ## unique_index with query: true generates bulk query methods
151
152
  ApiTestUser.respond_to?(:find_all_by_email)
152
153
  #=> true
153
154
 
154
- ## class_indexed_by with finder: false does not generate finder methods
155
+ ## unique_index with query: false does not generate query methods
155
156
  ApiTestUser.respond_to?(:find_by_username)
156
157
  #=> false
157
158
 
158
- ## class_indexed_by generates class-level index access methods
159
+ ## unique_index generates class-level index access methods
159
160
  ApiTestUser.respond_to?(:email_lookup)
160
161
  #=> true
161
162
 
162
- ## class_indexed_by generates class-level index rebuild methods
163
+ ## unique_index generates class-level index rebuild methods
163
164
  ApiTestUser.respond_to?(:rebuild_email_lookup)
164
165
  #=> true
165
166
 
166
- ## class_indexed_by generates instance methods for class indexing
167
+ ## unique_index generates instance methods for class indexing
167
168
  @user.respond_to?(:add_to_class_email_lookup)
168
169
  #=> true
169
170
 
170
- ## class_indexed_by generates removal methods
171
+ ## unique_index generates removal methods
171
172
  @user.respond_to?(:remove_from_class_email_lookup)
172
173
  #=> true
173
174
 
174
- ## class_indexed_by generates update methods
175
+ ## unique_index generates update methods
175
176
  @user.respond_to?(:update_in_class_email_lookup)
176
177
  #=> true
177
178
 
@@ -189,16 +190,16 @@ ApiTestUser.email_lookup.get(@user.email) == @user.user_id
189
190
  # 3. New API: parent: Parameter Tests
190
191
  # =============================================
191
192
 
192
- ## indexed_by with parent: generates context-specific methods
193
- @membership.respond_to?(:add_to_apitestuser_user_memberships)
193
+ ## multi_index with within: generates context-specific methods
194
+ @membership.respond_to?(:add_to_api_test_user_user_memberships)
194
195
  #=> true
195
196
 
196
- ## indexed_by with parent: generates removal methods
197
- @membership.respond_to?(:remove_from_apitestuser_user_memberships)
197
+ ## multi_index with within: generates removal methods
198
+ @membership.respond_to?(:remove_from_api_test_user_user_memberships)
198
199
  #=> true
199
200
 
200
- ## indexed_by with parent: generates update methods
201
- @membership.respond_to?(:update_in_apitestuser_user_memberships)
201
+ ## multi_index with within: generates update methods
202
+ @membership.respond_to?(:update_in_api_test_user_user_memberships)
202
203
  #=> true
203
204
 
204
205
  ## Parent class gets finder methods for indexed relationships
@@ -212,18 +213,18 @@ true
212
213
  # 4. Breaking Changes: ArgumentError Tests
213
214
  # =============================================
214
215
 
215
- ## class_tracked_in creates class-level collections without error
216
+ ## class_participates_in creates class-level collections without error
216
217
  test_class = Class.new(Familia::Horreum) do
217
218
  feature :relationships
218
- class_tracked_in :test_collection
219
+ class_participates_in :test_collection
219
220
  end
220
221
  test_class.respond_to?(:test_collection)
221
222
  #=> true
222
223
 
223
- ## class_indexed_by works like class-level (old feature)
224
+ ## unique_index works like class-level (old feature)
224
225
  test_class = Class.new(Familia::Horreum) do
225
226
  feature :relationships
226
- class_indexed_by :test_field, :test_index
227
+ unique_index :test_field, :test_index
227
228
  end
228
229
  test_class.respond_to?(:indexing_relationships)
229
230
  ##=> true
@@ -242,39 +243,39 @@ instance_methods = @user.methods.grep(/class_/)
242
243
  instance_methods.all? { |m| m.to_s.include?('class_') }
243
244
  #=> true
244
245
 
245
- ## Parent-based methods use lowercased class names
246
- parent_methods = @membership.methods.grep(/apitestuser/)
247
- parent_methods.length > 0
246
+ ## Context-based methods use snake_case class names
247
+ context_methods = @membership.methods.grep(/api_test_user/)
248
+ context_methods.length > 0
248
249
  #=> true
249
250
 
250
251
  # =============================================
251
252
  # 6. Metadata Storage Tests
252
253
  # =============================================
253
254
 
254
- ## class_tracked_in stores correct context_class
255
- tracking_meta = ApiTestUser.tracking_relationships.find { |r| r[:collection_name] == :all_users }
256
- tracking_meta[:context_class].end_with?('::apitestuser')
257
- #=> true
255
+ ## class_participates_in stores correct target_class (now a Class object)
256
+ participation_meta = ApiTestUser.participation_relationships.find { |r| r.collection_name == :all_users }
257
+ participation_meta.target_class
258
+ #=> ApiTestUser
258
259
 
259
- ## class_tracked_in stores correct context_class_name
260
- tracking_meta = ApiTestUser.tracking_relationships.find { |r| r[:collection_name] == :all_users }
261
- tracking_meta[:context_class_name].end_with?('::ApiTestUser')
262
- #=> true
260
+ ## class_participates_in stores correct target_class via familia_name
261
+ participation_meta = ApiTestUser.participation_relationships.find { |r| r.collection_name == :all_users }
262
+ participation_meta.target_class.familia_name
263
+ #=> 'ApiTestUser'
263
264
 
264
- ## class_indexed_by stores correct context_class
265
- indexing_meta = ApiTestUser.indexing_relationships.find { |r| r[:index_name] == :email_lookup }
266
- indexing_meta[:context_class] == ApiTestUser
267
- #=> true
265
+ ## unique_index stores correct target_class
266
+ indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
267
+ indexing_meta.target_class
268
+ #=> ApiTestUser
268
269
 
269
- ## class_indexed_by stores correct context_class_name
270
- indexing_meta = ApiTestUser.indexing_relationships.find { |r| r[:index_name] == :email_lookup }
271
- indexing_meta[:context_class_name].end_with?('ApiTestUser')
272
- #=> true
270
+ ## unique_index stores correct target_class via familia_name
271
+ indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
272
+ indexing_meta.target_class.familia_name
273
+ #=> 'ApiTestUser'
273
274
 
274
- ## indexed_by with parent: stores correct metadata
275
- membership_meta = ApiTestMembership.indexing_relationships.find { |r| r[:index_name] == :user_memberships }
276
- membership_meta[:context_class] == ApiTestUser
277
- #=> true
275
+ ## multi_index with within: stores correct metadata
276
+ membership_meta = ApiTestMembership.indexing_relationships.find { |r| r.index_name == :user_memberships }
277
+ membership_meta.target_class
278
+ #=> ApiTestUser
278
279
 
279
280
  # =============================================
280
281
  # 7. Functional Integration Tests
@@ -288,7 +289,7 @@ ApiTestUser.all_users.member?(@user.identifier) && ApiTestUser.email_lookup.get(
288
289
  ## Parent-based relationships work with tracking
289
290
  @project.save
290
291
  # Note: Skipping complex parent relationship test
291
- @membership.respond_to?(:add_to_apitestproject_memberships)
292
+ @membership.respond_to?(:add_to_api_test_project_memberships)
292
293
  #=> true
293
294
 
294
295
  ## Score-based tracking maintains proper ordering
@@ -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!