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,36 +1,42 @@
1
1
  # try/horreum/serialization_persistent_fields_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
- # Test class with mixed field categories for serialization
7
+ # Test class with mixed field types for serialization
8
8
  class SerializationCategoryTest < Familia::Horreum
9
+ feature :transient_fields
10
+
9
11
  identifier_field :id
10
12
  field :id
11
13
  field :name # persistent by default
12
- field :email, category: :encrypted # persistent, encrypted category
13
- field :tryouts_cache_data, category: :transient # should be excluded from serialization
14
- field :description, category: :persistent # explicitly persistent
15
- field :temp_settings, category: :transient # should be excluded
16
- field :metadata, category: :persistent # explicitly persistent
14
+ field :email # persistent field
15
+ transient_field :tryouts_cache_data # should be excluded from serialization
16
+ field :description # explicitly persistent
17
+ transient_field :temp_settings # should be excluded
18
+ field :metadata # explicitly persistent
17
19
  end
18
20
 
19
21
  # Class with all transient fields
20
22
  class AllTransientSerializationTest < Familia::Horreum
23
+ feature :transient_fields
24
+
21
25
  identifier_field :id
22
26
  field :id
23
- field :temp1, category: :transient
24
- field :temp2, category: :transient
27
+ transient_field :temp1
28
+ transient_field :temp2
25
29
  end
26
30
 
27
- # Mixed categories with aliased fields
31
+ # Mixed field types with aliased fields
28
32
  class AliasedSerializationTest < Familia::Horreum
33
+ feature :transient_fields
34
+
29
35
  identifier_field :id
30
36
  field :id
31
- field :internal_name, as: :display_name, category: :persistent
32
- field :temp_cache, as: :cache, category: :transient
33
- field :user_data, as: :data, category: :encrypted
37
+ field :internal_name, as: :display_name
38
+ transient_field :temp_cache, as: :cache
39
+ field :user_data, as: :data
34
40
  end
35
41
 
36
42
  # Setup test instance with all field types
@@ -66,7 +72,7 @@ end
66
72
  @hash_result.key?("name")
67
73
  #=> true
68
74
 
69
- ## to_h includes encrypted persistent fields
75
+ ## to_h includes regular persistent fields
70
76
  @hash_result.key?("email")
71
77
  #=> true
72
78
 
@@ -82,9 +88,9 @@ end
82
88
  @hash_result.key?(:temp_settings)
83
89
  #=> false
84
90
 
85
- ## to_h serializes complex values correctly
86
- @hash_result["metadata"]
87
- #=:> String
91
+ ## to_h returns actual Ruby values (Hash, not serialized JSON string)
92
+ @hash_result["metadata"].class
93
+ #=> Hash
88
94
 
89
95
  ## to_a excludes transient fields
90
96
  @array_result = @serialization_test.to_a
@@ -133,7 +139,7 @@ SerializationCategoryTest.persistent_fields.include?(:email)
133
139
  @all_transient.to_a
134
140
  #=> ["transient_test_1"]
135
141
 
136
- ## Aliased fields serialization uses original field names
142
+ ## Aliased fields serialization uses original field names (transient excluded)
137
143
  @aliased_hash = @aliased_test.to_h
138
144
  @aliased_hash.keys.sort
139
145
  #=> ["id", "internal_name", "user_data"]
@@ -1,6 +1,6 @@
1
1
  # try/horreum/serialization_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
 
@@ -103,9 +103,9 @@ Familia.dbclient.set('debug:ending_save_if_not_exists_tests', Familia.now.to_s)
103
103
  [@customer.name, @customer.email]
104
104
  #=> ["Bob Jones", "jane@example.com"]
105
105
 
106
- ## serialize_value handles strings
106
+ ## serialize_value handles strings with JSON encoding
107
107
  @customer.serialize_value('test string')
108
- #=> "test string"
108
+ #=> "\"test string\""
109
109
 
