familia 2.0.0.pre17 → 2.0.0.pre18

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 (220) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rst +60 -0
  3. data/CLAUDE.md +9 -2
  4. data/Gemfile.lock +1 -1
  5. data/README.md +13 -0
  6. data/bin/irb +1 -1
  7. data/docs/guides/core-field-system.md +48 -26
  8. data/docs/migrating/v2.0.0-pre18.md +58 -0
  9. data/docs/qodo-merge-compliance.md +96 -0
  10. data/lib/familia/base.rb +0 -2
  11. data/lib/familia/connection/middleware.rb +58 -4
  12. data/lib/familia/connection.rb +1 -1
  13. data/lib/familia/data_type/{commands.rb → database_commands.rb} +2 -2
  14. data/lib/familia/data_type/serialization.rb +5 -5
  15. data/lib/familia/data_type.rb +2 -2
  16. data/lib/familia/encryption/encrypted_data.rb +12 -2
  17. data/lib/familia/encryption/manager.rb +11 -4
  18. data/lib/familia/features/autoloader.rb +3 -1
  19. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +11 -3
  20. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +9 -9
  21. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +41 -27
  22. data/lib/familia/features/safe_dump.rb +2 -3
  23. data/lib/familia/horreum/database_commands.rb +1 -1
  24. data/lib/familia/horreum/definition.rb +6 -37
  25. data/lib/familia/horreum/management.rb +17 -12
  26. data/lib/familia/horreum/persistence.rb +1 -1
  27. data/lib/familia/horreum/serialization.rb +91 -73
  28. data/lib/familia/horreum.rb +10 -6
  29. data/lib/familia/identifier_extractor.rb +60 -0
  30. data/lib/familia/logging.rb +271 -112
  31. data/lib/familia/refinements.rb +0 -1
  32. data/lib/familia/version.rb +1 -1
  33. data/lib/familia.rb +2 -2
  34. data/lib/middleware/{database_middleware.rb → database_logger.rb} +47 -14
  35. data/pr_agent.toml +31 -0
  36. data/pr_compliance_checklist.yaml +45 -0
  37. data/try/edge_cases/empty_identifiers_try.rb +1 -1
  38. data/try/edge_cases/hash_symbolization_try.rb +31 -31
  39. data/try/edge_cases/json_serialization_try.rb +2 -2
  40. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +170 -0
  41. data/try/edge_cases/race_conditions_try.rb +1 -1
  42. data/try/edge_cases/reserved_keywords_try.rb +1 -1
  43. data/try/edge_cases/string_coercion_try.rb +1 -1
  44. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  45. data/try/features/encrypted_fields/aad_protection_try.rb +1 -1
  46. data/try/features/encrypted_fields/concealed_string_core_try.rb +1 -1
  47. data/try/features/encrypted_fields/context_isolation_try.rb +1 -1
  48. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  49. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +1 -1
  50. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +1 -1
  51. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +1 -1
  52. data/try/features/encrypted_fields/error_conditions_try.rb +1 -1
  53. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +1 -1
  54. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  55. data/try/features/encrypted_fields/key_rotation_try.rb +1 -1
  56. data/try/features/encrypted_fields/memory_security_try.rb +1 -1
  57. data/try/features/encrypted_fields/missing_current_key_version_try.rb +1 -1
  58. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  59. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +1 -1
  60. data/try/features/encrypted_fields/thread_safety_try.rb +1 -1
  61. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +1 -1
  62. data/try/{encryption → features/encryption}/config_persistence_try.rb +1 -1
  63. data/try/{encryption/encryption_core_try.rb → features/encryption/core_try.rb} +2 -2
  64. data/try/{encryption → features/encryption}/instance_variable_scope_try.rb +1 -1
  65. data/try/{encryption → features/encryption}/module_loading_try.rb +1 -1
  66. data/try/{encryption → features/encryption}/providers/aes_gcm_provider_try.rb +1 -1
  67. data/try/{encryption → features/encryption}/providers/xchacha20_poly1305_provider_try.rb +1 -1
  68. data/try/{encryption → features/encryption}/roundtrip_validation_try.rb +1 -1
  69. data/try/{encryption → features/encryption}/secure_memory_handling_try.rb +2 -2
  70. data/try/features/expiration/expiration_try.rb +1 -1
  71. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  72. data/try/features/feature_dependencies_try.rb +1 -1
  73. data/try/features/feature_improvements_try.rb +1 -1
  74. data/try/features/object_identifier/object_identifier_integration_try.rb +1 -1
  75. data/try/features/object_identifier/object_identifier_try.rb +1 -1
  76. data/try/features/quantization/quantization_try.rb +1 -1
  77. data/try/features/real_feature_integration_try.rb +17 -14
  78. data/try/features/relationships/indexing_commands_verification_try.rb +8 -3
  79. data/try/features/relationships/indexing_try.rb +6 -1
  80. data/try/features/relationships/participation_commands_verification_spec.rb +1 -1
  81. data/try/features/relationships/participation_commands_verification_try.rb +4 -4
  82. data/try/features/relationships/participation_performance_improvements_try.rb +1 -1
  83. data/try/features/relationships/participation_reverse_index_try.rb +1 -1
  84. data/try/features/relationships/relationships_api_changes_try.rb +1 -1
  85. data/try/features/relationships/relationships_edge_cases_try.rb +3 -3
  86. data/try/features/relationships/relationships_performance_minimal_try.rb +1 -1
  87. data/try/features/relationships/relationships_performance_simple_try.rb +1 -1
  88. data/try/features/relationships/relationships_performance_try.rb +1 -1
  89. data/try/features/relationships/relationships_performance_working_try.rb +1 -1
  90. data/try/features/relationships/relationships_try.rb +1 -1
  91. data/try/features/safe_dump/safe_dump_advanced_try.rb +1 -1
  92. data/try/features/safe_dump/safe_dump_try.rb +1 -1
  93. data/try/features/transient_fields/redacted_string_try.rb +1 -1
  94. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  95. data/try/features/transient_fields/single_use_redacted_string_try.rb +1 -1
  96. data/try/features/transient_fields/transient_fields_core_try.rb +1 -1
  97. data/try/features/transient_fields/transient_fields_integration_try.rb +1 -1
  98. data/try/{connection → integration/connection}/fiber_context_preservation_try.rb +1 -1
  99. data/try/{connection → integration/connection}/handler_constraints_try.rb +1 -1
  100. data/try/{core → integration/connection}/isolated_dbclient_try.rb +1 -1
  101. data/try/integration/connection/middleware_reconnect_try.rb +87 -0
  102. data/try/{connection → integration/connection}/operation_mode_guards_try.rb +1 -1
  103. data/try/{connection → integration/connection}/pipeline_fallback_integration_try.rb +1 -1
  104. data/try/{core → integration/connection}/pools_try.rb +1 -1
  105. data/try/{connection → integration/connection}/responsibility_chain_tracking_try.rb +1 -1
  106. data/try/{connection → integration/connection}/transaction_fallback_integration_try.rb +1 -1
  107. data/try/{connection → integration/connection}/transaction_mode_permissive_try.rb +1 -1
  108. data/try/{connection → integration/connection}/transaction_mode_strict_try.rb +1 -1
  109. data/try/{connection → integration/connection}/transaction_mode_warn_try.rb +1 -1
  110. data/try/{connection → integration/connection}/transaction_modes_try.rb +1 -1
  111. data/try/{core → integration}/conventional_inheritance_try.rb +1 -1
  112. data/try/{core → integration}/create_method_try.rb +1 -1
  113. data/try/integration/cross_component_try.rb +1 -1
  114. data/try/{core → integration}/database_consistency_try.rb +11 -8
  115. data/try/{core → integration}/familia_extended_try.rb +1 -1
  116. data/try/{core → integration}/familia_members_methods_try.rb +1 -1
  117. data/try/{models → integration/models}/customer_safe_dump_try.rb +1 -1
  118. data/try/{models → integration/models}/customer_try.rb +1 -1
  119. data/try/{models → integration/models}/datatype_base_try.rb +1 -1
  120. data/try/{models → integration/models}/familia_object_try.rb +1 -1
  121. data/try/{core → integration}/persistence_operations_try.rb +1 -1
  122. data/try/integration/relationships_persistence_round_trip_try.rb +441 -0
  123. data/try/{configuration → integration}/scenarios_try.rb +1 -1
  124. data/try/{core → integration}/secure_identifier_try.rb +1 -1
  125. data/try/{core → integration}/verifiable_identifier_try.rb +1 -1
  126. data/try/performance/benchmarks_try.rb +2 -2
  127. data/try/support/benchmarks/deserialization_benchmark.rb +180 -0
  128. data/try/support/benchmarks/deserialization_correctness_test.rb +237 -0
  129. data/try/{helpers → support/helpers}/test_helpers.rb +12 -3
  130. data/try/{core → unit/core}/autoloader_try.rb +1 -1
  131. data/try/{core → unit/core}/base_enhancements_try.rb +1 -9
  132. data/try/{core → unit/core}/connection_try.rb +1 -1
  133. data/try/{core → unit/core}/errors_try.rb +1 -1
  134. data/try/{core → unit/core}/extensions_try.rb +1 -1
  135. data/try/unit/core/familia_logger_try.rb +110 -0
  136. data/try/{core → unit/core}/familia_try.rb +1 -1
  137. data/try/{core → unit/core}/middleware_try.rb +41 -1
  138. data/try/{core → unit/core}/settings_try.rb +1 -1
  139. data/try/{core → unit/core}/time_utils_try.rb +1 -1
  140. data/try/{core → unit/core}/tools_try.rb +1 -1
  141. data/try/{core → unit/core}/utils_try.rb +17 -14
  142. data/try/{data_types → unit/data_types}/boolean_try.rb +1 -1
  143. data/try/{data_types → unit/data_types}/counter_try.rb +1 -1
  144. data/try/{data_types → unit/data_types}/datatype_base_try.rb +1 -1
  145. data/try/{data_types → unit/data_types}/hash_try.rb +1 -1
  146. data/try/{data_types → unit/data_types}/list_try.rb +1 -1
  147. data/try/{data_types → unit/data_types}/lock_try.rb +1 -1
  148. data/try/{data_types → unit/data_types}/sorted_set_try.rb +1 -1
  149. data/try/{data_types → unit/data_types}/sorted_set_zadd_options_try.rb +1 -1
  150. data/try/{data_types → unit/data_types}/string_try.rb +1 -1
  151. data/try/{data_types → unit/data_types}/unsortedset_try.rb +1 -1
  152. data/try/{horreum → unit/horreum}/auto_indexing_on_save_try.rb +1 -1
  153. data/try/{horreum → unit/horreum}/base_try.rb +3 -3
  154. data/try/{horreum → unit/horreum}/class_methods_try.rb +1 -1
  155. data/try/{horreum → unit/horreum}/commands_try.rb +1 -1
  156. data/try/{horreum → unit/horreum}/defensive_initialization_try.rb +1 -1
  157. data/try/{horreum → unit/horreum}/destroy_related_fields_cleanup_try.rb +1 -1
  158. data/try/{horreum → unit/horreum}/enhanced_conflict_handling_try.rb +1 -1
  159. data/try/{horreum → unit/horreum}/field_categories_try.rb +27 -18
  160. data/try/{horreum → unit/horreum}/field_definition_try.rb +1 -1
  161. data/try/{horreum → unit/horreum}/initialization_try.rb +2 -2
  162. data/try/unit/horreum/json_type_preservation_try.rb +248 -0
  163. data/try/{horreum → unit/horreum}/relations_try.rb +1 -1
  164. data/try/{horreum → unit/horreum}/serialization_persistent_fields_try.rb +24 -18
  165. data/try/{horreum → unit/horreum}/serialization_try.rb +4 -4
  166. data/try/{horreum → unit/horreum}/settings_try.rb +1 -1
  167. data/try/{refinements → unit/refinements}/dear_json_array_methods_try.rb +1 -1
  168. data/try/{refinements → unit/refinements}/dear_json_hash_methods_try.rb +1 -1
  169. data/try/{refinements → unit/refinements}/time_literals_numeric_methods_try.rb +1 -1
  170. data/try/{refinements → unit/refinements}/time_literals_string_methods_try.rb +1 -1
  171. metadata +134 -125
  172. data/lib/familia/distinguisher.rb +0 -85
  173. data/lib/familia/refinements/logger_trace.rb +0 -60
  174. data/try/refinements/logger_trace_methods_try.rb +0 -44
  175. /data/try/{debugging → support/debugging}/README.md +0 -0
  176. /data/try/{debugging → support/debugging}/cache_behavior_tracer.rb +0 -0
  177. /data/try/{debugging → support/debugging}/debug_aad_process.rb +0 -0
  178. /data/try/{debugging → support/debugging}/debug_concealed_internal.rb +0 -0
  179. /data/try/{debugging → support/debugging}/debug_concealed_reveal.rb +0 -0
  180. /data/try/{debugging → support/debugging}/debug_context_aad.rb +0 -0
  181. /data/try/{debugging → support/debugging}/debug_context_simple.rb +0 -0
  182. /data/try/{debugging → support/debugging}/debug_cross_context.rb +0 -0
  183. /data/try/{debugging → support/debugging}/debug_database_load.rb +0 -0
  184. /data/try/{debugging → support/debugging}/debug_encrypted_json_check.rb +0 -0
  185. /data/try/{debugging → support/debugging}/debug_encrypted_json_step_by_step.rb +0 -0
  186. /data/try/{debugging → support/debugging}/debug_exists_lifecycle.rb +0 -0
  187. /data/try/{debugging → support/debugging}/debug_field_decrypt.rb +0 -0
  188. /data/try/{debugging → support/debugging}/debug_fresh_cross_context.rb +0 -0
  189. /data/try/{debugging → support/debugging}/debug_load_path.rb +0 -0
  190. /data/try/{debugging → support/debugging}/debug_method_definition.rb +0 -0
  191. /data/try/{debugging → support/debugging}/debug_method_resolution.rb +0 -0
  192. /data/try/{debugging → support/debugging}/debug_minimal.rb +0 -0
  193. /data/try/{debugging → support/debugging}/debug_provider.rb +0 -0
  194. /data/try/{debugging → support/debugging}/debug_secure_behavior.rb +0 -0
  195. /data/try/{debugging → support/debugging}/debug_string_class.rb +0 -0
  196. /data/try/{debugging → support/debugging}/debug_test.rb +0 -0
  197. /data/try/{debugging → support/debugging}/debug_test_design.rb +0 -0
  198. /data/try/{debugging → support/debugging}/encryption_method_tracer.rb +0 -0
  199. /data/try/{debugging → support/debugging}/provider_diagnostics.rb +0 -0
  200. /data/try/{helpers → support/helpers}/test_cleanup.rb +0 -0
  201. /data/try/{memory → support/memory}/memory_basic_test.rb +0 -0
  202. /data/try/{memory → support/memory}/memory_detailed_test.rb +0 -0
  203. /data/try/{memory → support/memory}/memory_docker_ruby_dump.sh +0 -0
  204. /data/try/{memory → support/memory}/memory_search_for_string.rb +0 -0
  205. /data/try/{memory → support/memory}/test_actual_redactedstring_protection.rb +0 -0
  206. /data/try/{prototypes → support/prototypes}/atomic_saves_v1_context_proxy.rb +0 -0
  207. /data/try/{prototypes → support/prototypes}/atomic_saves_v2_connection_switching.rb +0 -0
  208. /data/try/{prototypes → support/prototypes}/atomic_saves_v3_connection_pool.rb +0 -0
  209. /data/try/{prototypes → support/prototypes}/atomic_saves_v4.rb +0 -0
  210. /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v2_connection_switching_helpers.rb +0 -0
  211. /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  212. /data/try/{prototypes → support/prototypes}/pooling/README.md +0 -0
  213. /data/try/{prototypes → support/prototypes}/pooling/configurable_stress_test.rb +0 -0
  214. /data/try/{prototypes → support/prototypes}/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  215. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_metrics.rb +0 -0
  216. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_stress_test.rb +0 -0
  217. /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_threading_models.rb +0 -0
  218. /data/try/{prototypes → support/prototypes}/pooling/lib/visualize_stress_results.rb +0 -0
  219. /data/try/{prototypes → support/prototypes}/pooling/pool_siege.rb +0 -0
  220. /data/try/{prototypes → support/prototypes}/pooling/run_stress_tests.rb +0 -0
