familia 2.0.0.pre19 → 2.0.0.pre22

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 (370) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +4 -9
  3. data/.github/workflows/code-smells.yml +64 -3
  4. data/.pre-commit-config.yaml +8 -6
  5. data/.reek.yml +10 -9
  6. data/.rubocop.yml +4 -0
  7. data/.talismanrc +5 -1
  8. data/CHANGELOG.rst +220 -112
  9. data/CLAUDE.md +28 -1
  10. data/Gemfile +1 -1
  11. data/Gemfile.lock +20 -17
  12. data/bin/try +16 -0
  13. data/bin/tryouts +16 -0
  14. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  15. data/docs/guides/encryption.md +486 -0
  16. data/docs/guides/feature-encrypted-fields.md +123 -7
  17. data/docs/guides/feature-expiration.md +161 -117
  18. data/docs/guides/feature-external-identifiers.md +415 -443
  19. data/docs/guides/feature-object-identifiers.md +400 -269
  20. data/docs/guides/feature-quantization.md +120 -6
  21. data/docs/guides/feature-relationships-indexing.md +318 -0
  22. data/docs/guides/feature-relationships-methods.md +146 -604
  23. data/docs/guides/feature-relationships-participation.md +263 -0
  24. data/docs/guides/feature-relationships.md +118 -136
  25. data/docs/guides/feature-system-devs.md +176 -693
  26. data/docs/guides/feature-system.md +119 -6
  27. data/docs/guides/feature-transient-fields.md +81 -0
  28. data/docs/guides/field-system.md +778 -0
  29. data/docs/guides/index.md +32 -15
  30. data/docs/guides/logging.md +187 -0
  31. data/docs/guides/optimized-loading.md +674 -0
  32. data/docs/guides/thread-safety-monitoring.md +61 -0
  33. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  34. data/docs/migrating/v2.0.0-pre22.md +241 -0
  35. data/docs/overview.md +7 -9
  36. data/docs/reference/api-technical.md +267 -320
  37. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  38. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  39. data/examples/autoloader/mega_customer.rb +2 -0
  40. data/examples/datatype_standalone.rb +4 -3
  41. data/examples/encrypted_fields.rb +2 -1
  42. data/examples/json_usage_patterns.rb +2 -0
  43. data/examples/relationships.rb +3 -0
  44. data/examples/safe_dump.rb +2 -1
  45. data/examples/sampling_demo.rb +53 -0
  46. data/examples/single_connection_transaction_confusions.rb +2 -1
  47. data/familia.gemspec +2 -1
  48. data/lib/familia/base.rb +2 -0
  49. data/lib/familia/connection/behavior.rb +2 -0
  50. data/lib/familia/connection/handlers.rb +2 -0
  51. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  52. data/lib/familia/connection/middleware.rb +34 -24
  53. data/lib/familia/connection/operation_core.rb +3 -2
  54. data/lib/familia/connection/operations.rb +2 -0
  55. data/lib/familia/connection/pipelined_core.rb +3 -3
  56. data/lib/familia/connection/transaction_core.rb +69 -2
  57. data/lib/familia/connection.rb +18 -3
  58. data/lib/familia/data_type/class_methods.rb +3 -1
  59. data/lib/familia/data_type/connection.rb +2 -0
  60. data/lib/familia/data_type/database_commands.rb +2 -0
  61. data/lib/familia/data_type/serialization.rb +79 -52
  62. data/lib/familia/data_type/settings.rb +2 -0
  63. data/lib/familia/data_type/types/counter.rb +2 -0
  64. data/lib/familia/data_type/types/hashkey.rb +7 -5
  65. data/lib/familia/data_type/types/listkey.rb +2 -0
  66. data/lib/familia/data_type/types/lock.rb +2 -0
  67. data/lib/familia/data_type/types/sorted_set.rb +7 -10
  68. data/lib/familia/data_type/types/stringkey.rb +24 -0
  69. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  70. data/lib/familia/data_type.rb +2 -0
  71. data/lib/familia/encryption/encrypted_data.rb +4 -2
  72. data/lib/familia/encryption/manager.rb +2 -0
  73. data/lib/familia/encryption/provider.rb +2 -0
  74. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  75. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  76. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  77. data/lib/familia/encryption/registry.rb +2 -0
  78. data/lib/familia/encryption/request_cache.rb +2 -0
  79. data/lib/familia/encryption.rb +9 -2
  80. data/lib/familia/errors.rb +2 -0
  81. data/lib/familia/features/autoloader.rb +2 -0
  82. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  83. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  84. data/lib/familia/features/encrypted_fields.rb +2 -2
  85. data/lib/familia/features/expiration/extensions.rb +3 -1
  86. data/lib/familia/features/expiration.rb +12 -4
  87. data/lib/familia/features/external_identifier.rb +62 -7
  88. data/lib/familia/features/object_identifier.rb +49 -0
  89. data/lib/familia/features/quantization.rb +3 -1
  90. data/lib/familia/features/relationships/README.md +3 -1
  91. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  92. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +138 -9
  93. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  94. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +97 -21
  95. data/lib/familia/features/relationships/indexing.rb +3 -0
  96. data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
  97. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  98. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  100. data/lib/familia/features/relationships/participation.rb +155 -69
  101. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  102. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  103. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  104. data/lib/familia/features/relationships.rb +5 -3
  105. data/lib/familia/features/safe_dump.rb +2 -0
  106. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  107. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  108. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  109. data/lib/familia/features/transient_fields.rb +2 -0
  110. data/lib/familia/features.rb +2 -0
  111. data/lib/familia/field_type.rb +3 -1
  112. data/lib/familia/horreum/connection.rb +17 -1
  113. data/lib/familia/horreum/database_commands.rb +8 -1
  114. data/lib/familia/horreum/definition.rb +16 -6
  115. data/lib/familia/horreum/management.rb +353 -52
  116. data/lib/familia/horreum/persistence.rb +179 -108
  117. data/lib/familia/horreum/related_fields.rb +2 -0
  118. data/lib/familia/horreum/serialization.rb +23 -4
  119. data/lib/familia/horreum/settings.rb +2 -0
  120. data/lib/familia/horreum/utils.rb +2 -0
  121. data/lib/familia/horreum.rb +15 -1
  122. data/lib/familia/identifier_extractor.rb +3 -1
  123. data/lib/familia/instrumentation.rb +156 -0
  124. data/lib/familia/json_serializer.rb +2 -0
  125. data/lib/familia/logging.rb +92 -32
  126. data/lib/familia/refinements/dear_json.rb +2 -0
  127. data/lib/familia/refinements/stylize_words.rb +2 -14
  128. data/lib/familia/refinements/time_literals.rb +2 -0
  129. data/lib/familia/refinements.rb +2 -0
  130. data/lib/familia/secure_identifier.rb +10 -2
  131. data/lib/familia/settings.rb +2 -0
  132. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  133. data/lib/familia/thread_safety/monitor.rb +328 -0
  134. data/lib/familia/utils.rb +13 -0
  135. data/lib/familia/verifiable_identifier.rb +3 -1
  136. data/lib/familia/version.rb +3 -1
  137. data/lib/familia.rb +31 -4
  138. data/lib/middleware/database_command_counter.rb +152 -0
  139. data/lib/middleware/database_logger.rb +295 -170
  140. data/lib/multi_result.rb +61 -31
  141. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  142. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  143. data/try/edge_cases/json_serialization_try.rb +2 -0
  144. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  145. data/try/edge_cases/race_conditions_try.rb +4 -0
  146. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  147. data/try/edge_cases/string_coercion_try.rb +2 -0
  148. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  149. data/try/features/count_any_edge_cases_try.rb +486 -0
  150. data/try/features/count_any_methods_try.rb +197 -0
  151. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  152. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  153. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  154. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  155. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  156. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  157. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  158. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  159. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  160. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  161. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  162. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  163. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  164. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  165. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  166. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  167. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  168. data/try/features/encryption/config_persistence_try.rb +4 -0
  169. data/try/features/encryption/core_try.rb +4 -0
  170. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  171. data/try/features/encryption/module_loading_try.rb +4 -0
  172. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  173. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  174. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  175. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  176. data/try/features/expiration/expiration_try.rb +4 -0
  177. data/try/features/external_identifier/external_identifier_try.rb +305 -8
  178. data/try/features/feature_dependencies_try.rb +2 -0
  179. data/try/features/feature_improvements_try.rb +2 -0
  180. data/try/features/field_groups_try.rb +2 -0
  181. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  182. data/try/features/object_identifier/object_identifier_try.rb +140 -0
  183. data/try/features/quantization/quantization_try.rb +4 -0
  184. data/try/features/real_feature_integration_try.rb +2 -0
  185. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  186. data/try/features/relationships/indexing_rebuild_try.rb +606 -0
  187. data/try/features/relationships/indexing_try.rb +2 -0
  188. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  189. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  190. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  191. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  192. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  193. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  194. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  195. data/try/features/relationships/relationships_api_changes_try.rb +2 -0
  196. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  197. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  198. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  199. data/try/features/relationships/relationships_performance_try.rb +4 -0
  200. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  201. data/try/features/relationships/relationships_try.rb +6 -4
  202. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  203. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  204. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  205. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  206. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  207. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  208. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  209. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  210. data/try/integration/connection/fiber_context_preservation_try.rb +4 -0
  211. data/try/integration/connection/handler_constraints_try.rb +4 -0
  212. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  213. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  214. data/try/integration/connection/operation_mode_guards_try.rb +4 -0
  215. data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
  216. data/try/integration/connection/pools_try.rb +4 -0
  217. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  218. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  219. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  220. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  221. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  222. data/try/integration/connection/transaction_modes_try.rb +4 -0
  223. data/try/integration/conventional_inheritance_try.rb +4 -0
  224. data/try/integration/create_method_try.rb +4 -0
  225. data/try/integration/cross_component_try.rb +4 -0
  226. data/try/integration/data_types/datatype_pipelines_try.rb +9 -3
  227. data/try/integration/data_types/datatype_transactions_try.rb +17 -7
  228. data/try/integration/database_consistency_try.rb +4 -0
  229. data/try/integration/familia_extended_try.rb +4 -0
  230. data/try/integration/familia_members_methods_try.rb +4 -0
  231. data/try/integration/models/customer_safe_dump_try.rb +4 -0
  232. data/try/integration/models/customer_try.rb +7 -3
  233. data/try/integration/models/datatype_base_try.rb +4 -0
  234. data/try/integration/models/familia_object_try.rb +4 -0
  235. data/try/integration/persistence_operations_try.rb +4 -0
  236. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  237. data/try/integration/save_methods_consistency_try.rb +241 -0
  238. data/try/integration/scenarios_try.rb +4 -0
  239. data/try/integration/secure_identifier_try.rb +4 -0
  240. data/try/integration/transaction_safety_core_try.rb +176 -0
  241. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  242. data/try/integration/verifiable_identifier_try.rb +4 -0
  243. data/try/investigation/pipeline_routing/README.md +228 -0
  244. data/try/performance/benchmarks_try.rb +4 -0
  245. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  246. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  247. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  248. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  249. data/try/support/debugging/debug_aad_process.rb +3 -0
  250. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  251. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  252. data/try/support/debugging/debug_context_aad.rb +3 -0
  253. data/try/support/debugging/debug_context_simple.rb +3 -0
  254. data/try/support/debugging/debug_cross_context.rb +3 -0
  255. data/try/support/debugging/debug_database_load.rb +3 -0
  256. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  257. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  258. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  259. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  260. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  261. data/try/support/debugging/debug_load_path.rb +3 -0
  262. data/try/support/debugging/debug_method_definition.rb +3 -0
  263. data/try/support/debugging/debug_method_resolution.rb +3 -0
  264. data/try/support/debugging/debug_minimal.rb +3 -0
  265. data/try/support/debugging/debug_provider.rb +3 -0
  266. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  267. data/try/support/debugging/debug_string_class.rb +3 -0
  268. data/try/support/debugging/debug_test.rb +3 -0
  269. data/try/support/debugging/debug_test_design.rb +3 -0
  270. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  271. data/try/support/debugging/provider_diagnostics.rb +4 -0
  272. data/try/support/helpers/test_cleanup.rb +4 -0
  273. data/try/support/helpers/test_helpers.rb +5 -0
  274. data/try/support/memory/memory_basic_test.rb +4 -0
  275. data/try/support/memory/memory_detailed_test.rb +4 -0
  276. data/try/support/memory/memory_search_for_string.rb +4 -0
  277. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  278. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  279. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  280. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  281. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  282. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  283. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  284. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  285. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  286. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  287. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  288. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  289. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  290. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  291. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  292. data/try/thread_safety/README.md +496 -0
  293. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  294. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  295. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  296. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  297. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  298. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  299. data/try/thread_safety/field_registration_race_try.rb +222 -0
  300. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  301. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  302. data/try/thread_safety/module_config_race_try.rb +175 -0
  303. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  304. data/try/unit/core/autoloader_try.rb +4 -0
  305. data/try/unit/core/base_enhancements_try.rb +4 -0
  306. data/try/unit/core/connection_try.rb +4 -0
  307. data/try/unit/core/errors_try.rb +4 -0
  308. data/try/unit/core/extensions_try.rb +4 -0
  309. data/try/unit/core/familia_logger_try.rb +2 -0
  310. data/try/unit/core/familia_try.rb +4 -0
  311. data/try/unit/core/middleware_sampling_try.rb +335 -0
  312. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  313. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  314. data/try/unit/core/middleware_try.rb +4 -0
  315. data/try/unit/core/settings_try.rb +4 -0
  316. data/try/unit/core/time_utils_try.rb +4 -0
  317. data/try/unit/core/tools_try.rb +4 -0
  318. data/try/unit/core/utils_try.rb +37 -0
  319. data/try/unit/data_types/boolean_try.rb +39 -22
  320. data/try/unit/data_types/counter_try.rb +4 -0
  321. data/try/unit/data_types/datatype_base_try.rb +4 -0
  322. data/try/unit/data_types/hash_try.rb +6 -2
  323. data/try/unit/data_types/list_try.rb +4 -0
  324. data/try/unit/data_types/lock_try.rb +4 -0
  325. data/try/unit/data_types/serialization_try.rb +386 -0
  326. data/try/unit/data_types/sorted_set_try.rb +4 -0
  327. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  328. data/try/unit/data_types/string_try.rb +4 -0
  329. data/try/unit/data_types/unsortedset_try.rb +4 -0
  330. data/try/unit/familia_resolve_class_try.rb +116 -0
  331. data/try/unit/horreum/auto_indexing_on_save_try.rb +5 -1
  332. data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
  333. data/try/unit/horreum/base_try.rb +4 -0
  334. data/try/unit/horreum/class_methods_try.rb +4 -0
  335. data/try/unit/horreum/commands_try.rb +4 -0
  336. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  337. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +6 -1
  338. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  339. data/try/unit/horreum/field_categories_try.rb +4 -0
  340. data/try/unit/horreum/field_definition_try.rb +4 -0
  341. data/try/unit/horreum/initialization_try.rb +4 -0
  342. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  343. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  344. data/try/unit/horreum/relations_try.rb +4 -0
  345. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  346. data/try/unit/horreum/serialization_try.rb +4 -0
  347. data/try/unit/horreum/settings_try.rb +4 -0
  348. data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
  349. data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -0
  350. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  351. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  352. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  353. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  354. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  355. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  356. data/try/unit/thread_safety_monitor_try.rb +149 -0
  357. metadata +69 -17
  358. data/.github/workflows/code-quality.yml +0 -138
  359. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
  360. data/changelog.d/20251011_203905_delano_next.rst +0 -30
  361. data/changelog.d/20251011_212633_delano_next.rst +0 -13
  362. data/changelog.d/20251011_221253_delano_next.rst +0 -26
  363. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  364. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  365. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  366. data/docs/archive/README.md +0 -64
  367. data/docs/archive/api-reference.md +0 -333
  368. data/docs/guides/core-field-system.md +0 -806
  369. data/docs/guides/implementation.md +0 -276
  370. data/docs/guides/security-model.md +0 -183