110
110
  ## serialize_value handles numbers
111
111
  @customer.serialize_value(42)
@@ -121,7 +121,7 @@ Familia.dbclient.set('debug:ending_save_if_not_exists_tests', Familia.now.to_s)
121
121
 
122
122
  ## deserialize_value handles JSON strings back to objects
123
123
  @customer.deserialize_value('{"key":"value","num":123}')
124
- #=> {:key=>"value", :num=>123}
124
+ #=> {"key"=>"value", "num"=>123}
125
125
 
126
126
  ## deserialize_value handles JSON arrays
127
127
  @customer.deserialize_value('[1,2,"three"]')
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Test Horreum settings
4
4
 
5
- require_relative '../helpers/test_helpers'
5
+ require_relative '../../support/helpers/test_helpers'
6
6
 
7
7
  ## database selection inheritance
8
8
  user_class = Class.new(Familia::Horreum) do
@@ -1,6 +1,6 @@
1
1
  # try/refinements/dear_json_array_methods_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  class TestArrayWithDearJson < Array
6
6
  include Familia::Refinements::DearJsonArrayMethods
@@ -1,6 +1,6 @@
1
1
  # try/refinements/dear_json_hash_methods_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  class TestHashWithDearJson < Hash
6
6
  include Familia::Refinements::DearJsonHashMethods
@@ -1,6 +1,6 @@
1
1
  # try/refinements/time_literals_numeric_methods_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  class TestNumericWithTimeLiterals
6
6
  include Familia::Refinements::TimeLiterals::NumericMethods
@@ -1,6 +1,6 @@
1
1
  # try/refinements/time_literals_string_methods_try.rb
2
2
 
3
- require_relative '../helpers/test_helpers'
3
+ require_relative '../../support/helpers/test_helpers'
4
4
 
5
5
  class TestStringWithTimeLiterals < String
6
6
  include Familia::Refinements::TimeLiterals::StringMethods
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: familia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre17
4
+ version: 2.0.0.pre18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -184,10 +184,12 @@ files:
184
184
  - docs/migrating/v2.0.0-pre12.md
185
185
  - docs/migrating/v2.0.0-pre13.md
186
186
  - docs/migrating/v2.0.0-pre14.md
187
+ - docs/migrating/v2.0.0-pre18.md
187
188
  - docs/migrating/v2.0.0-pre5.md
188
189
  - docs/migrating/v2.0.0-pre6.md
189
190
  - docs/migrating/v2.0.0-pre7.md
190
191
  - docs/overview.md
192
+ - docs/qodo-merge-compliance.md
191
193
  - docs/reference/api-technical.md
192
194
  - examples/autoloader/mega_customer.rb
193
195
  - examples/autoloader/mega_customer/features/deprecated_fields.rb
@@ -210,8 +212,8 @@ files:
210
212
  - lib/familia/connection/transaction_core.rb
211
213
  - lib/familia/data_type.rb
212
214
  - lib/familia/data_type/class_methods.rb
213
- - lib/familia/data_type/commands.rb
214
215
  - lib/familia/data_type/connection.rb
216
+ - lib/familia/data_type/database_commands.rb
215
217
  - lib/familia/data_type/serialization.rb
216
218
  - lib/familia/data_type/settings.rb
217
219
  - lib/familia/data_type/types/counter.rb
@@ -221,7 +223,6 @@ files:
221
223
  - lib/familia/data_type/types/sorted_set.rb
222
224
  - lib/familia/data_type/types/stringkey.rb
223
225
  - lib/familia/data_type/types/unsorted_set.rb
224
- - lib/familia/distinguisher.rb
225
226
  - lib/familia/encryption.rb
226
227
  - lib/familia/encryption/encrypted_data.rb
227
228
  - lib/familia/encryption/manager.rb
@@ -270,11 +271,11 @@ files:
270
271
  - lib/familia/horreum/serialization.rb
