familia 2.0.0.pre18 → 2.0.0.pre21

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/CHANGELOG.rst +205 -88
  8. data/CLAUDE.md +62 -10
  9. data/Gemfile +3 -3
  10. data/Gemfile.lock +27 -62
  11. data/README.md +39 -0
  12. data/bin/try +16 -0
  13. data/bin/tryouts +16 -0
  14. data/changelog.d/20251105_flexible_external_identifier_format.rst +66 -0
  15. data/changelog.d/20251107_112554_delano_179_participation_asymmetry.rst +44 -0
  16. data/changelog.d/20251107_213121_delano_fix_thread_safety_races_011CUumCP492Twxm4NLt2FvL.rst +20 -0
  17. data/changelog.d/20251107_fix_participates_in_symbol_resolution.rst +91 -0
  18. data/changelog.d/20251107_optimized_redis_exists_checks.rst +94 -0
  19. data/changelog.d/20251108_frozen_string_literal_pragma.rst +44 -0
  20. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  21. data/docs/guides/encryption.md +486 -0
  22. data/docs/guides/feature-encrypted-fields.md +123 -7
  23. data/docs/guides/feature-expiration.md +177 -133
  24. data/docs/guides/feature-external-identifiers.md +415 -443
  25. data/docs/guides/feature-object-identifiers.md +400 -269
  26. data/docs/guides/feature-quantization.md +120 -6
  27. data/docs/guides/feature-relationships-indexing.md +318 -0
  28. data/docs/guides/feature-relationships-methods.md +146 -604
  29. data/docs/guides/feature-relationships-participation.md +263 -0
  30. data/docs/guides/feature-relationships.md +118 -136
  31. data/docs/guides/feature-system-devs.md +176 -693
  32. data/docs/guides/feature-system.md +119 -6
  33. data/docs/guides/feature-transient-fields.md +81 -0
  34. data/docs/guides/field-system.md +778 -0
  35. data/docs/guides/index.md +32 -15
  36. data/docs/guides/logging.md +187 -0
  37. data/docs/guides/optimized-loading.md +674 -0
  38. data/docs/guides/thread-safety-monitoring.md +61 -0
  39. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  40. data/docs/migrating/v2.0.0-pre19.md +197 -0
  41. data/docs/migrating/v2.0.0-pre22.md +241 -0
  42. data/docs/overview.md +7 -9
  43. data/docs/reference/api-technical.md +267 -320
  44. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  45. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  46. data/examples/autoloader/mega_customer.rb +2 -0
  47. data/examples/datatype_standalone.rb +282 -0
  48. data/examples/encrypted_fields.rb +2 -1
  49. data/examples/json_usage_patterns.rb +2 -0
  50. data/examples/relationships.rb +3 -0
  51. data/examples/safe_dump.rb +2 -1
  52. data/examples/sampling_demo.rb +53 -0
  53. data/examples/single_connection_transaction_confusions.rb +2 -1
  54. data/familia.gemspec +2 -1
  55. data/lib/familia/base.rb +2 -0
  56. data/lib/familia/connection/behavior.rb +254 -0
  57. data/lib/familia/connection/handlers.rb +97 -0
  58. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  59. data/lib/familia/connection/middleware.rb +34 -24
  60. data/lib/familia/connection/operation_core.rb +3 -1
  61. data/lib/familia/connection/operations.rb +2 -0
  62. data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +4 -2
  63. data/lib/familia/connection/transaction_core.rb +75 -9
  64. data/lib/familia/connection.rb +21 -5
  65. data/lib/familia/data_type/class_methods.rb +3 -1
  66. data/lib/familia/data_type/connection.rb +153 -7
  67. data/lib/familia/data_type/database_commands.rb +9 -4
  68. data/lib/familia/data_type/serialization.rb +10 -4
  69. data/lib/familia/data_type/settings.rb +2 -0
  70. data/lib/familia/data_type/types/counter.rb +2 -0
  71. data/lib/familia/data_type/types/hashkey.rb +8 -6
  72. data/lib/familia/data_type/types/listkey.rb +2 -0
  73. data/lib/familia/data_type/types/lock.rb +2 -0
  74. data/lib/familia/data_type/types/sorted_set.rb +2 -0
  75. data/lib/familia/data_type/types/stringkey.rb +2 -0
  76. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  77. data/lib/familia/data_type.rb +2 -0
  78. data/lib/familia/encryption/encrypted_data.rb +4 -2
  79. data/lib/familia/encryption/manager.rb +2 -0
  80. data/lib/familia/encryption/provider.rb +2 -0
  81. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  82. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  83. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  84. data/lib/familia/encryption/registry.rb +2 -0
  85. data/lib/familia/encryption/request_cache.rb +2 -0
  86. data/lib/familia/encryption.rb +9 -2
  87. data/lib/familia/errors.rb +53 -14
  88. data/lib/familia/features/autoloader.rb +2 -0
  89. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  90. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  91. data/lib/familia/features/encrypted_fields.rb +2 -2
  92. data/lib/familia/features/expiration/extensions.rb +11 -11
  93. data/lib/familia/features/expiration.rb +29 -21
  94. data/lib/familia/features/external_identifier.rb +33 -7
  95. data/lib/familia/features/object_identifier.rb +2 -0
  96. data/lib/familia/features/quantization.rb +3 -1
  97. data/lib/familia/features/relationships/README.md +3 -1
  98. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  99. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +177 -47
  100. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  101. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +203 -63
  102. data/lib/familia/features/relationships/indexing.rb +40 -42
  103. data/lib/familia/features/relationships/indexing_relationship.rb +17 -5
  104. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  105. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  106. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  107. data/lib/familia/features/relationships/participation.rb +155 -69
  108. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  109. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  110. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  111. data/lib/familia/features/relationships.rb +5 -3
  112. data/lib/familia/features/safe_dump.rb +2 -0
  113. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  114. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  115. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  116. data/lib/familia/features/transient_fields.rb +2 -0
  117. data/lib/familia/features.rb +2 -0
  118. data/lib/familia/field_type.rb +5 -2
  119. data/lib/familia/horreum/connection.rb +28 -36
  120. data/lib/familia/horreum/database_commands.rb +131 -10
  121. data/lib/familia/horreum/definition.rb +18 -7
  122. data/lib/familia/horreum/management.rb +233 -57
  123. data/lib/familia/horreum/persistence.rb +314 -122
  124. data/lib/familia/horreum/related_fields.rb +2 -0
  125. data/lib/familia/horreum/serialization.rb +26 -4
  126. data/lib/familia/horreum/settings.rb +2 -0
  127. data/lib/familia/horreum/utils.rb +2 -8
  128. data/lib/familia/horreum.rb +46 -13
  129. data/lib/familia/identifier_extractor.rb +2 -0
  130. data/lib/familia/instrumentation.rb +156 -0
  131. data/lib/familia/json_serializer.rb +2 -0
  132. data/lib/familia/logging.rb +94 -37
  133. data/lib/familia/refinements/dear_json.rb +2 -0
  134. data/lib/familia/refinements/stylize_words.rb +2 -14
  135. data/lib/familia/refinements/time_literals.rb +2 -0
  136. data/lib/familia/refinements.rb +2 -0
  137. data/lib/familia/secure_identifier.rb +10 -2
  138. data/lib/familia/settings.rb +9 -7
  139. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  140. data/lib/familia/thread_safety/monitor.rb +328 -0
  141. data/lib/familia/utils.rb +13 -0
  142. data/lib/familia/verifiable_identifier.rb +3 -1
  143. data/lib/familia/version.rb +3 -1
  144. data/lib/familia.rb +31 -4
  145. data/lib/middleware/database_command_counter.rb +152 -0
  146. data/lib/middleware/database_logger.rb +325 -129
  147. data/lib/multi_result.rb +2 -0
  148. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  149. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  150. data/try/edge_cases/json_serialization_try.rb +2 -0
  151. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  152. data/try/edge_cases/race_conditions_try.rb +4 -0
  153. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  154. data/try/edge_cases/string_coercion_try.rb +6 -4
  155. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  156. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  157. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  158. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  159. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  160. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  161. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  162. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  163. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  164. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  165. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  166. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  167. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  168. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  169. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  170. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  171. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  172. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  173. data/try/features/encryption/config_persistence_try.rb +4 -0
  174. data/try/features/encryption/core_try.rb +4 -0
  175. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  176. data/try/features/encryption/module_loading_try.rb +4 -0
  177. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  178. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  179. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  180. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  181. data/try/features/expiration/expiration_try.rb +5 -1
  182. data/try/features/external_identifier/external_identifier_try.rb +171 -8
  183. data/try/features/feature_dependencies_try.rb +2 -0
  184. data/try/features/feature_improvements_try.rb +2 -0
  185. data/try/features/field_groups_try.rb +2 -0
  186. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  187. data/try/features/object_identifier/object_identifier_try.rb +2 -0
  188. data/try/features/quantization/quantization_try.rb +4 -0
  189. data/try/features/real_feature_integration_try.rb +2 -0
  190. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  191. data/try/features/relationships/indexing_rebuild_try.rb +600 -0
  192. data/try/features/relationships/indexing_try.rb +30 -4
  193. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  194. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  195. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  196. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  197. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  198. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  199. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  200. data/try/features/relationships/relationships_api_changes_try.rb +6 -4
  201. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  202. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  203. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  204. data/try/features/relationships/relationships_performance_try.rb +4 -0
  205. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  206. data/try/features/relationships/relationships_try.rb +6 -4
  207. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  208. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  209. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  210. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  211. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  212. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  213. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  214. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  215. data/try/integration/connection/fiber_context_preservation_try.rb +7 -3
  216. data/try/integration/connection/handler_constraints_try.rb +4 -0
  217. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  218. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  219. data/try/integration/connection/operation_mode_guards_try.rb +5 -1
  220. data/try/integration/connection/pipeline_fallback_integration_try.rb +15 -12
  221. data/try/integration/connection/pools_try.rb +4 -0
  222. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  223. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  224. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  225. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  226. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  227. data/try/integration/connection/transaction_modes_try.rb +4 -0
  228. data/try/integration/conventional_inheritance_try.rb +4 -0
  229. data/try/integration/create_method_try.rb +26 -22
  230. data/try/integration/cross_component_try.rb +4 -0
  231. data/try/integration/data_types/datatype_pipelines_try.rb +108 -0
  232. data/try/integration/data_types/datatype_transactions_try.rb +251 -0
  233. data/try/integration/database_consistency_try.rb +4 -0
  234. data/try/integration/familia_extended_try.rb +4 -0
  235. data/try/integration/familia_members_methods_try.rb +4 -0
  236. data/try/integration/models/customer_safe_dump_try.rb +9 -1
  237. data/try/integration/models/customer_try.rb +4 -0
  238. data/try/integration/models/datatype_base_try.rb +4 -0
  239. data/try/integration/models/familia_object_try.rb +5 -1
  240. data/try/integration/persistence_operations_try.rb +166 -10
  241. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  242. data/try/integration/save_methods_consistency_try.rb +241 -0
  243. data/try/integration/scenarios_try.rb +4 -0
  244. data/try/integration/secure_identifier_try.rb +4 -0
  245. data/try/integration/transaction_safety_core_try.rb +176 -0
  246. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  247. data/try/integration/verifiable_identifier_try.rb +4 -0
  248. data/try/investigation/pipeline_routing/README.md +228 -0
  249. data/try/performance/benchmarks_try.rb +4 -0
  250. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  251. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  252. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  253. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  254. data/try/support/debugging/debug_aad_process.rb +3 -0
  255. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  256. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  257. data/try/support/debugging/debug_context_aad.rb +3 -0
  258. data/try/support/debugging/debug_context_simple.rb +3 -0
  259. data/try/support/debugging/debug_cross_context.rb +3 -0
  260. data/try/support/debugging/debug_database_load.rb +3 -0
  261. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  262. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  263. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  264. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  265. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  266. data/try/support/debugging/debug_load_path.rb +3 -0
  267. data/try/support/debugging/debug_method_definition.rb +3 -0
  268. data/try/support/debugging/debug_method_resolution.rb +3 -0
  269. data/try/support/debugging/debug_minimal.rb +3 -0
  270. data/try/support/debugging/debug_provider.rb +3 -0
  271. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  272. data/try/support/debugging/debug_string_class.rb +3 -0
  273. data/try/support/debugging/debug_test.rb +3 -0
  274. data/try/support/debugging/debug_test_design.rb +3 -0
  275. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  276. data/try/support/debugging/provider_diagnostics.rb +4 -0
  277. data/try/support/helpers/test_cleanup.rb +4 -0
  278. data/try/support/helpers/test_helpers.rb +5 -0
  279. data/try/support/memory/memory_basic_test.rb +4 -0
  280. data/try/support/memory/memory_detailed_test.rb +4 -0
  281. data/try/support/memory/memory_search_for_string.rb +4 -0
  282. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  283. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  284. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  285. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  286. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  287. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  288. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  289. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  290. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  291. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  292. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  293. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  294. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  295. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  296. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  297. data/try/thread_safety/README.md +496 -0
  298. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  299. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  300. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  301. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  302. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  303. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  304. data/try/thread_safety/field_registration_race_try.rb +222 -0
  305. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  306. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  307. data/try/thread_safety/module_config_race_try.rb +175 -0
  308. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  309. data/try/unit/core/autoloader_try.rb +4 -0
  310. data/try/unit/core/base_enhancements_try.rb +4 -0
  311. data/try/unit/core/connection_try.rb +4 -0
  312. data/try/unit/core/errors_try.rb +4 -0
  313. data/try/unit/core/extensions_try.rb +4 -0
  314. data/try/unit/core/familia_logger_try.rb +2 -0
  315. data/try/unit/core/familia_try.rb +4 -0
  316. data/try/unit/core/middleware_sampling_try.rb +335 -0
  317. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  318. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  319. data/try/unit/core/middleware_try.rb +4 -0
  320. data/try/unit/core/settings_try.rb +4 -0
  321. data/try/unit/core/time_utils_try.rb +4 -0
  322. data/try/unit/core/tools_try.rb +4 -0
  323. data/try/unit/core/utils_try.rb +37 -0
  324. data/try/unit/data_types/boolean_try.rb +5 -1
  325. data/try/unit/data_types/counter_try.rb +4 -0
  326. data/try/unit/data_types/datatype_base_try.rb +4 -0
  327. data/try/unit/data_types/hash_try.rb +4 -0
  328. data/try/unit/data_types/list_try.rb +4 -0
  329. data/try/unit/data_types/lock_try.rb +4 -0
  330. data/try/unit/data_types/sorted_set_try.rb +4 -0
  331. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  332. data/try/unit/data_types/string_try.rb +5 -1
  333. data/try/unit/data_types/unsortedset_try.rb +4 -0
  334. data/try/unit/familia_resolve_class_try.rb +116 -0
  335. data/try/unit/horreum/auto_indexing_on_save_try.rb +36 -16
  336. data/try/unit/horreum/automatic_index_validation_try.rb +255 -0
  337. data/try/unit/horreum/base_try.rb +5 -1
  338. data/try/unit/horreum/class_methods_try.rb +6 -2
  339. data/try/unit/horreum/commands_try.rb +4 -0
  340. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  341. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +4 -0
  342. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  343. data/try/unit/horreum/field_categories_try.rb +4 -0
  344. data/try/unit/horreum/field_definition_try.rb +4 -0
  345. data/try/unit/horreum/initialization_try.rb +5 -1
  346. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  347. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  348. data/try/unit/horreum/relations_try.rb +8 -4
  349. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  350. data/try/unit/horreum/serialization_try.rb +6 -2
  351. data/try/unit/horreum/settings_try.rb +4 -0
  352. data/try/unit/horreum/unique_index_edge_cases_try.rb +380 -0
  353. data/try/unit/horreum/unique_index_guard_validation_try.rb +283 -0
  354. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  355. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  356. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  357. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  358. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  359. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  360. data/try/unit/thread_safety_monitor_try.rb +149 -0
  361. metadata +81 -14
  362. data/.github/workflows/code-quality.yml +0 -138
  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