@@ -1,4 +1,4 @@
1
- require_relative '../helpers/test_helpers'
1
+ require_relative '../../support/helpers/test_helpers'
2
2
 
3
3
  module RefinedContext
4
4
  using Familia::Refinements::TimeLiterals
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Test Familia::Tools - key migration and utility functions
4
4
 
5
- require_relative '../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  ## move_keys across Valkey/Redis instances (if available)
8
8
  begin
@@ -1,6 +1,6 @@
1
1
  # try/core/utils_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -62,26 +62,29 @@ custom_stamp = Familia.qstamp(3600, time: test_time)
62
62
  Time.at(custom_stamp).utc.hour
63
63
  #=> 14
64
64
 
65
- ## distinguisher handles basic types
66
- str_result = Familia.distinguisher('test')
67
- int_result = Familia.distinguisher(123)
68
- sym_result = Familia.distinguisher(:symbol)
69
- [str_result, int_result, sym_result]
70
- #=> ["test", "123", "symbol"]
65
+ ## identifier_extractor extracts class names
66
+ test_class = Class.new(Familia::Horreum)
67
+ test_class.define_singleton_method(:name) { 'TestClass' }
68
+ Familia.identifier_extractor(test_class)
69
+ #=> "TestClass"
71
70
 
72
- ## distinguisher raises error for high-risk types with strict mode
71
+ ## identifier_extractor extracts identifiers from Familia objects
72
+ customer_class = Class.new(Familia::Horreum) do
73
+ identifier_field :custid
74
+ field :custid
75
+ end
76
+ customer = customer_class.new(custid: 'customer_123')
77
+ Familia.identifier_extractor(customer)
78
+ #=> "customer_123"
79
+
80
+ ## identifier_extractor raises error for non-Familia objects
73
81
  begin
