familia 2.0.0.pre14 → 2.0.0.pre16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/code-quality.yml +138 -0
  3. data/.github/workflows/code-smellage.yml +145 -0
  4. data/.github/workflows/docs.yml +31 -8
  5. data/.gitignore +1 -1
  6. data/.pre-commit-config.yaml +7 -1
  7. data/.reek.yml +98 -0
  8. data/.rubocop.yml +48 -10
  9. data/.talismanrc +9 -0
  10. data/.yardopts +18 -13
  11. data/CHANGELOG.rst +66 -6
  12. data/CLAUDE.md +1 -1
  13. data/Gemfile +6 -5
  14. data/Gemfile.lock +99 -23
  15. data/LICENSE.txt +1 -1
  16. data/README.md +285 -85
  17. data/changelog.d/README.md +2 -2
  18. data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
  19. data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
  20. data/docs/archive/FAMILIA_UPDATE.md +3 -3
  21. data/docs/archive/README.md +3 -2
  22. data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
  23. data/docs/conf.py +29 -0
  24. data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
  25. data/docs/guides/feature-encrypted-fields.md +785 -0
  26. data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
  27. data/docs/guides/feature-external-identifiers.md +637 -0
  28. data/docs/guides/feature-object-identifiers.md +435 -0
  29. data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
  30. data/docs/guides/feature-relationships-methods.md +684 -0
  31. data/docs/guides/feature-relationships.md +200 -0
  32. data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
  33. data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
  34. data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
  35. data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
  36. data/docs/guides/index.md +176 -0
  37. data/docs/guides/{Security-Model.md → security-model.md} +1 -1
  38. data/docs/migrating/v2.0.0-pre.md +1 -1
  39. data/docs/migrating/v2.0.0-pre11.md +4 -4
  40. data/docs/migrating/v2.0.0-pre12.md +2 -2
  41. data/docs/migrating/v2.0.0-pre13.md +1 -1
  42. data/docs/migrating/v2.0.0-pre5.md +33 -12
  43. data/docs/migrating/v2.0.0-pre6.md +2 -2
  44. data/docs/migrating/v2.0.0-pre7.md +8 -8
  45. data/docs/overview.md +623 -19
  46. data/docs/reference/api-technical.md +1365 -0
  47. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  48. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  49. data/examples/autoloader/mega_customer.rb +3 -1
  50. data/examples/encrypted_fields.rb +378 -0
  51. data/examples/json_usage_patterns.rb +144 -0
  52. data/examples/relationships.rb +13 -13
  53. data/examples/safe_dump.rb +6 -6
  54. data/examples/single_connection_transaction_confusions.rb +379 -0
  55. data/lib/familia/base.rb +49 -10
  56. data/lib/familia/connection/handlers.rb +223 -0
  57. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  58. data/lib/familia/connection/middleware.rb +75 -0
  59. data/lib/familia/connection/operation_core.rb +93 -0
  60. data/lib/familia/connection/operations.rb +277 -0
  61. data/lib/familia/connection/pipeline_core.rb +87 -0
  62. data/lib/familia/connection/transaction_core.rb +100 -0
  63. data/lib/familia/connection.rb +60 -186
  64. data/lib/familia/data_type/commands.rb +53 -51
  65. data/lib/familia/data_type/serialization.rb +108 -107
  66. data/lib/familia/data_type/types/counter.rb +1 -1
  67. data/lib/familia/data_type/types/hashkey.rb +13 -10
  68. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  69. data/lib/familia/data_type/types/lock.rb +3 -2
  70. data/lib/familia/data_type/types/sorted_set.rb +26 -15
  71. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
  72. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  73. data/lib/familia/data_type.rb +75 -47
  74. data/lib/familia/distinguisher.rb +85 -0
  75. data/lib/familia/encryption/encrypted_data.rb +15 -24
  76. data/lib/familia/encryption/manager.rb +6 -4
  77. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  78. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  79. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  80. data/lib/familia/encryption/request_cache.rb +7 -7
  81. data/lib/familia/encryption.rb +2 -3
  82. data/lib/familia/errors.rb +9 -3
  83. data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
  84. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  85. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  86. data/lib/familia/features/encrypted_fields.rb +68 -66
  87. data/lib/familia/features/expiration/extensions.rb +61 -0
  88. data/lib/familia/features/expiration.rb +35 -87
  89. data/lib/familia/features/external_identifier.rb +11 -12
  90. data/lib/familia/features/object_identifier.rb +58 -20
  91. data/lib/familia/features/quantization.rb +17 -22
  92. data/lib/familia/features/relationships/README.md +97 -0
  93. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  94. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  95. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
  96. data/lib/familia/features/relationships/indexing.rb +176 -256
  97. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  98. data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  100. data/lib/familia/features/relationships/participation.rb +656 -0
  101. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  102. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  103. data/lib/familia/features/relationships.rb +69 -271
  104. data/lib/familia/features/safe_dump.rb +127 -132
  105. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  106. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  107. data/lib/familia/features/transient_fields.rb +5 -5
  108. data/lib/familia/features.rb +21 -21
  109. data/lib/familia/field_type.rb +24 -4
  110. data/lib/familia/horreum/core/connection.rb +229 -26
  111. data/lib/familia/horreum/core/database_commands.rb +27 -17
  112. data/lib/familia/horreum/core/serialization.rb +40 -20
  113. data/lib/familia/horreum/core/utils.rb +2 -1
  114. data/lib/familia/horreum/shared/settings.rb +2 -1
  115. data/lib/familia/horreum/subclass/definition.rb +33 -45
  116. data/lib/familia/horreum/subclass/management.rb +72 -24
  117. data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
  118. data/lib/familia/horreum.rb +196 -114
  119. data/lib/familia/json_serializer.rb +0 -1
  120. data/lib/familia/logging.rb +11 -114
  121. data/lib/familia/refinements/dear_json.rb +122 -0
  122. data/lib/familia/refinements/logger_trace.rb +20 -17
  123. data/lib/familia/refinements/stylize_words.rb +65 -0
  124. data/lib/familia/refinements/time_literals.rb +60 -52
  125. data/lib/familia/refinements.rb +2 -1
  126. data/lib/familia/secure_identifier.rb +60 -28
  127. data/lib/familia/settings.rb +83 -7
  128. data/lib/familia/utils.rb +5 -87
  129. data/lib/familia/verifiable_identifier.rb +4 -4
  130. data/lib/familia/version.rb +1 -1
  131. data/lib/familia.rb +72 -15
  132. data/lib/middleware/database_middleware.rb +56 -14
  133. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  134. data/try/configuration/scenarios_try.rb +1 -1
  135. data/try/connection/fiber_context_preservation_try.rb +250 -0
  136. data/try/connection/handler_constraints_try.rb +59 -0
  137. data/try/connection/operation_mode_guards_try.rb +208 -0
  138. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  139. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  140. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  141. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  142. data/try/connection/transaction_mode_strict_try.rb +98 -0
  143. data/try/connection/transaction_mode_warn_try.rb +131 -0
  144. data/try/connection/transaction_modes_try.rb +249 -0
  145. data/try/core/autoloader_try.rb +129 -11
  146. data/try/core/connection_try.rb +7 -7
  147. data/try/core/conventional_inheritance_try.rb +130 -0
  148. data/try/core/create_method_try.rb +15 -23
  149. data/try/core/database_consistency_try.rb +10 -10
  150. data/try/core/errors_try.rb +8 -11
  151. data/try/core/familia_extended_try.rb +2 -2
  152. data/try/core/familia_members_methods_try.rb +76 -0
  153. data/try/core/isolated_dbclient_try.rb +165 -0
  154. data/try/core/middleware_try.rb +16 -16
  155. data/try/core/persistence_operations_try.rb +4 -4
  156. data/try/core/pools_try.rb +42 -26
  157. data/try/core/secure_identifier_try.rb +28 -24
  158. data/try/core/time_utils_try.rb +10 -10
  159. data/try/core/tools_try.rb +1 -1
  160. data/try/core/utils_try.rb +2 -2
  161. data/try/data_types/boolean_try.rb +4 -4
  162. data/try/data_types/datatype_base_try.rb +0 -2
  163. data/try/data_types/list_try.rb +10 -10
  164. data/try/data_types/sorted_set_try.rb +5 -5
  165. data/try/data_types/string_try.rb +12 -12
  166. data/try/data_types/unsortedset_try.rb +33 -0
  167. data/try/debugging/cache_behavior_tracer.rb +7 -7
  168. data/try/debugging/debug_aad_process.rb +1 -1
  169. data/try/debugging/debug_concealed_internal.rb +1 -1
  170. data/try/debugging/debug_cross_context.rb +1 -1
  171. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  172. data/try/debugging/encryption_method_tracer.rb +10 -10
  173. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  174. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  175. data/try/encryption/config_persistence_try.rb +2 -2
  176. data/try/encryption/encryption_core_try.rb +19 -19
  177. data/try/encryption/instance_variable_scope_try.rb +1 -1
  178. data/try/encryption/module_loading_try.rb +2 -2
  179. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  180. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  181. data/try/encryption/secure_memory_handling_try.rb +1 -1
  182. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  183. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  184. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  185. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  186. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  187. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  188. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  189. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  190. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  191. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  192. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  193. data/try/features/feature_dependencies_try.rb +3 -3
  194. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  195. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  196. data/try/features/quantization/quantization_try.rb +1 -1
  197. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  198. data/try/features/relationships/indexing_try.rb +433 -0
  199. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  200. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  201. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  202. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  203. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  204. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  205. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  206. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  207. data/try/features/relationships/relationships_performance_try.rb +20 -20
  208. data/try/features/relationships/relationships_try.rb +27 -38
  209. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  210. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  211. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  212. data/try/helpers/test_cleanup.rb +86 -0
  213. data/try/helpers/test_helpers.rb +3 -3
  214. data/try/horreum/base_try.rb +3 -2
  215. data/try/horreum/commands_try.rb +1 -1
  216. data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
  217. data/try/horreum/initialization_try.rb +11 -7
  218. data/try/horreum/relations_try.rb +21 -13
  219. data/try/horreum/serialization_try.rb +12 -11
  220. data/try/integration/cross_component_try.rb +3 -3
  221. data/try/memory/memory_basic_test.rb +1 -1
  222. data/try/memory/memory_docker_ruby_dump.sh +1 -1
  223. data/try/models/customer_safe_dump_try.rb +1 -1
  224. data/try/models/customer_try.rb +8 -10
  225. data/try/models/datatype_base_try.rb +3 -3
  226. data/try/models/familia_object_try.rb +9 -8
  227. data/try/performance/benchmarks_try.rb +2 -2
  228. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  229. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  230. data/try/prototypes/atomic_saves_v4.rb +1 -1
  231. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  232. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  233. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  234. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  235. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  236. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  237. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  238. data/try/prototypes/pooling/pool_siege.rb +11 -11
  239. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  240. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  241. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  242. data/try/refinements/logger_trace_methods_try.rb +44 -0
  243. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  244. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  245. metadata +77 -45
  246. data/.rubocop_todo.yml +0 -208
  247. data/docs/connection_pooling.md +0 -192
  248. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  249. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  250. data/docs/guides/Feature-System-Autoloading.md +0 -228
  251. data/docs/guides/Home.md +0 -116
  252. data/docs/guides/Relationships-Guide.md +0 -737
  253. data/docs/guides/relationships-methods.md +0 -266
  254. data/docs/reference/auditing_database_commands.rb +0 -228
  255. data/examples/permissions.rb +0 -240
  256. data/lib/familia/features/autoloadable.rb +0 -113
  257. data/lib/familia/features/relationships/cascading.rb +0 -437
  258. data/lib/familia/features/relationships/membership.rb +0 -497
  259. data/lib/familia/features/relationships/permission_management.rb +0 -264
  260. data/lib/familia/features/relationships/querying.rb +0 -615
  261. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  262. data/lib/familia/features/relationships/tracking.rb +0 -418
  263. data/lib/familia/refinements/snake_case.rb +0 -40
  264. data/lib/familia/validation/command_recorder.rb +0 -336
  265. data/lib/familia/validation/expectations.rb +0 -519
  266. data/lib/familia/validation/validation_helpers.rb +0 -443
  267. data/lib/familia/validation/validator.rb +0 -412
  268. data/lib/familia/validation.rb +0 -140
  269. data/try/data_types/set_try.rb +0 -33
  270. data/try/features/autoloadable/autoloadable_try.rb +0 -61
  271. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  272. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
  273. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  274. data/try/validation/command_validation_try.rb.disabled +0 -207
  275. data/try/validation/performance_validation_try.rb.disabled +0 -324
  276. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
