familia 2.0.0.pre15 → 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 (274) 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 +64 -4
  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 +2 -2
  40. data/docs/migrating/v2.0.0-pre12.md +2 -2
  41. data/docs/migrating/v2.0.0-pre5.md +33 -12
  42. data/docs/migrating/v2.0.0-pre6.md +2 -2
  43. data/docs/migrating/v2.0.0-pre7.md +8 -8
  44. data/docs/overview.md +623 -19
  45. data/docs/reference/api-technical.md +1365 -0
  46. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  47. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  48. data/examples/autoloader/mega_customer.rb +3 -1
  49. data/examples/encrypted_fields.rb +378 -0
  50. data/examples/json_usage_patterns.rb +144 -0
  51. data/examples/relationships.rb +13 -13
  52. data/examples/safe_dump.rb +6 -6
  53. data/examples/single_connection_transaction_confusions.rb +379 -0
  54. data/lib/familia/base.rb +49 -10
  55. data/lib/familia/connection/handlers.rb +223 -0
  56. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  57. data/lib/familia/connection/middleware.rb +75 -0
  58. data/lib/familia/connection/operation_core.rb +93 -0
  59. data/lib/familia/connection/operations.rb +277 -0
  60. data/lib/familia/connection/pipeline_core.rb +87 -0
  61. data/lib/familia/connection/transaction_core.rb +100 -0
  62. data/lib/familia/connection.rb +60 -186
  63. data/lib/familia/data_type/commands.rb +53 -51
  64. data/lib/familia/data_type/serialization.rb +108 -107
  65. data/lib/familia/data_type/types/counter.rb +1 -1
  66. data/lib/familia/data_type/types/hashkey.rb +13 -10
  67. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  68. data/lib/familia/data_type/types/lock.rb +3 -2
  69. data/lib/familia/data_type/types/sorted_set.rb +26 -15
  70. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
  71. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  72. data/lib/familia/data_type.rb +75 -47
  73. data/lib/familia/distinguisher.rb +85 -0
  74. data/lib/familia/encryption/encrypted_data.rb +15 -24
  75. data/lib/familia/encryption/manager.rb +6 -4
  76. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  77. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  78. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  79. data/lib/familia/encryption/request_cache.rb +7 -7
  80. data/lib/familia/encryption.rb +2 -3
  81. data/lib/familia/errors.rb +9 -3
  82. data/lib/familia/features/autoloader.rb +30 -12
  83. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  84. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  85. data/lib/familia/features/encrypted_fields.rb +66 -64
  86. data/lib/familia/features/expiration/extensions.rb +1 -1
  87. data/lib/familia/features/expiration.rb +31 -26
  88. data/lib/familia/features/external_identifier.rb +9 -12
  89. data/lib/familia/features/object_identifier.rb +56 -19
  90. data/lib/familia/features/quantization.rb +16 -21
  91. data/lib/familia/features/relationships/README.md +97 -0
  92. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  93. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  94. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
  95. data/lib/familia/features/relationships/indexing.rb +176 -256
  96. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  97. data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
  98. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  99. data/lib/familia/features/relationships/participation.rb +656 -0
  100. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  101. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  102. data/lib/familia/features/relationships.rb +65 -266
  103. data/lib/familia/features/safe_dump.rb +127 -130
  104. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  105. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  106. data/lib/familia/features/transient_fields.rb +3 -5
  107. data/lib/familia/features.rb +4 -13
  108. data/lib/familia/field_type.rb +24 -4
  109. data/lib/familia/horreum/core/connection.rb +229 -26
  110. data/lib/familia/horreum/core/database_commands.rb +27 -17
  111. data/lib/familia/horreum/core/serialization.rb +40 -20
  112. data/lib/familia/horreum/core/utils.rb +2 -1
  113. data/lib/familia/horreum/shared/settings.rb +2 -1
  114. data/lib/familia/horreum/subclass/definition.rb +33 -45
  115. data/lib/familia/horreum/subclass/management.rb +72 -24
  116. data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
  117. data/lib/familia/horreum.rb +196 -114
  118. data/lib/familia/json_serializer.rb +0 -1
  119. data/lib/familia/logging.rb +11 -114
  120. data/lib/familia/refinements/dear_json.rb +122 -0
  121. data/lib/familia/refinements/logger_trace.rb +20 -17
  122. data/lib/familia/refinements/stylize_words.rb +65 -0
  123. data/lib/familia/refinements/time_literals.rb +60 -52
  124. data/lib/familia/refinements.rb +2 -1
  125. data/lib/familia/secure_identifier.rb +60 -28
  126. data/lib/familia/settings.rb +83 -7
  127. data/lib/familia/utils.rb +5 -87
  128. data/lib/familia/verifiable_identifier.rb +4 -4
  129. data/lib/familia/version.rb +1 -1
  130. data/lib/familia.rb +72 -14
  131. data/lib/middleware/database_middleware.rb +56 -14
  132. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  133. data/try/configuration/scenarios_try.rb +1 -1
  134. data/try/connection/fiber_context_preservation_try.rb +250 -0
  135. data/try/connection/handler_constraints_try.rb +59 -0
  136. data/try/connection/operation_mode_guards_try.rb +208 -0
  137. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  138. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  139. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  140. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  141. data/try/connection/transaction_mode_strict_try.rb +98 -0
  142. data/try/connection/transaction_mode_warn_try.rb +131 -0
  143. data/try/connection/transaction_modes_try.rb +249 -0
  144. data/try/core/autoloader_try.rb +120 -2
  145. data/try/core/connection_try.rb +7 -7
  146. data/try/core/conventional_inheritance_try.rb +130 -0
  147. data/try/core/create_method_try.rb +15 -23
  148. data/try/core/database_consistency_try.rb +10 -10
  149. data/try/core/errors_try.rb +8 -11
  150. data/try/core/familia_extended_try.rb +2 -2
  151. data/try/core/familia_members_methods_try.rb +76 -0
  152. data/try/core/isolated_dbclient_try.rb +165 -0
  153. data/try/core/middleware_try.rb +16 -16
  154. data/try/core/persistence_operations_try.rb +4 -4
  155. data/try/core/pools_try.rb +42 -26
  156. data/try/core/secure_identifier_try.rb +28 -24
  157. data/try/core/time_utils_try.rb +10 -10
  158. data/try/core/tools_try.rb +1 -1
  159. data/try/core/utils_try.rb +2 -2
  160. data/try/data_types/boolean_try.rb +4 -4
  161. data/try/data_types/datatype_base_try.rb +0 -2
  162. data/try/data_types/list_try.rb +10 -10
  163. data/try/data_types/sorted_set_try.rb +5 -5
  164. data/try/data_types/string_try.rb +12 -12
  165. data/try/data_types/unsortedset_try.rb +33 -0
  166. data/try/debugging/cache_behavior_tracer.rb +7 -7
  167. data/try/debugging/debug_aad_process.rb +1 -1
  168. data/try/debugging/debug_concealed_internal.rb +1 -1
  169. data/try/debugging/debug_cross_context.rb +1 -1
  170. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  171. data/try/debugging/encryption_method_tracer.rb +10 -10
  172. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  173. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  174. data/try/encryption/config_persistence_try.rb +2 -2
  175. data/try/encryption/encryption_core_try.rb +19 -19
  176. data/try/encryption/instance_variable_scope_try.rb +1 -1
  177. data/try/encryption/module_loading_try.rb +2 -2
  178. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  179. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  180. data/try/encryption/secure_memory_handling_try.rb +1 -1
  181. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  182. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  183. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  184. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  185. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  186. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  187. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  188. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  189. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  190. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  191. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  192. data/try/features/feature_dependencies_try.rb +3 -3
  193. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  194. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  195. data/try/features/quantization/quantization_try.rb +1 -1
  196. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  197. data/try/features/relationships/indexing_try.rb +433 -0
  198. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  199. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  200. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  201. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  202. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  203. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  204. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  205. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  206. data/try/features/relationships/relationships_performance_try.rb +20 -20
  207. data/try/features/relationships/relationships_try.rb +27 -38
  208. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  209. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  210. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  211. data/try/helpers/test_cleanup.rb +86 -0
  212. data/try/helpers/test_helpers.rb +3 -3
  213. data/try/horreum/base_try.rb +3 -2
  214. data/try/horreum/commands_try.rb +1 -1
  215. data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
  216. data/try/horreum/initialization_try.rb +11 -7
  217. data/try/horreum/relations_try.rb +21 -13
  218. data/try/horreum/serialization_try.rb +12 -11
  219. data/try/integration/cross_component_try.rb +3 -3
  220. data/try/memory/memory_basic_test.rb +1 -1
  221. data/try/memory/memory_docker_ruby_dump.sh +1 -1
  222. data/try/models/customer_safe_dump_try.rb +1 -1
  223. data/try/models/customer_try.rb +8 -10
  224. data/try/models/datatype_base_try.rb +3 -3
  225. data/try/models/familia_object_try.rb +9 -8
  226. data/try/performance/benchmarks_try.rb +2 -2
  227. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  228. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  229. data/try/prototypes/atomic_saves_v4.rb +1 -1
  230. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  231. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  232. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  233. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  234. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  235. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  236. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  237. data/try/prototypes/pooling/pool_siege.rb +11 -11
  238. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  239. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  240. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  241. data/try/refinements/logger_trace_methods_try.rb +44 -0
  242. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  243. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  244. metadata +75 -43
  245. data/.rubocop_todo.yml +0 -208
  246. data/docs/connection_pooling.md +0 -192
  247. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  248. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  249. data/docs/guides/Feature-System-Autoloading.md +0 -198
  250. data/docs/guides/Home.md +0 -116
  251. data/docs/guides/Relationships-Guide.md +0 -737
  252. data/docs/guides/relationships-methods.md +0 -266
  253. data/docs/reference/auditing_database_commands.rb +0 -228
  254. data/examples/permissions.rb +0 -240
  255. data/lib/familia/features/relationships/cascading.rb +0 -437
  256. data/lib/familia/features/relationships/membership.rb +0 -497
  257. data/lib/familia/features/relationships/permission_management.rb +0 -264
  258. data/lib/familia/features/relationships/querying.rb +0 -615
  259. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  260. data/lib/familia/features/relationships/tracking.rb +0 -418
  261. data/lib/familia/refinements/snake_case.rb +0 -40
  262. data/lib/familia/validation/command_recorder.rb +0 -336
  263. data/lib/familia/validation/expectations.rb +0 -519
  264. data/lib/familia/validation/validation_helpers.rb +0 -443
  265. data/lib/familia/validation/validator.rb +0 -412
  266. data/lib/familia/validation.rb +0 -140
  267. data/try/data_types/set_try.rb +0 -33
  268. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  269. data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
  270. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
  271. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  272. data/try/validation/command_validation_try.rb.disabled +0 -207
  273. data/try/validation/performance_validation_try.rb.disabled +0 -324
  274. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -24,6 +24,20 @@ hex_id = Familia.generate_id(16)