271
272
  - lib/familia/horreum/settings.rb
272
273
  - lib/familia/horreum/utils.rb
274
+ - lib/familia/identifier_extractor.rb
273
275
  - lib/familia/json_serializer.rb
274
276
  - lib/familia/logging.rb
275
277
  - lib/familia/refinements.rb
276
278
  - lib/familia/refinements/dear_json.rb
277
- - lib/familia/refinements/logger_trace.rb
278
279
  - lib/familia/refinements/stylize_words.rb
279
280
  - lib/familia/refinements/time_literals.rb
280
281
  - lib/familia/secure_identifier.rb
@@ -282,90 +283,18 @@ files:
282
283
  - lib/familia/utils.rb
283
284
  - lib/familia/verifiable_identifier.rb
284
285
  - lib/familia/version.rb
285
- - lib/middleware/database_middleware.rb
286
+ - lib/middleware/database_logger.rb
286
287
  - lib/multi_result.rb
287
- - try/configuration/scenarios_try.rb
288
- - try/connection/fiber_context_preservation_try.rb
289
- - try/connection/handler_constraints_try.rb
290
- - try/connection/operation_mode_guards_try.rb
291
- - try/connection/pipeline_fallback_integration_try.rb
292
- - try/connection/responsibility_chain_tracking_try.rb
293
- - try/connection/transaction_fallback_integration_try.rb
294
- - try/connection/transaction_mode_permissive_try.rb
295
- - try/connection/transaction_mode_strict_try.rb
296
- - try/connection/transaction_mode_warn_try.rb
297
- - try/connection/transaction_modes_try.rb
298
- - try/core/autoloader_try.rb
299
- - try/core/base_enhancements_try.rb
300
- - try/core/connection_try.rb
301
- - try/core/conventional_inheritance_try.rb
302
- - try/core/create_method_try.rb
303
- - try/core/database_consistency_try.rb
304
- - try/core/errors_try.rb
305
- - try/core/extensions_try.rb
306
- - try/core/familia_extended_try.rb
307
- - try/core/familia_members_methods_try.rb
308
- - try/core/familia_try.rb
309
- - try/core/isolated_dbclient_try.rb
310
- - try/core/middleware_try.rb
311
- - try/core/persistence_operations_try.rb
312
- - try/core/pools_try.rb
313
- - try/core/secure_identifier_try.rb
314
- - try/core/settings_try.rb
315
- - try/core/time_utils_try.rb
316
- - try/core/tools_try.rb
317
- - try/core/utils_try.rb
318
- - try/core/verifiable_identifier_try.rb
319
- - try/data_types/boolean_try.rb
320
- - try/data_types/counter_try.rb
321
- - try/data_types/datatype_base_try.rb
322
- - try/data_types/hash_try.rb
323
- - try/data_types/list_try.rb
324
- - try/data_types/lock_try.rb
325
- - try/data_types/sorted_set_try.rb
326
- - try/data_types/sorted_set_zadd_options_try.rb
327
- - try/data_types/string_try.rb
328
- - try/data_types/unsortedset_try.rb
329
- - try/debugging/README.md
330
- - try/debugging/cache_behavior_tracer.rb
331
- - try/debugging/debug_aad_process.rb
332
- - try/debugging/debug_concealed_internal.rb
333
- - try/debugging/debug_concealed_reveal.rb
334
- - try/debugging/debug_context_aad.rb
335
- - try/debugging/debug_context_simple.rb
336
- - try/debugging/debug_cross_context.rb
337
- - try/debugging/debug_database_load.rb
338
- - try/debugging/debug_encrypted_json_check.rb
339
- - try/debugging/debug_encrypted_json_step_by_step.rb
340
- - try/debugging/debug_exists_lifecycle.rb
341
- - try/debugging/debug_field_decrypt.rb
342
- - try/debugging/debug_fresh_cross_context.rb
343
- - try/debugging/debug_load_path.rb
344
- - try/debugging/debug_method_definition.rb
345
- - try/debugging/debug_method_resolution.rb
346
- - try/debugging/debug_minimal.rb
347
- - try/debugging/debug_provider.rb
348
- - try/debugging/debug_secure_behavior.rb
349
- - try/debugging/debug_string_class.rb
350
- - try/debugging/debug_test.rb
351
- - try/debugging/debug_test_design.rb
352
- - try/debugging/encryption_method_tracer.rb
353
- - try/debugging/provider_diagnostics.rb
288
+ - pr_agent.toml
289
+ - pr_compliance_checklist.yaml
354
290
  - try/edge_cases/empty_identifiers_try.rb