@@ -0,0 +1,778 @@
1
+ # Field System Guide
2
+
3
+ ## Overview
4
+
5
+ Familia's Field System provides a flexible, extensible architecture for defining and managing object attributes with customizable behavior, conflict resolution, and serialization. The system uses a FieldType-based architecture that separates field definition from implementation, enabling custom field behaviors and advanced features.
6
+
7
+ ## Core Architecture
8
+
9
+ ### FieldType System
10
+
11
+ The Field System is built around the `FieldType` class hierarchy:
12
+
13
+ ```
14
+ FieldType # Base class for all field types
15
+ ├── TransientFieldType # Non-persistent fields (memory only)
16
+ ├── EncryptedFieldType # Encrypted storage fields
17
+ ├── ExternalIdentifierFieldType # External ID fields
18
+ ├── ObjectIdentifierFieldType # Object ID fields
19
+ └── Custom field types # User-defined field behaviors
20
+ ```
21
+
22
+ ### Field Definition Flow
23
+
24
+ 1. **Field Declaration**: `field :name, options...`
25
+ 2. **FieldType Creation**: Appropriate FieldType instance created
26
+ 3. **Registration**: FieldType registered with the class
27
+ 4. **Method Installation**: Getter, setter, and fast methods defined
28
+ 5. **Runtime Usage**: Methods available on instances
29
+
30
+ ## Basic Usage
31
+
32
+ ### Simple Field Definition
33
+
34
+ ```ruby
35
+ class Customer < Familia::Horreum
36
+ # Basic field with default settings
37
+ field :name
38
+
39
+ # Field with custom method name
40
+ field :email_address, as: :email
41
+
42
+ # Field without accessor methods
43
+ field :internal_data, as: false
44
+
45
+ # Field without fast writer method
46
+ field :readonly_data, fast_method: false
47
+ end
48
+
49
+ customer = Customer.new
50
+ customer.name = "Acme Corp" # Standard setter
51
+ customer.email = "admin@acme.com" # Custom method name
52
+ customer.name!("Updated Corp") # Fast writer (immediate DB persistence)
53
+ ```
54
+
55
+ ### Method Conflict Resolution
56
+
57
+ Familia provides several strategies for handling method name conflicts:
58
+
59
+ ```ruby
60
+ class Customer < Familia::Horreum
61
+ # Raise error if method exists (default)
62
+ field :status, on_conflict: :raise
63
+
64
+ # Skip field definition if method exists
65
+ field :type, on_conflict: :skip
66
+
67
+ # Warn but proceed with definition
68
+ field :class, on_conflict: :warn
69
+
70
+ # Silently overwrite existing method
71
+ field :id, on_conflict: :overwrite
72
+ end
73
+ ```
74
+
75
+ ## Special Field Types
76
+
77
+ ### Transient Fields
78
+
79
+ Transient fields exist only in memory and are never persisted to the database. Values are automatically wrapped in `RedactedString` objects for security:
80
+
81
+ ```ruby
82
+ class SecretService < Familia::Horreum
83
+ feature :transient_fields
84
+
85
+ field :name # Regular persistent field
86
+ transient_field :api_key # Wrapped in RedactedString
87
+ transient_field :password # Not persisted to database
88
+ end
89
+
90
+ service = SecretService.new
91
+ service.api_key = "sk-1234567890"
92
+ service.api_key.class #=> RedactedString
93
+ puts service.api_key #=> "[REDACTED]"
94
+
95
+ # Safe access pattern
96
+ service.api_key.expose do |key|
97
+ HTTP.post(url, headers: { 'Authorization' => "Bearer #{key}" })
98
+ end
99
+ ```
100
+
101
+ ### Encrypted Fields
102
+
103
+ Encrypted fields provide transparent encryption/decryption with strong cryptographic protection:
104
+
105
+ ```ruby
106
+ class Document < Familia::Horreum
107
+ feature :encrypted_fields
108
+
109
+ field :title # Plaintext
110
+ encrypted_field :content # Encrypted storage
111
+ encrypted_field :api_key, aad_fields: [:title] # With additional authentication
112
+ end
113
+
114
+ doc = Document.new(title: "Secret", content: "classified info")
115
+ doc.content.class #=> ConcealedString
116
+ puts doc.content #=> "[CONCEALED]"
117
+
118
+ # Explicit access required
119
+ doc.content.reveal do |plaintext|
120
+ puts plaintext # => "classified info"
121
+ end
122
+ ```
123
+
124
+ ## Data Type Fields
125
+
126
+ Familia provides Redis/Valkey data structure fields through the related fields system:
127
+
128
+ ```ruby
129
+ class User < Familia::Horreum
130
+ identifier_field :user_id
131
+ field :user_id, :name, :email
132
+
133
+ # Redis data structure fields
134
+ list :activity_log # Redis LIST
135
+ set :permissions # Redis SET
136
+ sorted_set :scores # Redis ZSET
137
+ hashkey :preferences # Redis HASH
138
+ counter :login_count # Redis counter
139
+ end
140
+
141
+ user = User.new(user_id: 'u123')
142
+ user.activity_log << "logged in"
143
+ user.permissions.add("read")
144
+ user.scores.add("quiz1", 95)
145
+ user.preferences["theme"] = "dark"
146
+ user.login_count.increment
147
+ ```
148
+
149
+ ## Advanced Field Types
150
+
151
+ ### Creating Custom Field Types
152
+
153
+ Custom field types allow you to define specialized behavior for your fields:
154
+
155
+ ```ruby
156
+ class TimestampFieldType < Familia::FieldType
157
+ def define_setter(klass)
158
+ field_name = @name
159
+ method_name = @method_name
160
+
161
+ handle_method_conflict(klass, :"#{method_name}=") do
162
+ klass.define_method :"#{method_name}=" do |value|
163
+ timestamp = case value
164
+ when Time then value.to_i
165
+ when String then Time.parse(value).to_i
166
+ when Numeric then value.to_i
167
+ else nil
168
+ end
169
+ instance_variable_set(:"@#{field_name}", timestamp)
170
+ end
171
+ end
172
+ end
173
+
174
+ def define_getter(klass)
175
+ field_name = @name
176
+ method_name = @method_name
177
+
178
+ handle_method_conflict(klass, method_name) do
179
+ klass.define_method method_name do
180
+ timestamp = instance_variable_get(:"@#{field_name}")
181
+ timestamp ? Time.at(timestamp) : nil
182
+ end
183
+ end
184
+ end
185
+
186
+ def serialize(value, _record = nil)
187
+ value&.to_i
188
+ end
189
+
190
+ def deserialize(value, _record = nil)
191
+ value ? Time.at(value.to_i) : nil
192
+ end
193
+
194
+ def category
195
+ :timestamp
196
+ end
197
+ end
198
+
199
+ class Event < Familia::Horreum
200
+ def self.timestamp_field(name, **options)
201
+ field_type = TimestampFieldType.new(name, **options)
202
+ register_field_type(field_type)
203
+ end
204
+
205
+ identifier_field :event_id
206
+ field :event_id, :name, :description
207
+ timestamp_field :created_at
208
+ timestamp_field :updated_at
209
+ end
210
+
211
+ event = Event.new(event_id: 'e123')
212
+ event.created_at = "2024-01-01 12:00:00 UTC"
213
+ event.created_at.class #=> Time
214
+ event.created_at.to_s #=> "2024-01-01 12:00:00 UTC"
215
+ ```
216
+
217
+ ### Enum Field Type
218
+
219
+ Create fields with restricted values and validation:
220
+
221
+ ```ruby
222
+ class EnumFieldType < Familia::FieldType
223
+ def initialize(name, values:, **options)
224
+ super(name, **options)
225
+ @valid_values = Array(values).map(&:to_s).freeze
226
+ end
227
+
228
+ def define_setter(klass)
229
+ field_name = @name
230
+ method_name = @method_name
231
+ valid_values = @valid_values
232
+
233
+ handle_method_conflict(klass, :"#{method_name}=") do
234
+ klass.define_method :"#{method_name}=" do |value|
235
+ unless valid_values.include?(value.to_s)
236
+ raise ArgumentError, "Invalid #{field_name}: #{value}. Valid values: #{valid_values.join(', ')}"
237
+ end
238
+ instance_variable_set(:"@#{field_name}", value.to_s)
239
+ end
240
+ end
241
+ end
242
+
243
+ def install(klass)
244
+ super
245
+ # Add constants for enum values
246
+ @valid_values.each do |value|
247
+ const_name = "#{@name.to_s.upcase}_#{value.upcase}"
248
+ klass.const_set(const_name, value) unless klass.const_defined?(const_name)
249
+ end
250
+ end
251
+
252
+ def category
253
+ :enum
254
+ end
255
+ end
256
+
257
+ class Order < Familia::Horreum
258
+ def self.enum_field(name, values:, **options)
259
+ field_type = EnumFieldType.new(name, values: values, **options)
260
+ register_field_type(field_type)
261
+ end
262
+
263
+ identifier_field :order_id
264
+ field :order_id, :customer_id
265
+ enum_field :status, values: [:pending, :processing, :shipped, :delivered]
266
+ enum_field :priority, values: [:low, :normal, :high, :urgent]
267
+ end
268
+
269
+ order = Order.new(order_id: 'o123')
270
+ order.status = :pending # Valid
271
+ order.status = "processing" # Valid (string converted)
272
+ order.priority = Order::PRIORITY_HIGH # Using generated constant
273
+
274
+ # This raises ArgumentError
275
+ order.status = :invalid # Invalid value
276
+ ```
277
+
278
+ ## Field Metadata and Introspection
279
+
280
+ ### Accessing Field Information
281
+
282
+ ```ruby
283
+ class Product < Familia::Horreum
284
+ feature :transient_fields
285
+
286
+ field :name
287
+ field :price
288
+ field :description
289
+ transient_field :temp_data
290
+ end
291
+
292
+ # Get all field names
293
+ Product.fields #=> [:name, :price, :description, :temp_data]
294
+
295
+ # Get field types
296
+ Product.field_types #=> { name: FieldType, price: FieldType, ... }
297
+
298
+ # Get persistent vs transient fields
299
+ Product.persistent_fields #=> [:name, :price, :description]
300
+ Product.transient_fields #=> [:temp_data]
301
+
302
+ # Check field properties
303
+ product = Product.new
304
+ field_type = Product.field_types[:temp_data]
305
+ field_type.persistent? #=> false
306
+ field_type.transient? #=> true
307
+ field_type.category #=> :transient
308
+ ```
309
+
310
+ ### Field Categories and Filtering
311
+
312
+ Field types can specify categories for grouping and filtering:
313
+
314
+ ```ruby
315
+ class SearchableFieldType < Familia::FieldType
316
+ def category
317
+ :searchable
318
+ end
319
+ end
320
+
321
+ class Product < Familia::Horreum
322
+ def self.searchable_field(name, **options)
323
+ field_type = SearchableFieldType.new(name, **options)
324
+ register_field_type(field_type)
325
+ end
326
+
327
+ searchable_field :name
328
+ searchable_field :description
329
+ field :internal_id
330
+
331
+ def self.searchable_fields
332
+ field_types.select { |_, ft| ft.category == :searchable }.keys
333
+ end
334
+ end
335
+
336
+ Product.searchable_fields #=> [:name, :description]
337
+ ```
338
+
339
+ ## Fast Methods and Database Operations
340
+
341
+ ### Fast Method Behavior
342
+
343
+ Fast methods (ending with `!`) provide immediate database persistence without requiring a separate `save` call:
344
+
345
+ ```ruby
346
+ class UserProfile < Familia::Horreum
347
+ identifier_field :user_id
348
+ field :user_id, :name, :email, :last_login_at
349
+ end
350
+
351
+ profile = UserProfile.new(user_id: 'u123')
352
+ profile.name!("John Doe") # Immediately persists to database
353
+ profile.email!("john@example.com") # No save() needed
354
+
355
+ # Reading with fast method returns current database value
356
+ current_name = profile.name! # Reads from database
357
+ ```
358
+
359
+ ### Custom Fast Method Behavior
360
+
361
+ Override fast method behavior for specialized use cases:
362
+
363
+ ```ruby
364
+ class AuditedFieldType < Familia::FieldType
365
+ def define_fast_writer(klass)
366
+ return unless @fast_method_name&.to_s&.end_with?('!')
367
+
368
+ field_name = @name
369
+ method_name = @method_name
370
+ fast_method_name = @fast_method_name
371
+
372
+ handle_method_conflict(klass, fast_method_name) do
373
+ klass.define_method fast_method_name do |*args|
374
+ val = args.first
375
+ return hget(field_name) if val.nil?
376
+
377
+ # Audit the change
378
+ old_value = hget(field_name)
379
+ timestamp = Time.now.to_i
380
+
381
+ # Log the change
382
+ puts "AUDIT: #{field_name} changed from #{old_value} to #{val} at #{timestamp}"
383
+
384
+ # Update instance variable
385
+ send(:"#{method_name}=", val) if method_name
386
+
387
+ # Persist to database
388
+ hset(field_name, serialize_value(val))
389
+ end
390
+ end
391
+ end
392
+ end
393
+
394
+ class AuditedDocument < Familia::Horreum
395
+ def self.audited_field(name, **options)
396
+ field_type = AuditedFieldType.new(name, **options)
397
+ register_field_type(field_type)
398
+ end
399
+
400
+ identifier_field :doc_id
401
+ field :doc_id, :title
402
+ audited_field :content
403
+ audited_field :status
404
+ end
405
+ ```
406
+
407
+ ## Field Options and Configuration
408
+
409
+ ### Available Options
410
+
411
+ ```ruby
412
+ field :name,
413
+ as: :display_name, # Custom method name
414
+ fast_method: :name_now!, # Custom fast method name
415
+ fast_method: false, # Disable fast method
416
+ on_conflict: :skip, # Conflict resolution strategy
417
+ loggable: false # Exclude from serialization
418
+ ```
419
+
420
+ ### Conflict Resolution Strategies
421
+
422
+ - `:raise` - Raise error if method exists (default)
423
+ - `:skip` - Skip field definition if method exists
424
+ - `:warn` - Warn but proceed with definition
425
+ - `:overwrite` - Silently overwrite existing method
426
+
427
+ ## Integration Patterns
428
+
429
+ ### Rails Integration
430
+
431
+ ```ruby
432
+ module FamiliaFields
433
+ extend ActiveSupport::Concern
434
+
435
+ class_methods do
436
+ def string_field(*names, **options)
437
+ names.each { |name| field(name, **options) }
438
+ end
439
+
440
+ def integer_field(*names, **options)
441
+ field_type = Class.new(Familia::FieldType) do
442
+ def serialize(value, _record = nil)
443
+ value&.to_i
444
+ end
445
+
446
+ def deserialize(value, _record = nil)
447
+ value&.to_i
448
+ end
449
+ end
450
+
451
+ names.each do |name|
452
+ register_field_type(field_type.new(name, **options))
453
+ end
454
+ end
455
+
456
+ def boolean_field(*names, **options)
457
+ field_type = Class.new(Familia::FieldType) do
458
+ def serialize(value, _record = nil)
459
+ !!value
460
+ end
461
+
462
+ def deserialize(value, _record = nil)
463
+ case value.to_s.downcase
464
+ when 'true', '1', 'yes', 'on' then true
465
+ when 'false', '0', 'no', 'off' then false
466
+ else nil
467
+ end
468
+ end
469
+
470
+ def define_getter(klass)
471
+ field_name = @name
472
+ method_name = @method_name
473
+
474
+ handle_method_conflict(klass, method_name) do
475
+ klass.define_method method_name do
476
+ value = instance_variable_get(:"@#{field_name}")
477
+ !!value
478
+ end
479
+ end
480
+ end
481
+ end
482
+
483
+ names.each do |name|
484
+ register_field_type(field_type.new(name, **options))
485
+ end
486
+ end
487
+ end
488
+ end
489
+
490
+ class User < Familia::Horreum
491
+ include FamiliaFields
492
+
493
+ identifier_field :user_id
494
+ string_field :user_id, :email, :name
495
+ integer_field :age, :login_count
496
+ boolean_field :active, :verified
497
+ end
498
+ ```
499
+
500
+ ### Validation Integration
501
+
502
+ ```ruby
503
+ class ValidatedFieldType < Familia::FieldType
504
+ def initialize(name, validations: {}, **options)
505
+ super(name, **options)
506
+ @validations = validations
507
+ end
508
+
509
+ def define_setter(klass)
510
+ field_name = @name
511
+ method_name = @method_name
512
+ validations = @validations
513
+
514
+ handle_method_conflict(klass, :"#{method_name}=") do
515
+ klass.define_method :"#{method_name}=" do |value|
516
+ # Run validations
517
+ validations.each do |type, constraint|
518
+ case type
519
+ when :presence
520
+ raise ArgumentError, "#{field_name} cannot be blank" if constraint && value.to_s.strip.empty?
521
+ when :length
522
+ if constraint.is_a?(Hash)
523
+ min = constraint[:minimum] || constraint[:min]
524
+ max = constraint[:maximum] || constraint[:max]
525
+ len = value.to_s.length
526
+ raise ArgumentError, "#{field_name} too short (minimum: #{min})" if min && len < min
527
+ raise ArgumentError, "#{field_name} too long (maximum: #{max})" if max && len > max
528
+ end
529
+ when :format
530
+ raise ArgumentError, "#{field_name} format invalid" if constraint && !value.to_s.match?(constraint)
531
+ end
532
+ end
533
+
534
+ instance_variable_set(:"@#{field_name}", value)
535
+ end
536
+ end
537
+ end
538
+ end
539
+
540
+ class User < Familia::Horreum
541
+ def self.validated_field(name, validations: {}, **options)
542
+ field_type = ValidatedFieldType.new(name, validations: validations, **options)
543
+ register_field_type(field_type)
544
+ end
545
+
546
+ identifier_field :user_id
547
+ validated_field :user_id, validations: { presence: true }
548
+ validated_field :email, validations: {
549
+ presence: true,
550
+ format: /\A[^@\s]+@[^@\s]+\z/
551
+ }
552
+ validated_field :status, validations: {
553
+ presence: true,
554
+ format: /\A(active|inactive|pending)\z/
555
+ }
556
+ validated_field :name, validations: {
557
+ length: { minimum: 2, maximum: 50 }
558
+ }
559
+ end
560
+ ```
561
+
562
+ ## Performance Considerations
563
+
564
+ ### Efficient Field Operations
565
+
566
+ ```ruby
567
+ # Batch updates using fast methods
568
+ user.name!("John")
569
+ user.email!("john@example.com")
570
+ user.status!("active")
571
+
572
+ # Use transactions for multiple operations
573
+ redis.multi do
574
+ user.name!("John")
575
+ user.email!("john@example.com")
576
+ user.status!("active")
577
+ end
578
+ ```
579
+
580
+ ### Memory-Efficient Field Storage
581
+
582
+ ```ruby
583
+ class CompactFieldType < Familia::FieldType
584
+ def serialize(value, _record = nil)
585
+ case value
586
+ when String
587
+ # Compress long strings
588
+ value.length > 100 ? Zlib::Deflate.deflate(value) : value
589
+ when Hash
590
+ # Use more compact JSON representation
591
+ value.to_json
592
+ when Array
593
+ # Join simple arrays
594
+ value.all? { |v| v.is_a?(String) } ? value.join('|') : value.to_json
595
+ else
596
+ value
597
+ end
598
+ end
599
+
600
+ def deserialize(value, _record = nil)
601
+ return value unless value.is_a?(String)
602
+
603
+ # Try to decompress
604
+ if value.start_with?("\x78\x9C") # zlib magic bytes
605
+ Zlib::Inflate.inflate(value)
606
+ elsif value.start_with?('{', '[')
607
+ JSON.parse(value)
608
+ elsif value.include?('|')
609
+ value.split('|')
610
+ else
611
+ value
612
+ end
613
+ rescue JSON::ParserError, Zlib::Error
614
+ value # Return original if parsing fails
615
+ end
616
+ end
617
+ ```
618
+
619
+ ## Testing Field Types
620
+
621
+ ### RSpec Testing
622
+
623
+ ```ruby
624
+ describe TimestampFieldType do
625
+ let(:field_type) { TimestampFieldType.new(:created_at) }
626
+ let(:test_class) do
627
+ Class.new do
628
+ def self.name; 'TestClass'; end
629
+ include Familia::Horreum
630
+ end
631
+ end
632
+
633
+ before do
634
+ field_type.install(test_class)
635
+ end
636
+
637
+ it "converts various time formats" do
638
+ instance = test_class.new
639
+ instance.created_at = "2024-01-01 12:00:00 UTC"
640
+ expect(instance.created_at).to be_a(Time)
641
+ expect(instance.created_at.to_s).to include("2024-01-01 12:00:00")
642
+
643
+ instance.created_at = Time.now
644
+ expect(instance.created_at).to be_a(Time)
645
+ end
646
+
647
+ it "serializes to integer" do
648
+ time = Time.parse("2024-01-01 12:00:00 UTC")
649
+ expect(field_type.serialize(time)).to eq(time.to_i)
650
+ end
651
+
652
+ it "deserializes from integer" do
653
+ timestamp = Time.parse("2024-01-01 12:00:00 UTC").to_i
654
+ result = field_type.deserialize(timestamp)
655
+ expect(result).to be_a(Time)
656
+ expect(result.to_i).to eq(timestamp)
657
+ end
658
+ end
659
+ ```
660
+
661
+ ## Best Practices
662
+
663
+ ### 1. Choose Appropriate Field Types
664
+
665
+ ```ruby
666
+ class User < Familia::Horreum
667
+ feature :transient_fields
668
+ feature :encrypted_fields
669
+
670
+ field :name # Regular field for non-sensitive data
671
+ field :metadata # JSON data can be stored as regular field
672
+ transient_field :temp_token # Sensitive temporary data
673
+ encrypted_field :api_key # Sensitive persistent data
674
+ end
675
+
676
+ # Use specialized field types for domain-specific data
677
+ class GeoLocation < Familia::Horreum
678
+ coordinate_field :latitude # Custom validation for coordinates
679
+ coordinate_field :longitude
680
+ end
681
+ ```
682
+
683
+ ### 2. Handle Method Conflicts Gracefully
684
+
685
+ ```ruby
686
+ class SafeFieldDefinition < Familia::Horreum
687
+ # Always use skip strategy for potentially conflicting names
688
+ def self.safe_field(name, **options)
689
+ field(name, on_conflict: :skip, **options)
690
+ end
691
+ end
692
+ ```
693
+
694
+ ### 3. Optimize for Common Use Cases
695
+
696
+ ```ruby
697
+ class BaseModel < Familia::Horreum
698
+ def self.timestamps
699
+ timestamp_field :created_at
700
+ timestamp_field :updated_at
701
+ end
702
+
703
+ def self.soft_delete
704
+ boolean_field :deleted
705
+ timestamp_field :deleted_at
706
+ end
707
+ end
708
+
709
+ class User < BaseModel
710
+ timestamps
711
+ soft_delete
712
+
713
+ field :name, :email
714
+ end
715
+ ```
716
+
717
+ ### 4. Use Field Groups for Organization
718
+
719
+ ```ruby
720
+ class User < Familia::Horreum
721
+ field_group :identity do
722
+ field :user_id
723
+ field :email
724
+ field :username
725
+ end
726
+
727
+ field_group :profile do
728
+ field :first_name
729
+ field :last_name
730
+ field :avatar_url
731
+ end
732
+
733
+ field_group :preferences do
734
+ field :theme
735
+ field :language
736
+ field :timezone
737
+ end
738
+ end
739
+
740
+ # Access grouped fields
741
+ User.field_groups[:identity] #=> [:user_id, :email, :username]
742
+ User.field_groups[:profile] #=> [:first_name, :last_name, :avatar_url]
743
+ ```
744
+
745
+ ## API Reference
746
+
747
+ ### FieldType Class
748
+
749
+ ```ruby
750
+ # Constructor
751
+ FieldType.new(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, loggable: true, **options)
752
+
753
+ # Key methods
754
+ field_type.install(klass) # Install on class
755
+ field_type.define_getter(klass) # Define getter method
756
+ field_type.define_setter(klass) # Define setter method
757
+ field_type.define_fast_writer(klass) # Define fast writer method
758
+ field_type.serialize(value, record) # Serialize for storage
759
+ field_type.deserialize(value, record) # Deserialize from storage
760
+ field_type.persistent? # Check if persisted
761
+ field_type.category # Get field category
762
+ field_type.generated_methods # Get all generated method names
763
+ ```
764
+
765
+ ### Class Methods
766
+
767
+ ```ruby
768
+ # Field definition
769
+ field(name, **options) # Define a field
770
+ register_field_type(field_type) # Register custom field type
771
+
772
+ # Introspection
773
+ fields # Get all field names
774
+ field_types # Get all field types
775
+ persistent_fields # Get persistent field names
776
+ transient_fields # Get transient field names
777
+ field_method_map # Get field name to method mappings
778
+ ```