74
- Familia.distinguisher(true, strict_values: true)
82
+ Familia.identifier_extractor({ key: 'value' })
75
83
  rescue Familia::NotDistinguishableError => e
76
84
  e.class
77
85
  end
78
86
  #=> Familia::NotDistinguishableError
79
87
 
80
- ## distinguisher allows high-risk types with non-strict mode
81
- result = Familia.distinguisher(false, strict_values: false)
82
- result
83
- #=> "false"
84
-
85
88
  # Cleanup - restore defaults, leave nothing but footprints
86
89
  Familia.delim(':')
87
90
  Familia.suffix(:object)
@@ -1,6 +1,6 @@
1
1
  # try/data_types/boolean_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/counter_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new(token: 'atoken3')
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/base_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @limiter1 = Limiter.new :requests
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/hash_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new 'atoken'
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/list_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new 'atoken'
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/lock_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new(token: 'atoken4')
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/sorted_set_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new 'atoken'
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/features/data_type/sorted_set_zadd_options_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  # Test class for ZADD options testing
6
6
  class MetricsTest < Familia::Horreum
@@ -1,6 +1,6 @@
1
1
  # try/data_types/string_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new(token: 'atoken2')
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/data_types/unsortedset_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  @a = Bone.new 'atoken'
6
6
 