355
291
  - try/edge_cases/hash_symbolization_try.rb
356
292
  - try/edge_cases/json_serialization_try.rb
293
+ - try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb
357
294
  - try/edge_cases/race_conditions_try.rb
358
295
  - try/edge_cases/reserved_keywords_try.rb
359
296
  - try/edge_cases/string_coercion_try.rb
360
297
  - try/edge_cases/ttl_side_effects_try.rb
361
- - try/encryption/config_persistence_try.rb
362
- - try/encryption/encryption_core_try.rb
363
- - try/encryption/instance_variable_scope_try.rb
364
- - try/encryption/module_loading_try.rb
365
- - try/encryption/providers/aes_gcm_provider_try.rb
366
- - try/encryption/providers/xchacha20_poly1305_provider_try.rb
367
- - try/encryption/roundtrip_validation_try.rb
368
- - try/encryption/secure_memory_handling_try.rb
369
298
  - try/features/encrypted_fields/aad_protection_try.rb
370
299
  - try/features/encrypted_fields/concealed_string_core_try.rb
371
300
  - try/features/encrypted_fields/context_isolation_try.rb
@@ -383,6 +312,14 @@ files:
383
312
  - try/features/encrypted_fields/secure_by_default_behavior_try.rb
384
313
  - try/features/encrypted_fields/thread_safety_try.rb
385
314
  - try/features/encrypted_fields/universal_serialization_safety_try.rb
315
+ - try/features/encryption/config_persistence_try.rb
316
+ - try/features/encryption/core_try.rb
317
+ - try/features/encryption/instance_variable_scope_try.rb
318
+ - try/features/encryption/module_loading_try.rb
319
+ - try/features/encryption/providers/aes_gcm_provider_try.rb
320
+ - try/features/encryption/providers/xchacha20_poly1305_provider_try.rb
321
+ - try/features/encryption/roundtrip_validation_try.rb
322
+ - try/features/encryption/secure_memory_handling_try.rb
386
323
  - try/features/expiration/expiration_try.rb
387
324
  - try/features/external_identifier/external_identifier_try.rb
388
325
  - try/features/feature_dependencies_try.rb
@@ -413,53 +350,125 @@ files:
413
350
  - try/features/transient_fields/single_use_redacted_string_try.rb
414
351
  - try/features/transient_fields/transient_fields_core_try.rb
415
352
  - try/features/transient_fields/transient_fields_integration_try.rb