data/docs/overview.md CHANGED
@@ -2,7 +2,7 @@
2
2
  # Familia - Overview
3
3
 
4
4
  > [!NOTE]
5
- > This document refers to Valkey throughout, but all examples and patterns work identically with Redis. Familia supports both Valkey and Redis as they share the same protocol and data structures.
5
+ > This document refers to Valkey throughout, but all examples and patterns work identically with Valkey/Redis. Familia supports both Valkey and Valkey/Redis as they share the same protocol and data structures.
6
6
 
7
7
  ## Introduction
8
8
 
@@ -11,13 +11,14 @@ Familia is a Ruby ORM for Valkey (Redis) that provides object-oriented access to
11
11
  **Why Familia?**
12
12
  - Maps Ruby objects directly to Valkey's native data structures (strings, lists, sets, etc.)
13
13
  - Maintains Valkey's atomic operations and performance characteristics
14
- - Handles complex patterns (quantization, encryption, expiration) out of the box
14
+ - Handles complex patterns (quantization, encryption, expiration, relationships) out of the box
15
+ - Modular feature system for organizing functionality across complex projects
15
16
 
16
17
  ## Core Concepts
17
18
 
18
19
  ### What is a Horreum Class?
19
20
 
20
- The ```Horreum``` class is Familia's foundation, representing Valkey-compatible objects. It's named after ancient Roman storehouses, reflecting its purpose as a structured data repository.
21
+ The `Horreum` class is Familia's foundation, representing Valkey-compatible objects. It's named after ancient Roman storehouses, reflecting its purpose as a structured data repository.
21
22
 