@@ -5,7 +5,7 @@
5
5
  # Tests automatic index population when Familia::Horreum objects are saved
6
6
  #
7
7
 
8
- require_relative '../helpers/test_helpers'
8
+ require_relative '../../support/helpers/test_helpers'
9
9
 
10
10
  # Test classes for auto-indexing functionality
11
11
  class ::AutoIndexUser < Familia::Horreum
@@ -1,6 +1,6 @@
1
1
  # try/horreum/base_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -203,11 +203,11 @@ end
203
203
  @aliased.display_size! 100
204
204
  #=> true
205
205
 
206
- ## Aliased field refresh works correctly
206
+ ## Aliased field refresh works correctly (type preserved)
207
207
  @aliased.width = 50 # unsaved change
208
208
  @aliased.refresh!
209
209
  @aliased.width
210
- #=> "100"
210
+ #=> 100
211
211
 
212
212
  ## Fast method with custom name
213
213
  class CustomFastMethodTest < Familia::Horreum
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Test Horreum class methods
4
4
 
5
- require_relative '../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  TestUser = Class.new(Familia::Horreum) do
8
8
  identifier_field :email
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Test Horreum Valkey/Redis commands
4
4
 
5
- require_relative '../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  ## hget/hset operations
8
8
  begin
@@ -1,6 +1,6 @@
1
1
  # try/horreum/defensive_initialization_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  # Test defensive initialization behavior