@@ -1,806 +0,0 @@
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
- ```ruby
14
- FieldType # Base class for all field types
15
- ├── TransientFieldType # Non-persistent fields (memory only)
16
- ├── EncryptedFieldType # Encrypted storage fields
17
- └── Custom field types # User-defined field behaviors
18
- ```
19
-
20
- ### Field Definition Flow
21
-
22
- 1. **Field Declaration**: `field :name, options...`
23
- 2. **FieldType Creation**: Appropriate FieldType instance created
24
- 3. **Registration**: FieldType registered with the class
25
- 4. **Method Installation**: Getter, setter, and fast methods defined
26
- 5. **Runtime Usage**: Methods available on instances
27
-
28
- ## Basic Usage
29
-
30
- ### Simple Field Definition
31
-
32
- ```ruby
33
- class Customer < Familia::Horreum
34
- # Basic field with default settings
35
- field :name
36
-
37
- # Field with custom method name
38
- field :email_address, as: :email
39
-
40
- # Field without accessor methods
41
- field :internal_data, as: false
42
-
43
- # Field without fast writer method
44
- field :readonly_data, fast_method: false
45
- end
46
-
47
- customer = Customer.new
48
- customer.name = "Acme Corp" # Standard setter
49
- customer.email = "admin@acme.com" # Custom method name
50
- customer.name!("Updated Corp") # Fast writer (immediate DB persistence)
51
- ```
52
-
53
- ### Special Field Types
54
-
55
- Use dedicated field methods provided by features for special field behaviors:
56
-
57
- ```ruby
58
- class Document < Familia::Horreum
59
- feature :encrypted_fields
60
- feature :transient_fields
61
-
62
- field :title # Regular persistent field
63
- encrypted_field :content # Encrypted storage
64
- transient_field :api_key # Non-persistent (memory only)
65
- field :tags # Regular field
66
- field :metadata # Regular field
67
- end
68
- ```
69
-
70
- ### Method Conflict Resolution
71
-
72
- The Field System provides multiple strategies for handling method name conflicts:
73
-
74
- ```ruby
75
- class Customer < Familia::Horreum
76
- # Raise error if method exists (default)
77
- field :status, on_conflict: :raise
78
-
79
- # Skip field definition if method exists
80
- field :type, on_conflict: :skip
81
-
82
- # Warn but proceed with definition
83
- field :class, on_conflict: :warn
84
-
85
- # Silently overwrite existing method
86
- field :id, on_conflict: :overwrite
87
- end
88
- ```
89
-
90
- ## Advanced Field Types
91
-
92
- ### Creating Custom Field Types
93
-
94
- ```ruby
95
- # Custom field type for timestamps
96
- class TimestampFieldType < Familia::FieldType
97
- def category
98
- :timestamp
99
- end
100
-
101
- def define_setter(klass)
102
- field_name = @name
103
- method_name = @method_name
104
-
105
- handle_method_conflict(klass, :"#{method_name}=") do
106
- klass.define_method :"#{method_name}=" do |value|
107
- # Convert various formats to Unix timestamp
108
- timestamp = case value
109
- when Time then value.to_i
110
- when String then Time.parse(value).to_i
111
- when Numeric then value.to_i
112
- else raise ArgumentError, "Invalid timestamp: #{value}"
113
- end
114
- instance_variable_set(:"@#{field_name}", timestamp)
115
- end
116
- end
117
- end
118
-
119
- def define_getter(klass)
120
- field_name = @name
121
- method_name = @method_name
122
-
123
- handle_method_conflict(klass, method_name) do
124
- klass.define_method method_name do
125
- timestamp = instance_variable_get(:"@#{field_name}")
126
- timestamp ? Time.at(timestamp) : nil
127
- end
128
- end
129
- end
130
-
131
- def serialize(value, _record = nil)
132
- value.respond_to?(:to_i) ? value.to_i : value
133
- end
134
-
135
- def deserialize(value, _record = nil)
136
- value ? Time.at(value.to_i) : nil
137
- end
138
- end
139
-
140
- # Register and use the custom field type
141
- class Event < Familia::Horreum
142
- def self.timestamp_field(name, **options)
143
- field_type = TimestampFieldType.new(name, **options)
144
- register_field_type(field_type)
145
- end
146
-
147
- identifier_field :event_id
148
- field :event_id, :name, :description
149
- timestamp_field :created_at
150
- timestamp_field :updated_at
151
- end
152
-
153
- # Usage
154
- event = Event.new(event_id: 'evt_123')
155
- event.created_at = "2023-06-15 14:30:00" # String input
156
- puts event.created_at.class # => Time
157
- puts event.created_at # => 2023-06-15 14:30:00 UTC
158
- ```
159
-
160
- ### JSON Field Type
161
-
162
- ```ruby
163
- class JsonFieldType < Familia::FieldType
164
- def category
165
- :json
166
- end
167
-
168
- def define_setter(klass)
169
- field_name = @name
170
- method_name = @method_name
171
-
172
- handle_method_conflict(klass, :"#{method_name}=") do
173
- klass.define_method :"#{method_name}=" do |value|
174
- # Store as parsed JSON for manipulation
175
- parsed_value = case value
176
- when String then JSON.parse(value)
177
- when Hash, Array then value
178
- else raise ArgumentError, "Value must be JSON string, Hash, or Array"
179
- end
180
- instance_variable_set(:"@#{field_name}", parsed_value)
181
- end
182
- end
183
- end
184
-
185
- def serialize(value, _record = nil)
186
- value.to_json if value
187
- end
188
-
189
- def deserialize(value, _record = nil)
190
- value ? JSON.parse(value) : nil
191
- end
192
- end
193
-
194
- class Configuration < Familia::Horreum
195
- def self.json_field(name, **options)
196
- field_type = JsonFieldType.new(name, **options)
197
- register_field_type(field_type)
198
- end
199
-
200
- identifier_field :config_id
201
- field :config_id, :name
202
- json_field :settings
203
- json_field :metadata
204
- end
205
-
206
- # Usage
207
- config = Configuration.new(config_id: 'app_config')
208
- config.settings = { theme: 'dark', notifications: true }
209
- config.settings['api_timeout'] = 30
210
-
211
- # Automatically serialized to JSON in database
212
- config.save
213
- # Database stores: {"theme":"dark","notifications":true,"api_timeout":30}
214
- ```
215
-
216
- ### Enum Field Type
217
-
218
- ```ruby
219
- class EnumFieldType < Familia::FieldType
220
- def initialize(name, values:, **options)
221
- super(name, **options)
222
- @valid_values = values.map(&:to_s).to_set
223
- @default_value = values.first
224
- end
225
-
226
- def category
227
- :enum
228
- end
229
-
230
- def define_setter(klass)
231
- field_name = @name
232
- method_name = @method_name
233
- valid_values = @valid_values
234
-
235
- handle_method_conflict(klass, :"#{method_name}=") do
236
- klass.define_method :"#{method_name}=" do |value|
237
- value_str = value.to_s
238
- unless valid_values.include?(value_str)
239
- raise ArgumentError, "Invalid #{field_name}: #{value}. Valid values: #{valid_values.to_a.join(', ')}"
240
- end
241
- instance_variable_set(:"@#{field_name}", value_str)
242
- end
243
- end
244
- end
245
-
246
- # Add predicate methods for each enum value
247
- def install(klass)
248
- super(klass)
249
-
250
- @valid_values.each do |value|
251
- predicate_method = :"#{@method_name}_#{value}?"
252
- field_name = @name
253
-
254
- klass.define_method predicate_method do
255
- instance_variable_get(:"@#{field_name}") == value
256
- end
257
- end
258
- end
259
- end
260
-
261
- class Order < Familia::Horreum
262
- def self.enum_field(name, values:, **options)
263
- field_type = EnumFieldType.new(name, values: values, **options)
264
- register_field_type(field_type)
265
- end
266
-
267
- identifier_field :order_id
268
- field :order_id, :customer_id
269
- enum_field :status, values: [:pending, :processing, :shipped, :delivered, :cancelled]
270
- enum_field :priority, values: [:low, :normal, :high, :urgent]
271
- end
272
-
273
- # Usage
274
- order = Order.new(order_id: 'ord_123')
275
- order.status = :pending
276
- order.priority = 'high'
277
-
278
- # Predicate methods automatically available
279
- order.status_pending? # => true
280
- order.status_shipped? # => false
281
- order.priority_high? # => true
282
- order.priority_urgent? # => false
283
- ```
284
-
285
- ## Field Metadata and Introspection
286
-
287
- ### Accessing Field Information
288
-
289
- ```ruby
290
- class Product < Familia::Horreum
291
- feature :transient_fields
292
-
293
- field :name
294
- field :price
295
- field :description
296
- transient_field :temp_data
297
- end
298
-
299
- # Get all field names
300
- Product.fields
301
- # => [:name, :price, :description, :temp_data]
302
-
303
- # Get field types registry
304
- Product.field_types
305
- # => { name: #<FieldType...>, price: #<FieldType...>, ... }
306
-
307
- # Get fields by category (read-only introspection)
308
- Product.fields.select { |f| Product.field_types[f].category == :transient }
309
- # => [:temp_data]
310
-
311
- # Get persistent vs transient fields
312
- Product.persistent_fields # => [:name, :price, :description]
313
- Product.transient_fields # => [:temp_data]
314
-
315
- # Field method mapping (for backward compatibility)
316
- Product.field_method_map
317
- # => { name: :name, price: :price, secret_key: :secret_key, temp_data: :temp_data }
318
- ```
319
-
320
- ### Using Field Type Category for Introspection
321
-
322
- The `category` method on FieldType provides read-only metadata for introspection:
323
-
324
- ```ruby
325
- # Custom field type with category metadata
326
- class SearchableFieldType < Familia::FieldType
327
- def category
328
- :searchable
329
- end
330
- end
331
-
332
- # Features can process fields by inspecting their category
333
- module SearchableFieldsFeature
334
- def self.included(base)
335
- base.extend ClassMethods
336
-
337
- # Find all searchable fields by inspecting field type category
338
- searchable_fields = base.fields.select do |field|
339
- base.field_types[field].category == :searchable
340
- end
341
-
342
- searchable_fields.each do |field|
343
- create_search_index_for(base, field)
344
- end
345
- end
346
-
347
- module ClassMethods
348
- def search_by_field(field_name, query)
349
- # Implementation for field-specific search
350
- end
351
- end
352
-
353
- private
354
-
355
- def self.create_search_index_for(klass, field_name)
356
- # Create search index methods
357
- klass.define_singleton_method :"search_by_#{field_name}" do |query|
358
- # Search implementation
359
- end
360
- end
361
- end
362
-
363
- class Product < Familia::Horreum
364
- feature :searchable_fields
365
-
366
- # Use custom field type with searchable category
367
- def self.searchable_field(name, **options)
368
- field_type = SearchableFieldType.new(name, **options)
369
- register_field_type(field_type)
370
- end
371
-
372
- searchable_field :name
373
- searchable_field :description
374
- field :internal_id
375
- end
376
-
377
- # Auto-generated search methods available
378
- Product.search_by_name("laptop")
379
- Product.search_by_description("gaming")
380
- ```
381
-
382
- ## Fast Methods and Database Operations
383
-
384
- ### Fast Method Behavior
385
-
386
- Fast methods provide immediate database persistence without affecting other object state:
387
-
388
- ```ruby
389
- class UserProfile < Familia::Horreum
390
- identifier_field :user_id
391
- field :user_id, :name, :email, :last_login_at
392
- end
393
-
394
- profile = UserProfile.new(user_id: 'user_123')
395
- profile.save
396
-
397
- # Regular setter: updates instance variable only
398
- profile.last_login_at = Familia.now # Not yet in database
399
-
400
- # Fast method: immediate database write
401
- profile.last_login_at!(Familia.now) # Written to database immediately
402
-
403
- # Reading from database
404
- profile.last_login_at # => reads from instance variable
405
- profile.last_login_at! # => reads directly from database
406
- ```
407
-
408
- ### Custom Fast Method Behavior
409
-
410
- ```ruby
411
- class AuditedFieldType < Familia::FieldType
412
- def define_fast_writer(klass)
413
- return unless @fast_method_name
414
-
415
- field_name = @name
416
- method_name = @method_name
417
- fast_method_name = @fast_method_name
418
-
419
- handle_method_conflict(klass, fast_method_name) do
420
- klass.define_method fast_method_name do |*args|
421
- if args.empty?
422
- # Read from database
423
- hget(field_name)
424
- else
425
- # Write to database with audit trail
426
- value = args.first
427
- old_value = hget(field_name)
428
-
429
- # Update the field
430
- prepared = serialize_value(value)
431
- send(:"#{method_name}=", value) if method_name
432
- result = hset(field_name, prepared)
433
-
434
- # Create audit entry
435
- audit_entry = {
436
- field: field_name,
437
- old_value: old_value,
438
- new_value: value,
439
- changed_at: Familia.now,
440
- changed_by: Fiber[:current_user]&.id
441
- }
442
-
443
- # Store audit trail
444
- audit_key = "#{dbkey}:audit"
445
- Familia.dbclient.lpush(audit_key, audit_entry.to_json)
446
- Familia.dbclient.ltrim(audit_key, 0, 99) # Keep last 100 changes
447
-
448
- result
449
- end
450
- end
451
- end
452
- end
453
- end
454
-
455
- class AuditedDocument < Familia::Horreum
456
- def self.audited_field(name, **options)
457
- field_type = AuditedFieldType.new(name, **options)
458
- register_field_type(field_type)
459
- end
460
-
461
- identifier_field :doc_id
462
- field :doc_id, :title
463
- audited_field :content
464
- audited_field :status
465
- end
466
-
467
- # Usage creates audit trail
468
- doc = AuditedDocument.new(doc_id: 'doc_123')
469
- doc.save
470
-
471
- Fiber[:current_user] = OpenStruct.new(id: 'user_456')
472
- doc.content!("Initial content") # Audited change
473
- doc.status!("draft") # Audited change
474
-
475
- # View audit trail
476
- audit_key = "#{doc.dbkey}:audit"
477
- audit_entries = Familia.dbclient.lrange(audit_key, 0, -1)
478
- audit_entries.map { |entry| JSON.parse(entry) }
479
- ```
480
-
481
- ## Integration Patterns
482
-
483
- ### Rails Integration
484
-
485
- ```ruby
486
- # app/models/concerns/familia_fields.rb
487
- module FamiliaFields
488
- extend ActiveSupport::Concern
489
-
490
- class_methods do
491
- # Rails-style field definitions
492
- def string_field(name, **options)
493
- field(name, **options)
494
- end
495
-
496
- def integer_field(name, **options)
497
- field_type = Class.new(Familia::FieldType) do
498
- def serialize(value, _record = nil)
499
- value.to_i if value
500
- end
501
-
502
- def deserialize(value, _record = nil)
503
- value.to_i if value
504
- end
505
- end
506
-
507
- register_field_type(field_type.new(name, **options))
508
- end
509
-
510
- def boolean_field(name, **options)
511
- field_type = Class.new(Familia::FieldType) do
512
- def serialize(value, _record = nil)
513
- !!value
514
- end
515
-
516
- def deserialize(value, _record = nil)
517
- value == true || value == 'true' || value == '1'
518
- end
519
-
520
- def define_getter(klass)
521
- super(klass)
522
-
523
- # Add predicate method
524
- predicate_method = :"#{@method_name}?"
525
- field_name = @name
526
-
527
- klass.define_method predicate_method do
528
- !!instance_variable_get(:"@#{field_name}")
529
- end
530
- end
531
- end
532
-
533
- register_field_type(field_type.new(name, **options))
534
- end
535
- end
536
- end
537
-
538
- class User < Familia::Horreum
539
- include FamiliaFields
540
-
541
- identifier_field :user_id
542
- string_field :user_id, :email, :name
543
- integer_field :age, :login_count
544
- boolean_field :active, :verified
545
- end
546
-
547
- user = User.new(user_id: 'user_123')
548
- user.age = "25" # Automatically converted to integer
549
- user.active = "true" # Automatically converted to boolean
550
- user.verified? # => false (predicate method)
551
- ```
552
-
553
- ### Validation Integration
554
-
555
- ```ruby
556
- class ValidatedFieldType < Familia::FieldType
557
- def initialize(name, validations: {}, **options)
558
- super(name, **options)
559
- @validations = validations
560
- end
561
-
562
- def define_setter(klass)
563
- field_name = @name
564
- method_name = @method_name
565
- validations = @validations
566
-
567
- handle_method_conflict(klass, :"#{method_name}=") do
568
- klass.define_method :"#{method_name}=" do |value|
569
- # Run validations
570
- validations.each do |validator, constraint|
571
- case validator
572
- when :presence
573
- if constraint && (value.nil? || value.to_s.strip.empty?)
574
- raise ArgumentError, "#{field_name} cannot be blank"
575
- end
576
- when :length
577
- if constraint.is_a?(Hash) && constraint[:minimum]
578
- if value.to_s.length < constraint[:minimum]
579
- raise ArgumentError, "#{field_name} is too short (minimum #{constraint[:minimum]} characters)"
580
- end
581
- end
582
- when :format
583
- if constraint.is_a?(Regexp) && !value.to_s.match?(constraint)
584
- raise ArgumentError, "#{field_name} format is invalid"
585
- end
586
- when :inclusion
587
- if constraint.is_a?(Array) && !constraint.include?(value)
588
- raise ArgumentError, "#{field_name} must be one of: #{constraint.join(', ')}"
589
- end
590
- end
591
- end
592
-
593
- instance_variable_set(:"@#{field_name}", value)
594
- end
595
- end
596
- end
597
- end
598
-
599
- class User < Familia::Horreum
600
- def self.validated_field(name, validations: {}, **options)
601
- field_type = ValidatedFieldType.new(name, validations: validations, **options)
602
- register_field_type(field_type)
603
- end
604
-
605
- identifier_field :user_id
606
- validated_field :user_id, validations: { presence: true }
607
- validated_field :email, validations: {
608
- presence: true,
609
- format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
610
- }
611
- validated_field :status, validations: {
612
- inclusion: %w[active inactive suspended]
613
- }
614
- validated_field :name, validations: {
615
- presence: true,
616
- length: { minimum: 2 }
617
- }
618
- end
619
-
620
- # Usage with validation
621
- user = User.new
622
- user.email = "invalid-email" # Raises ArgumentError
623
- user.status = "unknown" # Raises ArgumentError
624
- user.name = "A" # Raises ArgumentError (too short)
625
- ```
626
-
627
- ## Performance Considerations
628
-
629
- ### Efficient Field Operations
630
-
631
- ```ruby
632
- class OptimizedFieldAccess < Familia::Horreum
633
- # Cache field type lookups
634
- def self.field_type_for(field_name)
635
- @field_type_cache ||= {}
636
- @field_type_cache[field_name] ||= field_types[field_name]
637
- end
638
-
639
- # Batch field updates
640
- def batch_update(field_values)
641
- # Update instance variables
642
- field_values.each do |field, value|
643
- setter_method = :"#{field}="
644
- send(setter_method, value) if respond_to?(setter_method)
645
- end
646
-
647
- # Single database call for persistence
648
- serialized_values = field_values.transform_values do |value|
649
- serialize_value(value)
650
- end
651
-
652
- hmset(serialized_values)
653
- end
654
-
655
- # Lazy field loading
656
- def lazy_load_field(field_name)
657
- return instance_variable_get(:"@#{field_name}") if instance_variable_defined?(:"@#{field_name}")
658
-
659
- value = hget(field_name)
660
- field_type = self.class.field_type_for(field_name)
661
- deserialized = field_type&.deserialize(value, self) || value
662
-
663
- instance_variable_set(:"@#{field_name}", deserialized)
664
- deserialized
665
- end
666
- end
667
- ```
668
-
669
- ### Memory-Efficient Field Storage
670
-
671
- ```ruby
672
- class CompactFieldType < Familia::FieldType
673
- def serialize(value, _record = nil)
674
- case value
675
- when String
676
- # Compress strings longer than 100 characters
677
- if value.length > 100
678
- Base64.encode64(Zlib::Deflate.deflate(value))
679
- else
680
- value
681
- end
682
- else
683
- value
684
- end
685
- end
686
-
687
- def deserialize(value, _record = nil)
688
- return value unless value.is_a?(String)
689
-
690
- # Check if it's base64 encoded compressed data
691
- if value.length > 100 && value.match?(/\A[A-Za-z0-9+\/]*={0,2}\z/)
692
- begin
693
- Zlib::Inflate.inflate(Base64.decode64(value))
694
- rescue
695
- value # Return as-is if decompression fails
696
- end
697
- else
698
- value
699
- end
700
- end
701
- end
702
- ```
703
-
704
- ## Testing Field Types
705
-
706
- ### RSpec Testing
707
-
708
- ```ruby
709
- RSpec.describe TimestampFieldType do
710
- let(:field_type) { described_class.new(:created_at) }
711
- let(:test_class) do
712
- Class.new(Familia::Horreum) do
713
- def self.name; 'TestClass'; end
714
- end
715
- end
716
-
717
- before do
718
- field_type.install(test_class)
719
- end
720
-
721
- it "converts various time formats" do
722
- instance = test_class.new
723
-
724
- instance.created_at = "2023-06-15 14:30:00"
725
- expect(instance.created_at).to be_a(Time)
726
-
727
- instance.created_at = Familia.now
728
- expect(instance.created_at).to be_a(Time)
729
-
730
- instance.created_at = Familia.now.to_i
731
- expect(instance.created_at).to be_a(Time)
732
- end
733
-
734
- it "serializes to integer" do
735
- time_value = Familia.now
736
- serialized = field_type.serialize(time_value)
737
- expect(serialized).to be_a(Integer)
738
- expect(serialized).to eq(time_value.to_i)
739
- end
740
-
741
- it "deserializes from integer" do
742
- timestamp = Familia.now.to_i
743
- deserialized = field_type.deserialize(timestamp)
744
- expect(deserialized).to be_a(Time)
745
- expect(deserialized.to_i).to eq(timestamp)
746
- end
747
- end
748
- ```
749
-
750
- ## Best Practices
751
-
752
- ### 1. Choose Appropriate Field Types
753
-
754
- ```ruby
755
- # Use dedicated field methods provided by features
756
- class User < Familia::Horreum
757
- feature :transient_fields
758
- feature :encrypted_fields
759
-
760
- field :name # Simple persistent field
761
- field :metadata # For complex data
762
- transient_field :temp_token # For runtime-only data
763
- encrypted_field :api_key # For sensitive data
764
- end
765
-
766
- # Create custom types for specialized behavior
767
- class GeoLocation < Familia::Horreum
768
- coordinate_field :latitude # Custom validation and formatting
769
- coordinate_field :longitude
770
- end
771
- ```
772
-
773
- ### 2. Handle Method Conflicts Gracefully
774
-
775
- ```ruby
776
- class SafeFieldDefinition < Familia::Horreum
777
- # Check for conflicts before defining fields
778
- def self.safe_field(name, **options)
779
- if method_defined?(name) || method_defined?(:"#{name}=")
780
- Rails.logger.warn "Method conflict for field #{name}, using alternative name"
781
- options[:as] = :"#{name}_value"
782
- end
783
-
784
- field(name, **options)
785
- end
786
- end
787
- ```
788
-
789
- ### 3. Optimize for Common Use Cases
790
-
791
- ```ruby
792
- # Provide convenience methods for common patterns
793
- class BaseModel < Familia::Horreum
794
- def self.timestamps
795
- timestamp_field :created_at, as: :created_at
796
- timestamp_field :updated_at, as: :updated_at
797
- end
798
-
799
- def self.soft_delete
800
- boolean_field :deleted, as: :deleted
801
- timestamp_field :deleted_at, as: :deleted_at
802
- end
803
- end
804
- ```
805
-
806
- The Field System provides a powerful foundation for defining flexible, extensible object attributes with customizable behavior, validation, and serialization capabilities.