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
@@ -51,10 +51,28 @@ class PoolTestSession < Familia::Horreum
51
51
 
52
52
  def init
53
53
  @session_id ||= SecureRandom.hex(8)
54
- @created_at ||= Time.now.to_i
54
+ @created_at ||= Familia.now.to_i
55
55
  end
56
56
  end
57
57
 
58
+ class PoolTestAccountDB1 < Familia::Horreum
59
+ logical_database 1
60
+ identifier_field :account_id
61
+ field :account_id
62
+ field :balance, on_conflict: :skip
63
+ field :holder_name
64
+
65
+ def init
66
+ @account_id ||= SecureRandom.hex(6)
67
+ @balance = @balance.to_f if @balance
68
+ end
69
+
70
+ def balance
71
+ @balance&.to_f
72
+ end
73
+ end
74
+
75
+
58
76
  ## Clean up before tests
59
77
  PoolTestAccount.dbclient.flushdb
60
78
  #=> "OK"
@@ -79,23 +97,6 @@ Familia.connection_provider.is_a?(Proc)
79
97
  #=> true
80
98
 
81
99
  ## Test 5: Account in DB 1 via class configuration
82
- class PoolTestAccountDB1 < Familia::Horreum
83
- self.logical_database = 1
84
- identifier_field :account_id
85
- field :account_id
86
- field :balance, on_conflict: :skip
87
- field :holder_name
88
-
89
- def init
90
- @account_id ||= SecureRandom.hex(6)
91
- @balance = @balance.to_f if @balance
92
- end
93
-
94
- def balance
95
- @balance&.to_f
96
- end
97
- end
98
-
99
100
  @account_db1 = PoolTestAccountDB1.new(balance: 750, holder_name: "Charlie")
100
101
  @account_db1.save
101
102
  #=> true
@@ -140,8 +141,8 @@ threads.each(&:join)
140
141
  # Test that transaction connection is available
141
142
  conn.ping
142
143
  end
143
- # Transaction returns array with results
144
- @transfer_result.first
144
+ # Transaction returns MultiResult with success status and results
145
+ @transfer_result.results.first
145
146
  #=> "PONG"
146
147
 
147
148
  ## Test 12: Transaction block executes properly
@@ -149,19 +150,19 @@ end
149
150
  [@account_a.balance, @account_b.balance]
150
151
  #=> [1000.0, 500.0]
151
152
 
152
- ## Test 13: with_connection method
153
- @connection_test_result = Familia.with_connection do |conn|
153
+ ## Test 13: with_dbclient method
154
+ @connection_test_result = Familia.with_dbclient do |conn|
154
155
  conn.set("test_key_#{SecureRandom.hex(4)}", "test_value")
155
156
  end
156
157
  @connection_test_result
157
158
  #=> "OK"
158
159
 
159
160
  ## Test 14: Pipeline operations with connection pool
160
- @pipeline_results = Familia.pipeline do |conn|
161
+ @pipeline_results = Familia.pipelined do |conn|
161
162
  conn.ping
162
163
  end
163
164
  # Pipeline executes successfully
164
- @pipeline_results.first
165
+ @pipeline_results.results.first
165
166
  #=> "PONG"
166
167
 
167
168
  ## Test 15: Multi/EXEC operations with connection pool
@@ -169,7 +170,7 @@ end
169
170
  conn.ping
170
171
  end
171
172
  # Multi/EXEC executes successfully
172
- @multi_results.first
173
+ @multi_results.results.first
173
174
  #=> "PONG"
174
175
 
175
176
  ## Test 16: Error handling in transactions
@@ -208,7 +209,7 @@ timeout_mutex = Mutex.new
208
209
  3.times do |i|
209
210
  timeout_threads << Thread.new do
210
211
  begin
211
- result = Familia.with_connection do |conn|
212
+ result = Familia.with_dbclient do |conn|
212
213
  sleep(0.1) # Brief hold
213
214
  conn.ping
214
215
  end
@@ -270,4 +271,19 @@ Familia.connection_provider = original_provider
270
271
  @captured_uris.any? { |uri| uri.include?('redis://') }
271
272
  #=> true
272
273
 
274
+ ## Check PoolTestAccountDB1 config name
275
+ PoolTestAccountDB1.config_name
276
+ #=> 'pool_test_account_db1'
277
+
278
+
273
279
  puts "Connection pool tests completed successfully!"
280
+
281
+ # Teardown
282
+ Familia.connection_provider = nil
283
+ Fiber[:familia_connection] = nil
284
+ Fiber[:familia_connection_handler_class] = nil
285
+ Fiber[:familia_transaction] = nil
286
+ Fiber[:familia_pipeline] = nil
287
+ Fiber[:familia_key_cache] = nil
288
+ Fiber[:familia_request_cache] = nil
289
+ Fiber[:familia_request_cache_enabled] = nil
@@ -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