6
6
  class User < Familia::Horreum
@@ -9,7 +9,7 @@
9
9
  # This addresses the bug where destroy! only deleted the main object key
10
10
  # but left related field keys in the database.
11
11
 
12
- require_relative '../helpers/test_helpers'
12
+ require_relative '../../support/helpers/test_helpers'
13
13
 
14
14
  MANY_FIELD_MULTIPLIER = 10
15
15
 
@@ -1,6 +1,6 @@
1
1
  # try/horreum/enhanced_conflict_handling_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,44 +1,53 @@
1
1
  # try/horreum/field_categories_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
7
- # Define test class with various field categories
7
+ # Define test class with various field types
8
8
  class FieldCategoryTest < Familia::Horreum
9
+ feature :encrypted_fields
10
+ feature :transient_fields
11
+
9
12
  identifier_field :id
10
13
  field :id
11
- field :name # default category (:field)
12
- field :email, category: :encrypted # encrypted category
13
- field :tryouts_cache_data, category: :transient # transient category
14
- field :description, category: :persistent # explicit persistent category
15
- field :settings, category: nil # nil category (defaults to :field)
14
+ field :name # regular field
15
+ encrypted_field :email # encrypted field
16
+ transient_field :tryouts_cache_data # transient field
17
+ field :description # regular persistent field
18
+ field :settings # regular field
16
19
  end