24
24
  [hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
25
25
  #=> [String, true, true]
26
26
 
27
+ ## Familia.generate_lite_id
28
+ Familia.respond_to?(:generate_lite_id)
29
+ #=> true
30
+
31
+ ## Can generate a default base-36 lite ID
32
+ lite_id = Familia.generate_lite_id
33
+ [lite_id.class, lite_id.length > 10, lite_id.match?(/^[a-z0-9]+$/)]
34
+ #=> [String, true, true]
35
+
36
+ ## Can generate a lite ID with a custom base (hex)
37
+ hex_lite_id = Familia.generate_lite_id(16)
38
+ [hex_lite_id.class, hex_lite_id.length == 32, hex_lite_id.match?(/^[a-f0-9]+$/)]
39
+ #=> [String, true, true]
40
+
27
41
  ## Familia.generate_trace_id
28
42
  Familia.respond_to?(:generate_trace_id)
29
43
  #=> true
@@ -35,39 +49,29 @@ trace_id = Familia.generate_trace_id
35
49
 
36
50
  ## Can generate a trace ID with a custom base (hex)
37
51
  hex_trace_id = Familia.generate_trace_id(16)
38
- [hex_trace_id.class, hex_trace_id.length == 16]
39
- #=> [String, true]
40
-
41
- ## Familia.generate_hex_id
42
- Familia.respond_to?(:generate_hex_id)
43
- #=> true
44
-
45
- ## Can generate a 256-bit hex ID
46
- hex_id = Familia.generate_hex_id
47
- [hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
52
+ [hex_trace_id.class, hex_trace_id.length == 16, hex_trace_id.match?(/^[a-f0-9]+$/)]
48
53
  #=> [String, true, true]
49
54
 
50
- ## Familia.generate_hex_trace_id
51
- Familia.respond_to?(:generate_hex_trace_id)
52
- #=> true
55
+ ## Generated lite IDs are unique
56
+ [Familia.generate_lite_id == Familia.generate_lite_id]
57
+ #=> [false]
53
58
 
54
- ## Can generate a 64-bit hex trace ID
55
- hex_trace_id = Familia.generate_hex_trace_id
56
- [hex_trace_id.class, hex_trace_id.length == 16, hex_trace_id.match?(/^[a-f0-9]+$/)]
57
- #=> [String, true, true]
59
+ ## Generated trace IDs are unique
60
+ [Familia.generate_trace_id == Familia.generate_trace_id]
61
+ #=> [false]
58
62
 
59
63
  ## Familia.shorten_to_trace_id
60
64
  Familia.respond_to?(:shorten_to_trace_id)
61
65
  #=> true
62
66
 
63
67
  ## Can shorten hex ID to trace ID (64 bits)
64
- hex_id = Familia.generate_hex_id
68
+ hex_id = Familia.generate_id(16)
65
69
  trace_id = Familia.shorten_to_trace_id(hex_id)
66
70
  [trace_id.class, trace_id.length < hex_id.length]
67
71
  #=> [String, true]
68
72
 
69
73
  ## Can shorten hex ID to trace ID with custom base (hex)
70
- hex_id = Familia.generate_hex_id
74
+ hex_id = Familia.generate_id(16)
71
75
  hex_trace_id = Familia.shorten_to_trace_id(hex_id, base: 16)
72
76
  [hex_trace_id.class, hex_trace_id.length == 16]
73
77
  #=> [String, true]
@@ -77,25 +81,25 @@ Familia.respond_to?(:truncate_hex)
77
81
  #=> true
78
82
 
79
83
  ## Can truncate hex ID to 128 bits by default
80
- hex_id = Familia.generate_hex_id
84
+ hex_id = Familia.generate_id(16)
81
85
  truncated_id = Familia.truncate_hex(hex_id)
82
86
  [truncated_id.class, truncated_id.length < hex_id.length]
83
87
  #=> [String, true]
84
88
 
85
89
  ## Can truncate hex ID to a custom bit length (64 bits)
86
- hex_id = Familia.generate_hex_id
90
+ hex_id = Familia.generate_id(16)
87
91
  truncated_64 = Familia.truncate_hex(hex_id, bits: 64)
88
92
  [truncated_64.class, truncated_64.length < hex_id.length]
89
93
  #=> [String, true]
90
94
 
91
95
  ## Can truncate with a custom base (hex)
92
- hex_id = Familia.generate_hex_id
96
+ hex_id = Familia.generate_id(16)
93
97
  hex_truncated = Familia.truncate_hex(hex_id, bits: 128, base: 16)
94
98
  [hex_truncated.class, hex_truncated.length == 32]
95
99
  #=> [String, true]
96
100
 
97
101
  ## Truncated IDs are deterministic
98
- hex_id = Familia.generate_hex_id
102
+ hex_id = Familia.generate_id(16)
99
103
  id1 = Familia.truncate_hex(hex_id)
100
104
  id2 = Familia.truncate_hex(hex_id)
101
105
  id1 == id2
@@ -118,7 +122,7 @@ end
118
122
  #=> "Input bits (12) cannot be less than desired output bits (64)."
119
123
 
120
124
  ## Shortened IDs are deterministic
121
- hex_id = Familia.generate_hex_id
125
+ hex_id = Familia.generate_id(16)
122
126
  id1 = Familia.shorten_to_trace_id(hex_id)
123
127
  id2 = Familia.shorten_to_trace_id(hex_id)
124
128
  id1 == id2
@@ -52,51 +52,51 @@ result.round(0)
52
52
  #=> 31556952.0
53
53
 
54
54
  ## Numeric#age_in - calculate age in months from timestamp (approximately 1 month ago)
55
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_MONTH
55
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
56
56
  result = RefinedContext.eval_in_refined_context("#{timestamp}.age_in(:months)")
57
57
  (result - 1.0).abs < 0.01
58
58
  #=> true
59
59
 
60
60
  ## Numeric#age_in - calculate age in years from timestamp (approximately 1 year ago)
61
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_YEAR
61
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
62
62
  result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.age_in(:years)")
63
63
  (result - 1.0).abs < 0.01
64
64
  #=> true
65
65
 
66
66
  ## Numeric#months_old - convenience method for age_in(:months)
67
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_MONTH
67
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
68
68
  result = RefinedContext.eval_in_refined_context("#{timestamp}.months_old")
69
69
  (result - 1.0).abs < 0.01
70
70
  #=> true
71
71
 
72
72
  ## Numeric#years_old - convenience method for age_in(:years)
73
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_YEAR
73
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
74
74
  result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.years_old")
75
75
  (result - 1.0).abs < 0.01
76
76
  #=> true
77
77
 
78
78
  ## Numeric#months_old - should NOT return seconds (the original bug)
79
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_MONTH
79
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
80
80
  result = RefinedContext.eval_in_refined_context("#{timestamp}.months_old")
81
81
  result.between?(0.9, 1.1) # Should be ~1 month, not millions of seconds
82
82
  #=> true
83
83
 
84
84
  ## Numeric#years_old - should NOT return seconds (the original bug)
85
- timestamp = Time.now.to_f - Familia::Refinements::TimeLiterals::PER_YEAR
85
+ timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
86
86
  result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.years_old")
87
87
  result.between?(0.9, 1.1) # Should be ~1 year, not millions of seconds
88
88
  #=> true
89
89
 
90
90
  ## age_in with from_time parameter - months
91
- past_time = Time.now - (2 * Familia::Refinements::TimeLiterals::PER_MONTH) # 2 months ago
92
- from_time = Time.now - Familia::Refinements::TimeLiterals::PER_MONTH # 1 month ago
91
+ past_time = Familia.now - (2 * Familia::Refinements::TimeLiterals::PER_MONTH) # 2 months ago
92
+ from_time = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH # 1 month ago
93
93
  result = RefinedContext.eval_in_refined_context("#{past_time.to_f}.age_in(:months, #{from_time.to_f})")
94
94
  (result - 1.0).abs < 0.01
95
95
  #=> true
96
96
 
97
97
  ## age_in with from_time parameter - years
98
- past_time = Time.now - (2 * Familia::Refinements::TimeLiterals::PER_YEAR) # 2 years ago
99
- from_time = Time.now - Familia::Refinements::TimeLiterals::PER_YEAR # 1 year ago
98
+ past_time = Familia.now - (2 * Familia::Refinements::TimeLiterals::PER_YEAR) # 2 years ago
99
+ from_time = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR # 1 year ago
100
100
  result = RefinedContext.instance_eval_in_refined_context("#{past_time.to_f}.age_in(:years, #{from_time.to_f})")
101
101
  (result - 1.0).abs < 0.01
102
102
  #=> true
@@ -4,7 +4,7 @@
4
4
 
5
5
  require_relative '../helpers/test_helpers'
6
6
 
7
- ## move_keys across Redis instances (if available)
7
+ ## move_keys across Valkey/Redis instances (if available)
8
8
  begin
9
9
  source_redis = Redis.new(db: 10)
10
10
  dest_redis = Redis.new(db: 11)
@@ -72,10 +72,10 @@ sym_result = Familia.distinguisher(:symbol)
72
72
  ## distinguisher raises error for high-risk types with strict mode
73
73
  begin
74
74
  Familia.distinguisher(true, strict_values: true)
75
- rescue Familia::HighRiskFactor => e
75
+ rescue Familia::NotDistinguishableError => e
76
76
  e.class
77
77
  end
78
- #=> Familia::HighRiskFactor
78
+ #=> Familia::NotDistinguishableError
79
79
 
80
80
  ## distinguisher allows high-risk types with non-strict mode
81
81
  result = Familia.distinguisher(false, strict_values: false)
@@ -17,10 +17,10 @@ Familia.debug = false
17
17
  ## Trying to store a boolean value to a hash key raises an exception
18
18
  begin
19
19
  @hashkey['test'] = true
20
- rescue Familia::HighRiskFactor => e
20
+ rescue Familia::NotDistinguishableError => e
21
21
  e.message
22
22
  end
23
- #=> "High risk factor for serialization bugs: true<TrueClass>"
23
+ #=> "Cannot represent true<TrueClass> as a string"
24
24
 
25
25
  ## Boolean values are returned as strings
26
26
  @hashkey['test']
@@ -29,10 +29,10 @@ end
29
29
  ## Trying to store a nil value to a hash key raises an exception
30
30
  begin
31
31
  @hashkey['test'] = nil
32
- rescue Familia::HighRiskFactor => e
32
+ rescue Familia::NotDistinguishableError => e
33
33
  e.message
34
34
  end
35
- #=> "High risk factor for serialization bugs: <NilClass>"
35
+ #=> "Cannot represent <NilClass> as a string"
36
36
 
37
37
  ## The exceptions prevented the hash from being updated
38
38
  @hashkey['test']
@@ -30,8 +30,6 @@ RefinedContext.eval_in_refined_context("@limiter1.counter.qstamp(10.minutes, '%H
30
30
 
31
31
  ## Limiter#qstamp as a number
32
32
  @limiter2 = Limiter.new :requests
33
- p [@limiter1.default_expiration, @limiter2.default_expiration]
34
- p [@limiter1.counter.parent.default_expiration, @limiter2.counter.parent.default_expiration]
35
33
  RefinedContext.instance_variable_set(:@limiter2, @limiter2)
36
34
  RefinedContext.eval_in_refined_context("@limiter2.counter.qstamp(10.minutes, pattern: nil, time: 1_302_468_980)")
37
35
  #=> 1302468600
@@ -4,37 +4,37 @@ require_relative '../helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new 'atoken'
6
6
 
7
- ## Familia::List#push
7
+ ## Familia::ListKey#push
8
8
  ret = @a.owners.push :value1
9
9
  ret.class
10
- #=> Familia::List
10
+ #=> Familia::ListKey
11
11
 
12
- ## Familia::List#<<
12
+ ## Familia::ListKey#<<
13
13
  ret = @a.owners << :value2 << :value3 << :value4
14
14
  ret.class
15
- #=> Familia::List
15
+ #=> Familia::ListKey
16
16
 
17
- ## Familia::List#pop
17
+ ## Familia::ListKey#pop
18
18
  @a.owners.pop
19
19
  #=> 'value4'
20
20
 
21
- ## Familia::List#first
21
+ ## Familia::ListKey#first
22
22
  @a.owners.first
23
23
  #=> 'value1'
24
24
 
25
- ## Familia::List#last
25
+ ## Familia::ListKey#last
26
26
  @a.owners.last
27
27
  #=> 'value3'
28
28
 
29
- ## Familia::List#to_a
29
+ ## Familia::ListKey#to_a
30
30
  @a.owners.to_a
31
31
  #=> ['value1','value2','value3']
32
32
 
33
- ## Familia::List#delete
33
+ ## Familia::ListKey#delete
34
34
  @a.owners.remove 'value3'
35
35
  #=> 1
36
36
 
37
- ## Familia::List#size
37
+ ## Familia::ListKey#size
38
38
  @a.owners.size
39
39
  #=> 2
40
40
 
@@ -6,11 +6,11 @@ require_relative '../helpers/test_helpers'
6
6
 
7
7
  ## Familia::SortedSet#add
8
8
  @a = Bone.new 'atoken'
9
- @a.metrics.add 2, :metric2
10
- @a.metrics.add 4, :metric4
11
- @a.metrics.add 0, :metric0
12
- @a.metrics.add 1, :metric1
13
- @a.metrics.add 3, :metric3
9
+ @a.metrics.add :metric2, 2
10
+ @a.metrics.add :metric4, 4
11
+ @a.metrics.add :metric0, 0
12
+ @a.metrics.add :metric1, 1
13
+ @a.metrics.add :metric3, 3
14
14
  #=> true
15
15
 
16
16
  ## Familia::SortedSet#members
@@ -8,24 +8,24 @@ require_relative '../helpers/test_helpers'
8
8
  @a.dbkey
9
9
  #=> 'bone:atoken2:object'
10
10
 
11
- ## Familia::String#value should give default value
11
+ ## Familia::StringKey#value should give default value
12
12
  @a.value.value
13
13
  #=> 'GREAT!'
14
14
 
15
- ## Familia::String#value=
15
+ ## Familia::StringKey#value=
16
16
  @a.value.value = 'DECENT!'
17
17
  #=> 'DECENT!'
18
18
 
19
- ## Familia::String#to_s
19
+ ## Familia::StringKey#to_s
20
20
  @a.value.to_s
21
21
  #=> 'DECENT!'
22
22
 
23
- ## Familia::String#destroy!
23
+ ## Familia::StringKey#destroy!
24
24
  @a.value.delete!
25
25
  #=> true
26
26
 
27
- ## Familia::String.new
28
- @ret = Familia::String.new 'arbitrary:key'
27
+ ## Familia::StringKey.new
28
+ @ret = Familia::StringKey.new 'arbitrary:key'
29
29
  @ret.dbkey
30
30
  #=> 'arbitrary:key'
31
31
 
@@ -37,27 +37,27 @@ require_relative '../helpers/test_helpers'
37
37
  @ret.value
38
38
  #=> '1000'
39
39
 
40
- ## Familia::String#increment
40
+ ## Familia::StringKey#increment
41
41
  @ret.increment
42
42
  #=> 1001
43
43
 
44
- ## Familia::String#incrementby
44
+ ## Familia::StringKey#incrementby
45
45
  @ret.incrementby 99
46
46
  #=> 1100
47
47
 
48
- ## Familia::String#decrement
48
+ ## Familia::StringKey#decrement
49
49
  @ret.decrement
50
50
  #=> 1099
51
51
 
52
- ## Familia::String#decrementby
52
+ ## Familia::StringKey#decrementby
53
53
  @ret.decrementby 49
54
54
  #=> 1050
55
55
 
56
- ## Familia::String#append
56
+ ## Familia::StringKey#append
57
57
  @ret.append 'bytes'
58
58
  #=> 9
59
59
 
60
- ## Familia::String#value after append
60
+ ## Familia::StringKey#value after append
61
61
  @ret.value
62
62
  #=> '1050bytes'
63
63
 
@@ -0,0 +1,33 @@
1
+ # try/data_types/unsortedset_try.rb
2
+
3
+ require_relative '../helpers/test_helpers'
4
+
5
+ @a = Bone.new 'atoken'
6
+
7
+ ## Familia::UnsortedSet#add
8
+ ret = @a.tags.add :a
9
+ ret.class
10
+ #=> Familia::UnsortedSet
11
+
12
+ ## Familia::UnsortedSet#<<
13
+ ret = @a.tags << :a << :b << :c
14
+ ret.class
15
+ #=> Familia::UnsortedSet
16
+
17
+ ## Familia::UnsortedSet#members
18
+ @a.tags.members.sort
19
+ #=> ['a', 'b', 'c']
20
+
21
+ ## Familia::UnsortedSet#member? knows when a value exists
22
+ @a.tags.member? :a
23
+ #=> true
24
+
25
+ ## Familia::UnsortedSet#member? knows when a value doesn't exist
26
+ @a.tags.member? :x
27
+ #=> false
28
+
29
+ ## Familia::UnsortedSet#member? knows when a value doesn't exist
30
+ @a.tags.size
31
+ #=> 3
32
+
33
+ @a.tags.delete!
@@ -14,9 +14,9 @@ Familia.config.encryption_keys = test_keys
14
14
  Familia.config.current_key_version = :v1
15
15
 
16
16
  # Clear any existing cache
17
- Thread.current[:familia_key_cache] = nil
17
+ Fiber[:familia_key_cache] = nil
18
18
  puts "1. Initial Cache State:"
19
- puts " Cache: #{Thread.current[:familia_key_cache].inspect}"
19
+ puts " Cache: #{Fiber[:familia_key_cache].inspect}"
20
20
  puts
21
21
 
22
22
  # Define test model with multiple encrypted fields
@@ -32,14 +32,14 @@ end
32
32
 
33
33
  puts "2. Model Creation:"
34
34
  user = CacheTraceModel.new(user_id: 'cache-user-1')
35
- puts " After model creation: #{Thread.current[:familia_key_cache].inspect}"
35
+ puts " After model creation: #{Fiber[:familia_key_cache].inspect}"
36
36
  puts
37
37
 
38
38
  puts "3. Setting Encrypted Fields:"
39
39
  ['field_a', 'field_b', 'field_c'].each_with_index do |field, i|
40
40
  puts " Setting #{field}..."
41
41
  user.public_send("#{field}=", "test-value-#{i+1}")
42
- cache = Thread.current[:familia_key_cache]
42
+ cache = Fiber[:familia_key_cache]
43
43
  if cache
44
44
  puts " Cache size: #{cache.size}"
45
45
  puts " Cache keys: #{cache.keys.inspect}"
@@ -54,7 +54,7 @@ puts "4. Reading Encrypted Fields:"
54
54
  puts " Reading #{field}..."
55
55
  value = user.public_send(field)
56
56
  puts " Retrieved: #{value}"
57
- cache = Thread.current[:familia_key_cache]
57
+ cache = Fiber[:familia_key_cache]
58
58
  if cache
59
59
  puts " Cache size: #{cache.size}"
60
60
  puts " Cache keys: #{cache.keys.inspect}"
@@ -65,7 +65,7 @@ end
65
65
  puts
66
66
 
67
67
  puts "5. Final Cache Analysis:"
68
- cache = Thread.current[:familia_key_cache]
68
+ cache = Fiber[:familia_key_cache]
69
69
  if cache && !cache.empty?
70
70
  puts " Cache size: #{cache.size} entries"
71
71
  cache.each_with_index do |(key, value), i|
@@ -81,7 +81,7 @@ puts "6. Multiple Models Cache Test:"
81
81
  user2 = CacheTraceModel.new(user_id: 'cache-user-2')
82
82
  user2.field_a = 'different-value'
83
83
  puts " After second model field set:"
84
- cache = Thread.current[:familia_key_cache]
84
+ cache = Fiber[:familia_key_cache]
85
85
  if cache
86
86
  puts " Cache size: #{cache.size}"
87
87
  puts " Unique contexts: #{cache.keys.map { |k| k.split(':').last }.uniq.size}"
@@ -3,7 +3,7 @@
3
3
  $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
4
4
  ENV['TEST'] = 'true'
5
5
  require 'familia'
6
- require_relative 'try/helpers/test_helpers'
6
+ require_relative '../helpers/test_helpers'
7
7
 
8
8
  puts "Debugging AAD during encrypt/decrypt process..."
9
9
 
@@ -3,7 +3,7 @@
3
3
  $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
4
4
  ENV['TEST'] = 'true' # Mark as test environment
5
5
  require 'familia'
6
- require_relative 'try/helpers/test_helpers'
6
+ require_relative '../helpers/test_helpers'
7
7
 
8
8
  puts "Testing ConcealedString internal call chain..."
9
9
 
@@ -44,7 +44,7 @@ puts "cipher_b: #{cipher_b.class} - #{cipher_b.encrypted_value}"
44
44
  # Now try cross-context access
45
45
  puts "\n=== Cross-context test ==="
46
46
  model_a.instance_variable_set(:@api_key, cipher_b)
47
- puts "Set cipher_b into model_a"
47
+ puts "UnsortedSet cipher_b into model_a"
48
48
 
49
49
  begin
50
50
  result = model_a.api_key
@@ -45,7 +45,7 @@ puts "cipher_b encrypted: #{cipher_b.encrypted_value}"
45
45
  # Now try cross-context access
46
46
  puts "\n=== Cross-context test ==="
47
47
  model_a.instance_variable_set(:@api_key, cipher_b)
48
- puts "Set cipher_b into model_a"
48
+ puts "UnsortedSet cipher_b into model_a"
49
49
 
50
50
  begin
51
51
  result = model_a.api_key
@@ -22,9 +22,9 @@ module Familia
22
22
  puts " Plaintext length: #{plaintext&.length} chars"
23
23
  puts " AAD: #{additional_data ? 'present' : 'none'}"
24
24
 
25
- start_time = Time.now
25
+ start_time = Familia.now
26
26
  result = orig_encrypt(plaintext, context: context, additional_data: additional_data)
27
- elapsed = ((Time.now - start_time) * 1000).round(2)
27
+ elapsed = ((Familia.now - start_time) * 1000).round(2)
28
28
 
29
29
  puts " Result length: #{result ? result.length : 'nil'} chars"
30
30
  puts " Elapsed: #{elapsed}ms"
@@ -38,17 +38,17 @@ module Familia
38
38
  puts " Encrypted length: #{encrypted_json&.length} chars"
39
39
  puts " AAD: #{additional_data ? 'present' : 'none'}"
40
40
 
41
- start_time = Time.now
41
+ start_time = Familia.now
42
42
  begin
43
43
  result = orig_decrypt(encrypted_json, context: context, additional_data: additional_data)
44
- elapsed = ((Time.now - start_time) * 1000).round(2)
44
+ elapsed = ((Familia.now - start_time) * 1000).round(2)
45
45
 
46
46
  puts " Result length: #{result ? result.length : 'nil'} chars"
47
47
  puts " Elapsed: #{elapsed}ms"
48
48
  puts
49
49
  result
50
50
  rescue => e
51
- elapsed = ((Time.now - start_time) * 1000).round(2)
51
+ elapsed = ((Familia.now - start_time) * 1000).round(2)
52
52
  puts " ERROR: #{e.class}: #{e.message}"
53
53
  puts " Elapsed: #{elapsed}ms"
54
54
  puts
@@ -66,16 +66,16 @@ module Familia
66
66
  puts " Context: #{context}"
67
67
  puts " Version: #{version || 'current'}"
68
68
 
69
- cache = Thread.current[:familia_key_cache] ||= {}
69
+ cache = Fiber[:familia_key_cache] ||= {}
70
70
  cache_key = "#{version || current_key_version}:#{context}"
71
71
  puts " Cache key: #{cache_key}"
72
72
  puts " Cache before: #{cache.keys.inspect}"
73
73
 
74
- start_time = Time.now
74
+ start_time = Familia.now
75
75
  result = orig_derive_key_with_provider(provider, context, version: version)
76
- elapsed = ((Time.now - start_time) * 1000).round(2)
76
+ elapsed = ((Familia.now - start_time) * 1000).round(2)
77
77
 
78
- cache_after = Thread.current[:familia_key_cache] || {}
78
+ cache_after = Fiber[:familia_key_cache] || {}
79
79
  puts " Cache after: #{cache_after.keys.inspect}"
80
80
  puts " Derived key: [#{result.bytesize} bytes]"
81
81
  puts " Elapsed: #{elapsed}ms"
@@ -92,7 +92,7 @@ Familia.config.encryption_keys = test_keys
92
92
  Familia.config.current_key_version = :v1
93
93
 
94
94
  # Clear cache
95
- Thread.current[:familia_key_cache] = nil
95
+ Fiber[:familia_key_cache] = nil
96
96
 
97
97
  # Define test model
98
98
  class TraceTestModel < Familia::Horreum
@@ -34,7 +34,7 @@ end
34
34
  @test_obj.config[:nested].keys
35
35
  #=> [:theme]
36
36
 
37
- ## Get raw JSON from Redis
37
+ ## Get raw JSON from Valkey/Redis
38
38
  @raw_json = @test_obj.hget('config')
39
39
  @raw_json.class
40
40
  #=> String
@@ -16,7 +16,7 @@ begin
16
16
  session = session_class.new(session_id: 'test123', name: 'Session')
17
17
  session.save
18
18
 
19
- # Set shorter TTL
19
+ # UnsortedSet shorter TTL
20
20
  session.expire(60)
21
21
  original_ttl = session.realttl
22
22
 
@@ -90,7 +90,7 @@ end
90
90
  provider = @provider_class.new
91
91
  master_key = 'a' * 32
92
92
  context = 'TestModel:field:user123'
93
- # Set config with null byte
93
+ # UnsortedSet config with null byte
94
94
  original_personal = Familia.config.encryption_personalization
95
95
  Familia.config.encryption_personalization = "bad\0config"
96
96
  begin
@@ -189,4 +189,4 @@ true
189
189
  #=> true
190
190
 
191
191
  # TEARDOWN
192
- Thread.current[:familia_key_cache]&.clear if Thread.current[:familia_key_cache]
192
+ Fiber[:familia_key_cache]&.clear if Fiber[:familia_key_cache]