22
23
  ```ruby
23
24
  class Flower < Familia::Horreum
@@ -95,6 +96,82 @@ end
95
96
 
96
97
  Each type maintains Valkey's native operations while providing Ruby-friendly interfaces.
97
98
 
99
+ ### DataType Naming Options
100
+
101
+ Familia provides both traditional concise names and explicit names for DataType methods to avoid namespace confusion with Ruby core types:
102
+
103
+ ```ruby
104
+ class Product < Familia::Horreum
105
+ # Traditional naming (concise, safe for lowercase)
106
+ string :view_count # Creates StringKey instance
107
+ list :categories # Creates ListKey instance
108
+
109
+ # Explicit naming (clear intent, namespace-safe)
110
+ stringkey :description # Creates StringKey instance
111
+ listkey :history # Creates ListKey instance
112
+
113
+ # Both work identically - choose based on preference
114
+ set :tags # UnsortedSet (unchanged)
115
+ sorted_set :ratings # SortedSet (unchanged)
116
+ hashkey :attributes # HashKey (unchanged)
117
+ end
118
+
119
+ # Access patterns are identical
120
+ product.view_count.class # => Familia::StringKey
121
+ product.description.class # => Familia::StringKey
122
+ product.categories.class # => Familia::ListKey
123
+ product.history.class # => Familia::ListKey
124
+ ```
125
+
126
+ **Key Benefits:**
127
+ - **Developer Choice**: Use concise (`string`, `list`) or explicit (`stringkey`, `listkey`) method names
128
+ - **Namespace Safety**: No confusion with Ruby's core `String`, `Array`, `Set`, `Hash` types
129
+ - **Backward Compatibility**: All existing code continues to work unchanged
130
+ - **Future-Proof**: Clear naming convention for any new DataTypes
131
+
132
+ ### Generated Method Patterns
133
+
134
+ Familia automatically generates methods for all field and data type declarations:
135
+
136
+ ```ruby
137
+ class Product < Familia::Horreum
138
+ # Field declarations generate three methods each
139
+ field :name, :price
140
+ # → name, name=, name! # getter, setter, fast writer
141
+ # → price, price=, price! # getter, setter, fast writer
142
+
143
+ # Data type declarations generate accessor, setter, and type check
144
+ set :tags
145
+ list :categories # Traditional method
146
+ listkey :search_history # Explicit method (same functionality)
147
+ hashkey :attributes
148
+ # → tags, tags=, tags? # accessor, setter, type check
149
+ # → categories, categories=, categories?
150
+ # → search_history, search_history=, search_history?
151
+ # → attributes, attributes=, attributes?
152
+
153
+ # Class-level data types
154
+ class_set :global_tags
155
+ class_counter :total_products
156
+ # → Product.global_tags, Product.global_tags?
157
+ # → Product.total_products, Product.total_products?
158
+ end
159
+
160
+ # Field method usage
161
+ product.name = "Ruby Gem" # Set field value
162
+ product.name # Get field value
163
+ product.name!("New Name") # Set and save immediately
164
+
165
+ # Data type method usage
166
+ product.tags # Get UnsortedSet instance
167
+ product.tags = new_set # Replace UnsortedSet instance
168
+ product.tags? # => true (confirms it's an UnsortedSet)
169
+
170
+ # Method conflict resolution
171
+ field :type, on_conflict: :skip # Skip if method exists
172
+ field :id, on_conflict: :overwrite # Force overwrite existing method
173
+ ```
174
+
98
175
  ## Essential Features
99
176
 
100
177
  ### Automatic Expiration
@@ -133,7 +210,7 @@ class User < Familia::Horreum
133
210
  end
134
211
 
135
212
  user.safe_dump
136
- #=> {id: "123", email: "alice@example.com", full_name: "Alice Smith"}
213
+ # => {id: "123", email: "alice@example.com", full_name: "Alice Windows"}
137
214
  ```