17
20
 
18
21
  # Test class with multiple transient fields
19
22
  class MultiTransientTest < Familia::Horreum
23
+ feature :transient_fields
24
+
20
25
  identifier_field :id
21
26
  field :id
22
27
  field :permanent_data
23
- field :temp1, category: :transient
24
- field :temp2, category: :transient
25
- field :temp3, category: :transient
28
+ transient_field :temp1
29
+ transient_field :temp2
30
+ transient_field :temp3
26
31
  end
27
32
 
28
- # Field categories work with field aliasing
33
+ # Field types work with field aliasing
29
34
  class AliasedCategoryTest < Familia::Horreum
35
+ feature :transient_fields
36
+
30
37
  identifier_field :id
31
38
  field :id
32
- field :internal_temp, as: :temp, category: :transient
33
- field :internal_perm, as: :perm, category: :persistent
39
+ transient_field :internal_temp, as: :temp
40
+ field :internal_perm, as: :perm
34
41
  end
35
42
 
36
43
  # Test edge case with all transient fields
37
44
  class AllTransientTest < Familia::Horreum
45
+ feature :transient_fields
46
+
38
47
  identifier_field :id
39
48
  field :id
40
- field :temp1, category: :transient
41
- field :temp2, category: :transient
49
+ transient_field :temp1
50
+ transient_field :temp2
42
51
  end
43
52
 
44
53
  ## Field types are stored correctly
@@ -58,11 +67,11 @@ FieldCategoryTest.field_types[:email].category
58
67
  FieldCategoryTest.field_types[:tryouts_cache_data].category
59
68
  #=> :transient
60
69
 
61
- ## Explicit persistent category field has correct category
70
+ ## Regular fields have :field category
62
71
  FieldCategoryTest.field_types[:description].category
63
- #=> :persistent
72
+ #=> :field
64
73
 
65
- ## Nil category field defaults to :field
74
+ ## Regular fields default to :field category
66
75
  FieldCategoryTest.field_types[:settings].category
67
76
  #=> :field
68
77
 
@@ -1,6 +1,6 @@
1
1
  # try/horreum/field_definition_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -1,6 +1,6 @@
1
1
  # try/horreum/initialization_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  Familia.debug = false
6
6
 
@@ -97,7 +97,7 @@ Familia.debug = false
97
97
  @complex.save
98
98
  @complex.refresh!
99
99
  [@complex.custid, @complex.name, @complex.role, @complex.verified]
100
- #=> ["complex@test.com", "Complex User", "admin", "true"]
100
+ #=> ["complex@test.com", "Complex User", "admin", true]
101
101
 
102
102
  ## Clean up saved test objects