416
- - try/helpers/test_cleanup.rb
417
- - try/helpers/test_helpers.rb
418
- - try/horreum/auto_indexing_on_save_try.rb
419
- - try/horreum/base_try.rb
420
- - try/horreum/class_methods_try.rb
421
- - try/horreum/commands_try.rb
422
- - try/horreum/defensive_initialization_try.rb
423
- - try/horreum/destroy_related_fields_cleanup_try.rb
424
- - try/horreum/enhanced_conflict_handling_try.rb
425
- - try/horreum/field_categories_try.rb
426
- - try/horreum/field_definition_try.rb
427
- - try/horreum/initialization_try.rb
428
- - try/horreum/relations_try.rb
429
- - try/horreum/serialization_persistent_fields_try.rb
430
- - try/horreum/serialization_try.rb
431
- - try/horreum/settings_try.rb
353
+ - try/integration/connection/fiber_context_preservation_try.rb
354
+ - try/integration/connection/handler_constraints_try.rb
355
+ - try/integration/connection/isolated_dbclient_try.rb
356
+ - try/integration/connection/middleware_reconnect_try.rb
357
+ - try/integration/connection/operation_mode_guards_try.rb
358
+ - try/integration/connection/pipeline_fallback_integration_try.rb
359
+ - try/integration/connection/pools_try.rb
360
+ - try/integration/connection/responsibility_chain_tracking_try.rb
361
+ - try/integration/connection/transaction_fallback_integration_try.rb
362
+ - try/integration/connection/transaction_mode_permissive_try.rb
363
+ - try/integration/connection/transaction_mode_strict_try.rb
364
+ - try/integration/connection/transaction_mode_warn_try.rb
365
+ - try/integration/connection/transaction_modes_try.rb
366
+ - try/integration/conventional_inheritance_try.rb
367
+ - try/integration/create_method_try.rb
432
368
  - try/integration/cross_component_try.rb
433
- - try/memory/memory_basic_test.rb
434
- - try/memory/memory_detailed_test.rb
435
- - try/memory/memory_docker_ruby_dump.sh
436
- - try/memory/memory_search_for_string.rb
437
- - try/memory/test_actual_redactedstring_protection.rb
438
- - try/models/customer_safe_dump_try.rb
439
- - try/models/customer_try.rb
440
- - try/models/datatype_base_try.rb
441
- - try/models/familia_object_try.rb
369
+ - try/integration/database_consistency_try.rb
370
+ - try/integration/familia_extended_try.rb
371
+ - try/integration/familia_members_methods_try.rb
372
+ - try/integration/models/customer_safe_dump_try.rb
373
+ - try/integration/models/customer_try.rb
374
+ - try/integration/models/datatype_base_try.rb
375
+ - try/integration/models/familia_object_try.rb
376
+ - try/integration/persistence_operations_try.rb
377
+ - try/integration/relationships_persistence_round_trip_try.rb
378
+ - try/integration/scenarios_try.rb
379
+ - try/integration/secure_identifier_try.rb
380
+ - try/integration/verifiable_identifier_try.rb
442
381
  - try/performance/benchmarks_try.rb