138
215
 
139
216
  The new DSL prevents accidental exposure of sensitive data and makes field definitions easier to organize in feature modules.
@@ -151,6 +228,292 @@ end
151
228
 
152
229
  This automatically groups metrics into 10-minute intervals formatted as "HH:MM", ideal for analytics dashboards.
153
230
 
231
+ **Key Benefits:**
232
+ - **Time Bucketing**: Group time-based data into configurable intervals (minutes, hours, days)
233
+ - **Reduced Storage**: Aggregate similar data points to optimize memory usage
234
+ - **Analytics Ready**: Perfect for dashboards and time-series data visualization
235
+
236
+ > For advanced quantization strategies, value bucketing, geographic quantization, and performance patterns, see the [Technical Reference](reference/api-technical.md#quantization-feature-v200-pre7).
237
+
238
+ ### Object Identifiers
239
+
240
+ Automatically generate unique identifiers for objects:
241
+
242
+ ```ruby
243
+ class Document < Familia::Horreum
244
+ feature :object_identifier, generator: :uuid_v4
245
+ field :title
246
+ field :content
247
+ end
248
+
249
+ class Session < Familia::Horreum
250
+ feature :object_identifier, generator: :hex
251
+ field :user_id
252
+ field :data
253
+ end
254
+
255
+ # Objects get automatic IDs
256
+ doc = Document.create(title: "My Doc")
257
+ doc.objid # => "550e8400-e29b-41d4-a716-446655440000" (UUID)
258
+
259
+ session = Session.create(user_id: "123")
260
+ session.objid # => "a1b2c3d4e5f6" (hex)
261
+ ```
262
+
263
+ **Available Generators:**
264
+ - `:uuid_v4` - Standard UUID format for global uniqueness
265
+ - `:hex` - Compact hexadecimal identifiers for internal use
266
+
267
+ > For custom generators, collision detection, and advanced identifier patterns, see the [Technical Reference](reference/api-technical.md#object-identifier-feature-v200-pre7).
268
+
269
+ ### Specialized Field Types
270
+
271
+ Familia provides specialized field types beyond basic fields:
272
+
273
+ ```ruby
274
+ class SecureModel < Familia::Horreum
275
+ feature :encrypted_fields
276
+ feature :transient_fields
277
+ feature :object_identifier
278
+
279
+ # Regular fields generate: name, name=, name!
280
+ field :name, :email
281
+
282
+ # Encrypted fields return ConcealedString instances
283
+ encrypted_field :api_key, :credit_card
284
+ # → api_key, api_key=, api_key!
285
+ # → Values wrapped in ConcealedString for safety
286
+
287
+ # Transient fields never persist to database
288
+ transient_field :password, :session_token
289
+ # → password, password= (no fast writer method)
290
+ # → Values wrapped in RedactedString
291
+
292
+ # Redacted fields are persisted but return [REDACTED] in logs
293
+ redacted_field :security_question
294
+ # → security_question, security_question=, security_question!
295
+
296
+ # Object identifier fields auto-generate unique IDs
297
+ # → objid, objid= (lazy generation, preserves initialization values)
298
+ # → objid_generator_used (provenance tracking)
299
+ end
300
+
301
+ # Usage examples
302
+ model = SecureModel.create(name: "Alice", api_key: "secret123")
303
+
304
+ # Encrypted field safety
305
+ model.api_key.class # => ConcealedString
306
+ model.api_key.to_s # => "[CONCEALED]" (safe for logs)
307
+ model.api_key.reveal # => "secret123" (actual value)
308
+
309
+ # Transient field behavior
310
+ model.password = "temp123"
311
+ model.save
312
+ model.reload
313
+ model.password # => nil (not persisted)
314
+
315
+ # Object identifier generation
316
+ model.objid # => Auto-generated UUID or hex
317
+ model.objid_generator_used # => :uuid_v7 (provenance)
318
+ ```
319
+
320
+ **Field Type Features:**
321
+ - **Method Conflict Resolution**: Use `on_conflict: :skip/:warn/:overwrite` for existing methods
322
+ - **Fast Writer Control**: Use `fast_method: false` to disable fast writers
323
+ - **Custom Method Names**: Use `as: :custom_name` for different method names
324
+ - **Security by Default**: Encrypted and transient fields prevent accidental exposure
325
+
326
+ ### External Identifiers
327
+
328
+ Integrate with external systems and validate identifiers:
329
+
330
+ ```ruby
331
+ class ExternalUser < Familia::Horreum
332
+ feature :external_identifier
333
+
334
+ field :external_id
335
+ field :name
336
+ field :sync_status
337
+
338
+ # Validate external system identifiers
339
+ def valid_external_id?
340
+ external_id.present? && external_id.match?(/^ext_\d+$/)
341
+ end
342
+ end
343
+
344
+ # Map external identifiers to internal objects
345
+ user = ExternalUser.create(external_id: "ext_12345", name: "Alice")
346
+ ```
347
+
348
+ This feature helps maintain consistency when integrating with external APIs or legacy systems.
349
+
350
+ > For advanced external identifier patterns, batch operations, and sync status management, see the [Technical Reference](reference/api-technical.md#external-identifier-feature-v200-pre7).
351
+
352
+ ### Relationships
353
+
354
+ Manage complex object relationships with CRUD operations:
355
+
356
+ ```ruby
357
+
358
+ # Define relationships
359
+ class User < Familia::Horreum
360
+ feature :relationships
361
+ identifier_field :email
362
+ field :email, :name
363
+
364
+ participates_in Team, :teams
365
+ end
366
+
367
+ class Team < Familia::Horreum
368
+ feature :relationships
369
+ identifier_field :name
370
+ field :name, :description
371
+ end
372
+
373
+ # Create relationships
374
+ alice = User.create(email: "alice@example.com", name: "Alice")
375
+ dev_team = Team.create(name: "developers", description: "Dev Team")
376
+
377
+ # Add relationships
378
+ alice.teams << dev_team
379
+
380
+ # Query relationships
381
+ alice.teams.to_a # => [dev_team identifiers]
382
+ dev_team.in_user_teams?(alice) # => true
383
+
384
+ # Remove relationships
385
+ alice.teams.delete(dev_team.identifier)
386
+
387
+ # Bulk operations
388
+ alice.teams.merge([qa_team.identifier, design_team.identifier])
389
+ alice.teams.clear
390
+ ```
391
+
392
+ #### Generated Relationship Method Patterns
393
+
394
+ The relationships feature automatically generates comprehensive method patterns:
395
+
396
+ ```ruby
397
+ # Participation methods (on User class)
398
+ alice.in_team_teams?(dev_team) # Check membership
399
+ alice.add_to_team_teams(dev_team, 1.0) # Add with score
400
+ alice.remove_from_team_teams(dev_team) # Remove membership
401
+ alice.score_in_team_teams(dev_team) # Get participation score
402
+
403
+ # Target class methods (on Team class)
404
+ dev_team.teams # Collection getter (SortedSet)
405
+ dev_team.add_team(alice, 1.0) # Add single member with score
406
+ dev_team.remove_team(alice) # Remove single member
407
+ dev_team.add_teams([alice, bob]) # Bulk add members
408
+
409
+ # Indexing methods (if using indexed_by)
410
+ class User < Familia::Horreum
411
+ indexed_by :email, :email_index, target: Team
412
+ end
413
+
414
+ # Generated index methods on User:
415
+ alice.add_to_team_email_index(dev_team) # Add to index
416
+ alice.remove_from_team_email_index(dev_team) # Remove from index
417
+ alice.update_in_team_email_index(dev_team, old_email) # Update index
418
+
419
+ # Generated finder methods on Team:
420
+ Team.find_by_email("alice@example.com") # Find user by email
421
+ Team.find_all_by_email(["alice@example.com"]) # Bulk find by emails
422
+ Team.email_index_for("alice@example.com") # Direct index access
423
+ ```
424
+
425
+ **Key Features:**
426
+ - **Bidirectional Links**: Automatic reverse relationship management
427
+ - **Ruby-like Syntax**: Clean `customer.domains << domain` collection operations
428
+ - **Automatic Indexing**: Efficient O(1) lookups with automatic index maintenance
429
+ - **Performance Optimized**: Bulk operations and efficient sorted set operations
430
+
431
+ > For advanced relationship patterns, permission-encoded relationships, time-series tracking, and performance optimization, see the [Technical Reference](reference/api-technical.md#relationships-feature-v200-pre7).
432
+
433
+ ### Transient Fields
434
+
435
+ Handle temporary or sensitive data that shouldn't persist:
436
+
437
+ ```ruby
438
+ class LoginAttempt < Familia::Horreum
439
+ feature :transient_fields
440
+
441
+ field :username
442
+ field :timestamp
443
+ transient_field :password
444
+ redacted_field :security_token
445
+ end
446
+
447
+ attempt = LoginAttempt.new(
448
+ username: "alice",
449
+ password: "secret123",
450
+ security_token: "sensitive_data"
451
+ )
452
+
453
+ # Transient fields aren't saved to Valkey
454
+ attempt.save
455
+ attempt.reload
456
+ attempt.password # => nil (not persisted)
457
+
458
+ # Redacted fields return safe values
459
+ attempt.security_token.class # => RedactedString
460
+ attempt.security_token.to_s # => "[REDACTED]"
461
+ attempt.security_token.reveal # => "sensitive_data"
462
+ ```
463
+
464
+ **Field Types:**
465
+ - **Transient Fields**: Exist only in memory, never persisted
466
+ - **Redacted Fields**: Return `[REDACTED]` when converted to strings for logging safety
467
+
468
+ > For RedactedString implementation details, single-use patterns, and security considerations, see the [Technical Reference](reference/api-technical.md#transient-fields-feature-v200-pre5).
469
+
470
+ ### Permission Management
471
+
472
+ The relationships feature includes a powerful permission management system:
473
+
474
+ ```ruby
475
+ class Document < Familia::Horreum
476
+ feature :relationships
477
+ permission_tracking :user_permissions
478
+
479
+ field :title, :content
480
+ end
481
+
482
+ # Generated permission control methods
483
+ doc.grant(user, :read, :write) # Grant permissions to user
484
+ doc.revoke(user, :write) # Revoke specific permissions
485
+ doc.add_permission(user, :delete) # Add to existing permissions
486
+ doc.set_permissions(user, :read, :edit) # Replace all permissions
487
+
488
+ # Generated permission query methods
489
+ doc.can?(user, :read) # Check if user has permission
490
+ doc.permissions_for(user) # Get user's permission array
491
+ doc.category?(user, :content_editor) # Check permission category
492
+ doc.permission_tier_for(user) # Get tier: :administrator, :content_editor, :viewer, :none
493
+
494
+ # Generated bulk operations
495
+ doc.all_permissions # Hash of all users and permissions
496
+ doc.clear_all_permissions # Remove all permissions
497
+ doc.users_by_category(:viewer) # Filter users by permission level
498
+
499
+ # Generated collection filtering
500
+ doc.accessible_items("org:123:documents") # Get items with scores
501
+ doc.items_by_permission("org:123:documents", :readable) # Filter by permission
502
+ doc.permission_matrix("org:123:documents") # Count by permission level
503
+ doc.admin_access?(user, "org:123:documents") # Check admin privileges
504
+ ```
505
+
506
+ **Permission Categories:**
507
+ - `:viewer` - Read-only access
508
+ - `:content_editor` - Read and edit access
509
+ - `:administrator` - Full access including user management
510
+
511
+ **Key Features:**
512
+ - **Granular Control**: Fine-grained permission assignment per user
513
+ - **Category-based Queries**: Efficient filtering by permission levels
514
+ - **Bulk Operations**: Manage permissions across collections
515
+ - **Performance Optimized**: O(1) permission checks using Valkey/Redis sorted sets
516
+
154
517
  ## Advanced Patterns
155
518
 
156
519
  ### Custom Methods and Logic
@@ -182,7 +545,7 @@ Execute multiple Valkey commands atomically:
182
545
  ```ruby
183
546
  user.transaction do |conn|
184
547
  conn.set("user:#{user.id}:status", "active")
185
- conn.zadd("active_users", Time.now.to_i, user.id)
548
+ conn.zadd("active_users", Familia.now.to_i, user.id)
186
549
  end
187
550
  ```
188
551
 
@@ -209,21 +572,66 @@ This ensures efficient Valkey connection usage in multi-threaded applications.
209
572
 
210
573
  ### Encrypted Fields
211
574
 
212
- Protect sensitive data at rest:
575
+ Protect sensitive data at rest with transparent encryption:
213
576
 
214
577
  ```ruby
578
+ # First, configure encryption keys
579
+ Familia.configure do |config|
580
+ config.encryption_keys = {
581
+ v1: ENV['FAMILIA_ENCRYPTION_KEY'],
582
+ v2: ENV['FAMILIA_ENCRYPTION_KEY_V2']
583
+ }
584
+ config.current_key_version = :v2
585
+ config.encryption_personalization = 'MyApp-2024' # Optional
586
+ end
587
+
588
+ # Validate configuration before use
589
+ Familia::Encryption.validate_configuration!
590
+
215
591
  class SecureUser < Familia::Horreum
216
592
  feature :encrypted_fields
217
593
 
218
594
  identifier_field :id
219
- field :id
220
- field :email # Plain text
221
- encrypted_field :ssn # Encrypted
222
- encrypted_field :notes, aad_fields: [:id, :email] # With auth data
595
+ field :id, :email # Plaintext fields
596
+ encrypted_field :ssn # Encrypted with field-specific key
597
+ encrypted_field :credit_card # Another encrypted field
598
+ encrypted_field :notes, aad_fields: [:id, :email] # With tamper protection
599
+ end
600
+
601
+ # Usage is transparent
602
+ user = SecureUser.new(
603
+ id: 'user123',
604
+ email: 'alice@example.com',
605
+ ssn: '123-45-6789',
606
+ credit_card: '4111-1111-1111-1111',
607
+ notes: 'VIP customer'
608
+ )
609
+ user.save
610
+
611
+ # Access returns ConcealedString to help prevent accidentally logging or displaying the value
612
+ user.ssn.class # => ConcealedString
613
+ user.ssn.reveal # => "123-45-6789" (actual value)
614
+ user.ssn.to_s # => "[CONCEALED]" (safe for logging)
615
+
616
+ # Performance optimization for multiple operations
617
+ Familia::Encryption.with_request_cache do
618
+ user.ssn = "new-ssn"
619
+ user.credit_card = "new-card"
620
+ user.save # Reuses derived keys
223
621
  end
622
+
623
+ # Key rotation support
624
+ user.re_encrypt_fields! # Re-encrypt with current key version
625
+ user.encrypted_fields_status # Check encryption status
224
626
  ```
225
627
 
226
- Uses authenticated encryption to protect data while allowing selective field access.
628
+ **Key Features:**
629
+ - **Transparent Encryption**: Fields encrypted/decrypted automatically
630
+ - **Security by Default**: ConcealedString prevents accidental value exposure
631
+ - **Key Rotation**: Seamless updates with backward compatibility
632
+ - **Multiple Algorithms**: XChaCha20-Poly1305 (preferred) with AES-256-GCM fallback
633
+
634
+ > For advanced encryption configuration, multiple providers, request caching, and key rotation procedures, see the [Technical Reference](reference/api-technical.md#encrypted-fields-feature-v200-pre5).
227
635
 
228
636
  ### Open-ended Serialization
229
637
 
@@ -243,6 +651,28 @@ end
243
651
 
244
652
  Enables integration with custom serialization formats beyond Familia's defaults.
245
653
 
654
+ ### Feature System Architecture
655
+
656
+ Familia's modular feature system helps organize functionality across complex projects:
657
+
658
+ ```ruby
659
+ class ComplexModel < Familia::Horreum
660
+ # Enable features as needed
661
+ feature :expiration # TTL management
662
+ feature :safe_dump # API-safe serialization
663
+ feature :relationships # Object relationships
664
+ feature :encrypted_fields # Secure field storage
665
+ end
666
+ ```
667
+
668
+ **Key Benefits:**
669
+ - **Per-Class Configuration**: Each model can configure features independently
670
+ - **Automatic Loading**: Use autoloader for large projects to organize features in separate files
671
+ - **Dependency Management**: Features can depend on other features for complex functionality
672
+ - **Reusable Modules**: Share common functionality across multiple models
673
+
674
+ > For advanced feature organization patterns, autoloader configuration, and complex dependency management, see the [Technical Reference](reference/api-technical.md#advanced-feature-system-architecture).
675
+
246
676
  ## Configuration
247
677
 
248
678
  ### Basic Setup
@@ -260,17 +690,83 @@ Familia.redis_config = {
260
690
  }
261
691
  ```
262
692
 
263
- ### Encryption Setup (Optional)
693
+ ### Production Configuration
694
+
695
+ **Environment-based Setup:**
696
+ ```ruby
697
+ # config/familia.rb
698
+ case ENV['RAILS_ENV'] || ENV['RACK_ENV']
699
+ when 'production'
700
+ Familia.redis_config = {
701
+ host: ENV['REDIS_HOST'],
702
+ port: ENV['REDIS_PORT'],
703
+ password: ENV['REDIS_PASSWORD'],
704
+ ssl: true,
705
+ timeout: 10,
706
+ reconnect_attempts: 3
707
+ }
708
+ when 'development'
709
+ Familia.uri = 'redis://localhost:6379/0'
710
+ when 'test'
711
+ Familia.uri = 'redis://localhost:6379/15'
712
+ end
713
+ ```
714
+
715
+ **Advanced Connection Pooling:**
716
+ ```ruby
717
+ # Multi-database with connection pooling
718
+ require 'connection_pool'
719
+
720
+ primary_pool = ConnectionPool.new(size: 20) { Redis.new(url: ENV['PRIMARY_REDIS_URL']) }
721
+ cache_pool = ConnectionPool.new(size: 10) { Redis.new(url: ENV['CACHE_REDIS_URL']) }
722
+
723
+ Familia.connection_provider = lambda do |uri|
724
+ case uri
725
+ when /primary/
726
+ primary_pool.with { |conn| yield conn }
727
+ when /cache/
728
+ cache_pool.with { |conn| yield conn }
729
+ else
730
+ Redis.new(url: uri)
731
+ end
732
+ end
733
+ ```
734
+
735
+ ### Encryption Setup
264
736
 
737
+ **Development Keys:**
265
738
  ```ruby
266
739
  # Generate base64-encoded 32-byte keys
267
- Familia.config.encryption_keys = {
268
- v1: Base64.strict_encode64(SecureRandom.bytes(32)),
269
- v2: Base64.strict_encode64(SecureRandom.bytes(32))
270
- }
271
- Familia.config.current_key_version = :v2
740
+ Familia.configure do |config|
741
+ config.encryption_keys = {
742
+ v1: Base64.strict_encode64(SecureRandom.bytes(32)),
743
+ v2: Base64.strict_encode64(SecureRandom.bytes(32))
744
+ }
745
+ config.current_key_version = :v2
746
+ config.encryption_personalization = "#{Rails.application.class.name}-#{Rails.env}"
747
+ end
748
+ ```
749
+
750
+ **Production Security:**
751
+ ```ruby
752
+ # Use secure key management
753
+ Familia.configure do |config|
754
+ # Load keys from secure key management service
755
+ config.encryption_keys = {
756
+ v1: ENV['FAMILIA_ENCRYPTION_KEY_V1'],
757
+ v2: ENV['FAMILIA_ENCRYPTION_KEY_V2'],
758
+ v3: ENV['FAMILIA_ENCRYPTION_KEY_V3'] # For rotation
759
+ }
760
+ config.current_key_version = :v3
761
+ config.encryption_personalization = ENV['FAMILIA_ENCRYPTION_CONTEXT']
762
+
763
+ # Validate configuration on startup
764
+ Familia::Encryption.validate_configuration!
765
+ end
272
766
  ```
273
767
 
768
+ > For production configuration patterns, advanced connection pooling, multi-database setup, and environment-based configuration, see the [Technical Reference](reference/api-technical.md#connection-management-v200-pre).
769
+
274
770
  ## Common Patterns
275
771
 
276
772
  ### Bulk Operations
@@ -282,7 +778,7 @@ users = User.multiget('alice@example.com', 'bob@example.com')
282
778
  # Batch operations
283
779
  User.transaction do |conn|
284
780
  conn.set('user:alice:status', 'active')
285
- conn.zadd('active_users', Time.now.to_i, 'alice')
781
+ conn.zadd('active_users', Familia.now.to_i, 'alice')
286
782
  end
287
783
  ```
288
784
 
@@ -313,13 +809,44 @@ Familia.connect_to_uri('redis://localhost:6379/0')
313
809
  ```ruby
314
810
  # Debug key names
315
811
  user = User.new(email: 'test@example.com')
316
- puts user.rediskey # Shows the Valkey key that would be used
812
+ puts user.dbkey # Shows the Valkey key that would be used
317
813
  ```
318
814
 
319
815
  **Encryption Issues:**
320
816
  ```ruby
321
817
  # Validate encryption config
322
818
  Familia::Encryption.validate_configuration!
819
+
820
+ # Check encryption status for specific fields
821
+ user.encrypted_fields_status
822
+ # => {ssn: {encrypted: true, key_version: :v2}, credit_card: {encrypted: false}}
823
+
824
+ # Re-encrypt all fields with current key
825
+ user.re_encrypt_fields!
826
+ ```
827
+
828
+ **Relationship Issues:**
829
+ ```ruby
830
+ # Debug relationship indexes
831
+ alice.relationships_debug_info
832
+ # => Shows internal relationship state and indexes
833
+
834
+ # Check relationship consistency
835
+ User.validate_relationship_indexes! # Raises if inconsistent
836
+ ```
837
+
838
+ **Feature Conflicts:**
839
+ ```ruby
840
+ # Check which features are enabled
841
+ MyModel.features_enabled
842
+ # => [:safe_dump, :encrypted_fields, :relationships]
843
+
844
+ # Check feature dependencies
845
+ MyModel.feature_dependencies(:relationships)
846
+ # => Shows required features
847
+
848
+ # Verify feature loading order
849
+ Familia.debug = true # Shows feature loading sequence
323
850
  ```
324
851
 
325
852
  ### Debug Mode
@@ -355,4 +882,81 @@ Familia.config.current_key_version = :v1
355
882
  def clear_redis
356
883
  Familia.redis.flushdb
357
884
  end
885
+
886
+ # Feature-specific testing patterns
887
+ def setup_encryption_for_tests
888
+ test_keys = {
889
+ v1: Base64.strict_encode64('a' * 32),
890
+ v2: Base64.strict_encode64('b' * 32)
891
+ }
892
+ Familia.configure do |config|
893
+ config.encryption_keys = test_keys
894
+ config.current_key_version = :v1
895
+ config.encryption_personalization = 'TestApp-Test'
896
+ end
897
+ end
898
+
899
+ def test_relationships_cleanup
900
+ # Clean up relationship indexes
901
+ Familia.redis.keys('*:relationships:*').each do |key|
902
+ Familia.redis.del(key)
903
+ end
904
+ end
905
+ ```
906
+
907
+ ### Feature Testing Strategies
908
+
909
+ **Testing with Encrypted Fields:**
910
+ ```ruby
911
+ # test/models/secure_user_test.rb
912
+ require 'test_helper'
913
+
914
+ class SecureUserTest < Minitest::Test
915
+ def setup
916
+ setup_encryption_for_tests
917
+ clear_redis
918
+ end
919
+
920
+ def test_encrypted_field_concealment
921
+ user = SecureUser.create(
922
+ id: 'test123',
923
+ email: 'test@example.com',
924
+ ssn: '123-45-6789'
925
+ )
926
+
927
+ assert_instance_of Familia::Features::EncryptedFields::ConcealedString, user.ssn
928
+ assert_equal '[CONCEALED]', user.ssn.to_s
929
+ assert_equal '123-45-6789', user.ssn.reveal
930
+ end
931
+ end
932
+ ```
933
+
934
+ **Testing Relationships:**
935
+ ```ruby
936
+ def test_relationship_bidirectionality
937
+ alice = User.create(email: "alice@test.com")
938
+ team = Team.create(name: "test-team")
939
+
940
+ alice.add_membership(team)
941
+
942
+ assert_includes alice.memberships, team
943
+ assert_includes team.members, alice
944
+ end
945
+ ```
946
+
947
+ **Testing Transient Fields:**
948
+ ```ruby
949
+ def test_transient_field_not_persisted
950
+ attempt = LoginAttempt.new(
951
+ username: "alice",
952
+ password: "secret"
953
+ )
954
+ attempt.save
955
+
956
+ reloaded = LoginAttempt.load(attempt.identifier)
957
+ assert_nil reloaded.password # Not persisted
958
+ assert_equal "alice", reloaded.username # Regular field persisted
959
+ end
358
960
  ```
961
+
962
+ > For comprehensive testing patterns, advanced test helpers, and feature-specific testing strategies, see the [Technical Reference](reference/api-technical.md#testing-patterns).