103
103
  [@customer6, @complex].map(&:delete!)
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../support/helpers/test_helpers'
4
+
5
+ Familia.debug = false
6
+
7
+ # Setup: Define test model with various field types
8
+ class JsonTestModel < Familia::Horreum
9
+ identifier_field :model_id
10
+ field :model_id
11
+ field :name # String
12
+ field :age # Integer
13
+ field :active # Boolean
14
+ field :score # Float
15
+ field :metadata # Hash
16
+ field :tags # Array
17
+ field :nested_data # Hash with nested structures
18
+ field :badge_number # String that looks numeric
19
+ field :status # String that looks boolean
20
+ field :placeholder # String that looks like null
21
+ end
22
+
23
+ @test_id = "json_test_#{Familia.now.to_i}"
24
+
25
+ ## String fields preserve String type
26
+ @model = JsonTestModel.new(model_id: @test_id, name: "Test User")
27
+ @model.save
28
+ @loaded = JsonTestModel.find(@test_id)
29
+ @loaded.name
30
+ #=> "Test User"
31
+
32
+ ## String field returns String class
33
+ @loaded.name.class
34
+ #=> String
35
+
36
+ ## Integer fields preserve Integer type (not String)
37
+ @model = JsonTestModel.new(model_id: @test_id, age: 35)
38
+ @model.save
39
+ @loaded = JsonTestModel.find(@test_id)
40
+ @loaded.age
41
+ #=> 35
42
+
43
+ ## Integer field returns Integer class (not String)
44
+ @loaded.age.class
45
+ #=> Integer
46
+
47
+ ## Boolean true preserves TrueClass type (not String "true")
48
+ @model = JsonTestModel.new(model_id: @test_id, active: true)
49
+ @model.save
50
+ @loaded = JsonTestModel.find(@test_id)
51
+ @loaded.active
52
+ #=> true
53
+
54
+ ## Boolean true returns TrueClass
55
+ @loaded.active.class
56
+ #=> TrueClass
57
+
58
+ ## Boolean false preserves FalseClass type (not String "false")
59
+ @model = JsonTestModel.new(model_id: @test_id, active: false)
60
+ @model.save
61
+ @loaded = JsonTestModel.find(@test_id)
62
+ @loaded.active
63
+ #=> false
64
+
65
+ ## Boolean false returns FalseClass
66
+ @loaded.active.class
67
+ #=> FalseClass
68
+
69
+ ## Float fields preserve Float type
70
+ @model = JsonTestModel.new(model_id: @test_id, score: 98.6)
71
+ @model.save
72
+ @loaded = JsonTestModel.find(@test_id)
73
+ @loaded.score
74
+ #=> 98.6
75
+
76
+ ## Float field returns Float class
77
+ @loaded.score.class
78
+ #=> Float
79
+
80
+ ## Hash fields preserve Hash structure
81
+ @model = JsonTestModel.new(model_id: @test_id, metadata: { "key" => "value", "count" => 42 })
82
+ @model.save
83
+ @loaded = JsonTestModel.find(@test_id)
84
+ @loaded.metadata
85
+ #=> {"key"=>"value", "count"=>42}
86
+
87
+ ## Hash field returns Hash class
88
+ @loaded.metadata.class
89
+ #=> Hash
90
+
91
+ ## Array fields preserve Array structure and element types
92
+ @model = JsonTestModel.new(model_id: @test_id, tags: ["ruby", "redis", 123, true])
93
+ @model.save
94
+ @loaded = JsonTestModel.find(@test_id)
95
+ @loaded.tags
96
+ #=> ["ruby", "redis", 123, true]
97
+
98
+ ## Array field returns Array class
99
+ @loaded.tags.class
100
+ #=> Array
101
+
102
+ ## Array preserves Integer element type
103
+ @loaded.tags[2].class
104
+ #=> Integer
105
+
106
+ ## Array preserves Boolean element type
107
+ @loaded.tags[3].class
108
+ #=> TrueClass
109
+
110
+ ## Nested Hash structures preserve types at all levels
111
+ @nested = {
112
+ "user" => {
113
+ "name" => "John",
114
+ "age" => 30,
115
+ "active" => true,
116
+ "roles" => ["admin", "user"]
117
+ },
118
+ "count" => 5
119
+ }
120
+ @model = JsonTestModel.new(model_id: @test_id, nested_data: @nested)
121
+ @model.save
122
+ @loaded = JsonTestModel.find(@test_id)
123
+ @loaded.nested_data["user"]["age"].class
124
+ #=> Integer
125
+
126
+ ## Nested Hash Boolean preserved
127
+ @loaded.nested_data["user"]["active"].class
128
+ #=> TrueClass
129
+
130
+ ## Nested Hash top-level Integer preserved
131
+ @loaded.nested_data["count"].class
132
+ #=> Integer
133
+ ## Round-trip consistency: save → load → save → load maintains types
134
+ @model = JsonTestModel.new(
135
+ model_id: @test_id,
136
+ name: "Round Trip",
137
+ age: 42,
138
+ active: true,
139
+ score: 99.9,
140
+ tags: [1, 2, 3]
141
+ )
142
+ @model.save
143
+ @first_load = JsonTestModel.find(@test_id)
144
+ @first_load.save # Re-save without modification
145
+ @second_load = JsonTestModel.find(@test_id)
146
+ [@second_load.age.class, @second_load.active.class, @second_load.tags[0].class]
147
+ #=> [Integer, TrueClass, Integer]
148
+
149
+ ## batch_update preserves types
150
+ @model = JsonTestModel.new(model_id: @test_id)
151
+ @model.batch_update(name: "Batch", age: 50, active: false, tags: ["one", 2])
152
+ @loaded = JsonTestModel.find(@test_id)
153
+ [@loaded.name, @loaded.age, @loaded.active, @loaded.tags[1]]
154
+ #=> ["Batch", 50, false, 2]
155
+
156
+ ## batch_update types verification
157
+ [@loaded.age.class, @loaded.active.class, @loaded.tags[1].class]
158
+ #=> [Integer, FalseClass, Integer]
159
+
160
+ ## refresh! maintains type preservation
161
+ @model = JsonTestModel.new(model_id: @test_id, age: 25, active: true)
162
+ @model.save
163
+ # Modify directly in Redis to simulate external change
164
+ @model.dbclient.hset(@model.dbkey, "age", Familia::JsonSerializer.dump(30))
165
+ @model.refresh!
166
+ [@model.age, @model.age.class]
167
+ #=> [30, Integer]
168
+
169
+ ## to_h returns string keys (external API compatibility)
170
+ @model = JsonTestModel.new(model_id: @test_id, name: "API Test", age: 40)
171
+ @hash = @model.to_h
172
+ @hash.keys.first.class
173
+ #=> String
174
+
175
+ ## to_h has string key for name field
176
+ @hash.key?("name")
177
+ #=> true
178
+
179
+ ## Nil values are handled correctly
180
+ @model = JsonTestModel.new(model_id: @test_id, name: "Nil Test", age: nil, active: nil)
181
+ @model.save
182
+ @loaded = JsonTestModel.find(@test_id)
183
+ [@loaded.age, @loaded.active]
184
+ #=> [nil, nil]
185
+
186
+ ## Empty string preserved correctly
187
+ @model = JsonTestModel.new(model_id: @test_id, name: "")
188
+ @model.save
189
+ @loaded = JsonTestModel.find(@test_id)
190
+ @loaded.name
191
+ #=> ""
192
+
193
+ ## Empty string has String class
194
+ @loaded.name.class
195
+ #=> String
196
+
197
+ ## String fields with numeric values preserve String type (badge "007")
198
+ @model = JsonTestModel.new(model_id: @test_id, badge_number: "007")
199
+ @model.save
200
+ @loaded = JsonTestModel.find(@test_id)
201
+ @loaded.badge_number
202
+ #=> "007"
203
+
204
+ ## Badge number preserves String class (not Integer)
205
+ @loaded.badge_number.class
206
+ #=> String
207
+
208
+ ## String "true" stays String (not Boolean)
209
+ @model = JsonTestModel.new(model_id: @test_id, status: "true")
210
+ @model.save
211
+ @loaded = JsonTestModel.find(@test_id)
212
+ [@loaded.status, @loaded.status.class]
213
+ #=> ["true", String]
214
+
215
+ ## String "false" stays String (not Boolean)
216
+ @model = JsonTestModel.new(model_id: @test_id, status: "false")
217
+ @model.save
218
+ @loaded = JsonTestModel.find(@test_id)
219
+ [@loaded.status, @loaded.status.class]
220
+ #=> ["false", String]
221
+
222
+ ## String "null" stays String (not nil)
223
+ @model = JsonTestModel.new(model_id: @test_id, placeholder: "null")
224
+ @model.save
225
+ @loaded = JsonTestModel.find(@test_id)
226
+ [@loaded.placeholder, @loaded.placeholder.class]
227
+ #=> ["null", String]
228
+
229
+ ## Numeric string "123" preserves String type
230
+ @model = JsonTestModel.new(model_id: @test_id, badge_number: "123")
231
+ @model.save
232
+ @loaded = JsonTestModel.find(@test_id)
233
+ [@loaded.badge_number, @loaded.badge_number.class]
234
+ #=> ["123", String]
235
+
236
+ ## Zero values preserved with correct types
237
+ @model = JsonTestModel.new(model_id: @test_id, age: 0, score: 0.0)
238
+ @model.save
239
+ @loaded = JsonTestModel.find(@test_id)
240
+ [@loaded.age, @loaded.score]
241
+ #=> [0, 0.0]
242
+
243
+ ## Zero Integer and Float have correct classes
244
+ [@loaded.age.class, @loaded.score.class]
245
+ #=> [Integer, Float]
246
+
247
+ # Teardown: Clean up test data
248
+ JsonTestModel.destroy!(@test_id) if JsonTestModel.exists?(@test_id)
@@ -1,7 +1,7 @@
1
1
  # try/horreum/relations_try.rb
2
2
  # Test Horreum Database type relations functionality
3
3
 
4
- require_relative '../helpers/test_helpers'
4
+ require_relative '../../support/helpers/test_helpers'
5
5
 
6
6
  Familia.debug = false
7
7