443
- - try/prototypes/atomic_saves_v1_context_proxy.rb
444
- - try/prototypes/atomic_saves_v2_connection_switching.rb
445
- - try/prototypes/atomic_saves_v3_connection_pool.rb
446
- - try/prototypes/atomic_saves_v4.rb
447
- - try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb
448
- - try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb
449
- - try/prototypes/pooling/README.md
450
- - try/prototypes/pooling/configurable_stress_test.rb
451
- - try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb
452
- - try/prototypes/pooling/lib/connection_pool_metrics.rb
453
- - try/prototypes/pooling/lib/connection_pool_stress_test.rb
454
- - try/prototypes/pooling/lib/connection_pool_threading_models.rb
455
- - try/prototypes/pooling/lib/visualize_stress_results.rb
456
- - try/prototypes/pooling/pool_siege.rb
457
- - try/prototypes/pooling/run_stress_tests.rb
458
- - try/refinements/dear_json_array_methods_try.rb
459
- - try/refinements/dear_json_hash_methods_try.rb
460
- - try/refinements/logger_trace_methods_try.rb
461
- - try/refinements/time_literals_numeric_methods_try.rb
462
- - try/refinements/time_literals_string_methods_try.rb
382
+ - try/support/benchmarks/deserialization_benchmark.rb
383
+ - try/support/benchmarks/deserialization_correctness_test.rb
384
+ - try/support/debugging/README.md
385
+ - try/support/debugging/cache_behavior_tracer.rb
386
+ - try/support/debugging/debug_aad_process.rb
387
+ - try/support/debugging/debug_concealed_internal.rb
388
+ - try/support/debugging/debug_concealed_reveal.rb
389
+ - try/support/debugging/debug_context_aad.rb
390
+ - try/support/debugging/debug_context_simple.rb
391
+ - try/support/debugging/debug_cross_context.rb
392
+ - try/support/debugging/debug_database_load.rb
393
+ - try/support/debugging/debug_encrypted_json_check.rb
394
+ - try/support/debugging/debug_encrypted_json_step_by_step.rb
395
+ - try/support/debugging/debug_exists_lifecycle.rb
396
+ - try/support/debugging/debug_field_decrypt.rb
397
+ - try/support/debugging/debug_fresh_cross_context.rb
398
+ - try/support/debugging/debug_load_path.rb
399
+ - try/support/debugging/debug_method_definition.rb
400
+ - try/support/debugging/debug_method_resolution.rb
401
+ - try/support/debugging/debug_minimal.rb
402
+ - try/support/debugging/debug_provider.rb
403
+ - try/support/debugging/debug_secure_behavior.rb
404
+ - try/support/debugging/debug_string_class.rb
405
+ - try/support/debugging/debug_test.rb
406
+ - try/support/debugging/debug_test_design.rb
407
+ - try/support/debugging/encryption_method_tracer.rb
408
+ - try/support/debugging/provider_diagnostics.rb
409
+ - try/support/helpers/test_cleanup.rb
410
+ - try/support/helpers/test_helpers.rb
411
+ - try/support/memory/memory_basic_test.rb
412
+ - try/support/memory/memory_detailed_test.rb
413
+ - try/support/memory/memory_docker_ruby_dump.sh
414
+ - try/support/memory/memory_search_for_string.rb
415
+ - try/support/memory/test_actual_redactedstring_protection.rb
416
+ - try/support/prototypes/atomic_saves_v1_context_proxy.rb
417
+ - try/support/prototypes/atomic_saves_v2_connection_switching.rb
418
+ - try/support/prototypes/atomic_saves_v3_connection_pool.rb
419
+ - try/support/prototypes/atomic_saves_v4.rb
420
+ - try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb
421
+ - try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb
422
+ - try/support/prototypes/pooling/README.md
423
+ - try/support/prototypes/pooling/configurable_stress_test.rb
424
+ - try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb
425
+ - try/support/prototypes/pooling/lib/connection_pool_metrics.rb
426
+ - try/support/prototypes/pooling/lib/connection_pool_stress_test.rb
427
+ - try/support/prototypes/pooling/lib/connection_pool_threading_models.rb
428
+ - try/support/prototypes/pooling/lib/visualize_stress_results.rb
429
+ - try/support/prototypes/pooling/pool_siege.rb
430
+ - try/support/prototypes/pooling/run_stress_tests.rb
431
+ - try/unit/core/autoloader_try.rb
432
+ - try/unit/core/base_enhancements_try.rb
433
+ - try/unit/core/connection_try.rb
434
+ - try/unit/core/errors_try.rb
435
+ - try/unit/core/extensions_try.rb
436
+ - try/unit/core/familia_logger_try.rb
437
+ - try/unit/core/familia_try.rb
438
+ - try/unit/core/middleware_try.rb
439
+ - try/unit/core/settings_try.rb
440
+ - try/unit/core/time_utils_try.rb
441
+ - try/unit/core/tools_try.rb
442
+ - try/unit/core/utils_try.rb
443
+ - try/unit/data_types/boolean_try.rb
444
+ - try/unit/data_types/counter_try.rb
445
+ - try/unit/data_types/datatype_base_try.rb
446
+ - try/unit/data_types/hash_try.rb
447
+ - try/unit/data_types/list_try.rb
448
+ - try/unit/data_types/lock_try.rb
449
+ - try/unit/data_types/sorted_set_try.rb
450
+ - try/unit/data_types/sorted_set_zadd_options_try.rb
451
+ - try/unit/data_types/string_try.rb
452
+ - try/unit/data_types/unsortedset_try.rb
453
+ - try/unit/horreum/auto_indexing_on_save_try.rb
454
+ - try/unit/horreum/base_try.rb
455
+ - try/unit/horreum/class_methods_try.rb
456
+ - try/unit/horreum/commands_try.rb
457
+ - try/unit/horreum/defensive_initialization_try.rb
458
+ - try/unit/horreum/destroy_related_fields_cleanup_try.rb
459
+ - try/unit/horreum/enhanced_conflict_handling_try.rb
460
+ - try/unit/horreum/field_categories_try.rb
461
+ - try/unit/horreum/field_definition_try.rb
462
+ - try/unit/horreum/initialization_try.rb
463
+ - try/unit/horreum/json_type_preservation_try.rb
464
+ - try/unit/horreum/relations_try.rb
465
+ - try/unit/horreum/serialization_persistent_fields_try.rb
466
+ - try/unit/horreum/serialization_try.rb
467
+ - try/unit/horreum/settings_try.rb
468
+ - try/unit/refinements/dear_json_array_methods_try.rb
469
+ - try/unit/refinements/dear_json_hash_methods_try.rb
470
+ - try/unit/refinements/time_literals_numeric_methods_try.rb
471
+ - try/unit/refinements/time_literals_string_methods_try.rb
463
472
  - try/valkey.conf
464
473
  homepage: https://github.com/delano/familia
465
474
  licenses:
@@ -1,85 +0,0 @@
1
- # lib/familia/distinguisher.rb
2
-
3
- module Familia
4
- module Distinguisher
5
- # This method determines the appropriate transformation to apply based on
6
- # the class of the input argument.
7
- #
8
- # @param [Object] value_to_distinguish The value to be processed. Keep in
9
- # mind that all data is stored as a string so whatever the type
10
- # of the value, it will be converted to a string.
11
- # @param [Boolean] strict_values Whether to enforce strict value handling.
12
- # Defaults to true.
13
- # @return [String, nil] The processed value as a string or nil for unsupported
14
- # classes.
15
- #
16
- # The method uses a case statement to handle different classes:
17
- # - For `Symbol`, `String`, `Integer`, and `Float` classes, it traces the
18
- # operation and converts the value to a string.
19
- # - For `Familia::Horreum` class, it traces the operation and returns the
20
- # identifier of the value.
21
- # - For `TrueClass`, `FalseClass`, and `NilClass`, it traces the operation and
22
- # converts the value to a string ("true", "false", or "").
23
- # - For any other class, it traces the operation and returns nil.
24
- #
25
- # Alternative names for `value_to_distinguish` could be `input_value`, `value`,
26
- # or `object`.
27
- #
28
- def distinguisher(value_to_distinguish, strict_values: true)
29
- case value_to_distinguish
30
- when ::Symbol, ::String, ::Integer, ::Float
31
- Familia.trace :TOREDIS_DISTINGUISHER, nil, 'string' if Familia.debug?
32
-
33
- # Symbols and numerics are naturally serializable to strings
34
- # so it's a relatively low risk operation.
35
- value_to_distinguish.to_s
36
-
37
- when ::TrueClass, ::FalseClass, ::NilClass
38
- Familia.trace :TOREDIS_DISTINGUISHER, nil, 'true/false/nil' if Familia.debug?
39
-
40
- # TrueClass, FalseClass, and NilClass are considered high risk because their
41
- # original types cannot be reliably determined from their serialized string
42
- # representations. This can lead to unexpected behavior during deserialization.
43
- # For instance, a TrueClass value serialized as "true" might be deserialized as
44
- # a String, causing application errors. Even more problematic, a NilClass value
45
- # serialized as an empty string makes it impossible to distinguish between a
46
- # nil value and an empty string upon deserialization. Such scenarios can result
47
- # in subtle, hard-to-diagnose bugs. To mitigate these risks, we raise an
48
- # exception when encountering these types unless the strict_values option is
49
- # explicitly set to false.
50
- #
51
- raise Familia::NotDistinguishableError, value_to_distinguish if strict_values
52
-
53
- value_to_distinguish.to_s #=> "true", "false", ""
54
-
55
- when Familia::Base, Class
56
- Familia.trace :TOREDIS_DISTINGUISHER, nil, 'base' if Familia.debug?
57
-
58
- # When called with a class we simply transform it to its name. For
59
- # instances of Familia class, we store the identifier.
60
- if value_to_distinguish.is_a?(Class)
61
- value_to_distinguish.name
62
- else
63
- value_to_distinguish.identifier
64
- end
65
-
66
- else
67
- Familia.trace :TOREDIS_DISTINGUISHER, nil, "else1 #{strict_values}" if Familia.debug?
68
-
69
- if value_to_distinguish.class.ancestors.member?(Familia::Base)
70
- Familia.trace :TOREDIS_DISTINGUISHER, nil, 'isabase' if Familia.debug?
71
-
72
- value_to_distinguish.identifier
73
-
74
- else
75
- Familia.trace :TOREDIS_DISTINGUISHER, nil, "else2 #{strict_values}" if Familia.debug?
76
- raise Familia::NotDistinguishableError, value_to_distinguish if strict_values
77
-
78
- nil
79
- end
80
- end
81
- end
82
- end
83
-
84
- extend Distinguisher
85
- end
@@ -1,60 +0,0 @@
1
- # lib/familia/refinements/logger_trace.rb
2
-
3
- require 'pathname'
4
- require 'logger'
5
-
6
- # Controls whether tracing is enabled via an environment variable
7
- FAMILIA_TRACE = ENV.fetch('FAMILIA_TRACE', 'false').downcase
8
-
9
- # Familia::Refinements::LoggerTrace
10
- #
11
- # This module adds a 'trace' log level to the Ruby Logger class.
12
- # It is enabled when the FAMILIA_TRACE environment variable is set to
13
- # '1', 'true', or 'yes' (case-insensitive).
14
- #
15
- # @example Enabling trace logging
16
- # # UnsortedSet environment variable
17
- # ENV['FAMILIA_TRACE'] = 'true'
18
- #
19
- # # In your Ruby code
20
- # require 'logger'
21
- # using Familia::Refinements::LoggerTrace
22
- #
23
- # logger = Logger.new(STDOUT)
24
- # logger.trace("This is a trace message")
25
- #
26
- module Familia
27
- module Refinements
28
- # Familia::Refinements::LoggerTrace
29
- module LoggerTraceMethods
30
- ##
31
- # Logs a message at the TRACE level.
32
- #
33
- # @param progname [String] The program name to include in the log message
34
- # @yield A block that evaluates to the message to log
35
- # @return [true] Always returns true
36
- #
37
- # @example Logging a trace message
38
- # logger.trace("MyApp") { "Detailed trace information" }
39
- def trace(progname = nil, &)
40
- Fiber[:severity_letter] = 'T'
41
- add(Familia::Refinements::LoggerTrace::TRACE, nil, progname, &)
42
- ensure
43
- Fiber[:severity_letter] = nil
44
- end
45
- end
46
-
47
- module LoggerTrace
48
- unless defined?(ENABLED)
49
- # Indicates whether trace logging is enabled
50
- ENABLED = %w[1 true yes].include?(FAMILIA_TRACE).freeze
51
- # The numeric level for trace logging (same as DEBUG)
52
- TRACE = 0
53
- end
54
-
55
- refine Logger do
56
- import_methods LoggerTraceMethods
57
- end
58
- end
59
- end
60
- end