familia 2.0.0.pre15 → 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 (274) 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 +64 -4
  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 +2 -2
  40. data/docs/migrating/v2.0.0-pre12.md +2 -2
  41. data/docs/migrating/v2.0.0-pre5.md +33 -12
  42. data/docs/migrating/v2.0.0-pre6.md +2 -2
  43. data/docs/migrating/v2.0.0-pre7.md +8 -8
  44. data/docs/overview.md +623 -19
  45. data/docs/reference/api-technical.md +1365 -0
  46. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  47. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  48. data/examples/autoloader/mega_customer.rb +3 -1
  49. data/examples/encrypted_fields.rb +378 -0
  50. data/examples/json_usage_patterns.rb +144 -0
  51. data/examples/relationships.rb +13 -13
  52. data/examples/safe_dump.rb +6 -6
  53. data/examples/single_connection_transaction_confusions.rb +379 -0
  54. data/lib/familia/base.rb +49 -10
  55. data/lib/familia/connection/handlers.rb +223 -0
  56. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  57. data/lib/familia/connection/middleware.rb +75 -0
  58. data/lib/familia/connection/operation_core.rb +93 -0
  59. data/lib/familia/connection/operations.rb +277 -0
  60. data/lib/familia/connection/pipeline_core.rb +87 -0
  61. data/lib/familia/connection/transaction_core.rb +100 -0
  62. data/lib/familia/connection.rb +60 -186
  63. data/lib/familia/data_type/commands.rb +53 -51
  64. data/lib/familia/data_type/serialization.rb +108 -107
  65. data/lib/familia/data_type/types/counter.rb +1 -1
  66. data/lib/familia/data_type/types/hashkey.rb +13 -10
  67. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  68. data/lib/familia/data_type/types/lock.rb +3 -2
  69. data/lib/familia/data_type/types/sorted_set.rb +26 -15
  70. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
  71. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  72. data/lib/familia/data_type.rb +75 -47
  73. data/lib/familia/distinguisher.rb +85 -0
  74. data/lib/familia/encryption/encrypted_data.rb +15 -24
  75. data/lib/familia/encryption/manager.rb +6 -4
  76. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  77. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  78. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  79. data/lib/familia/encryption/request_cache.rb +7 -7
  80. data/lib/familia/encryption.rb +2 -3
  81. data/lib/familia/errors.rb +9 -3
  82. data/lib/familia/features/autoloader.rb +30 -12
  83. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  84. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  85. data/lib/familia/features/encrypted_fields.rb +66 -64
  86. data/lib/familia/features/expiration/extensions.rb +1 -1
  87. data/lib/familia/features/expiration.rb +31 -26
  88. data/lib/familia/features/external_identifier.rb +9 -12
  89. data/lib/familia/features/object_identifier.rb +56 -19
  90. data/lib/familia/features/quantization.rb +16 -21
  91. data/lib/familia/features/relationships/README.md +97 -0
  92. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  93. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  94. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
  95. data/lib/familia/features/relationships/indexing.rb +176 -256
  96. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  97. data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
  98. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  99. data/lib/familia/features/relationships/participation.rb +656 -0
  100. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  101. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  102. data/lib/familia/features/relationships.rb +65 -266
  103. data/lib/familia/features/safe_dump.rb +127 -130
  104. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  105. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  106. data/lib/familia/features/transient_fields.rb +3 -5
  107. data/lib/familia/features.rb +4 -13
  108. data/lib/familia/field_type.rb +24 -4
  109. data/lib/familia/horreum/core/connection.rb +229 -26
  110. data/lib/familia/horreum/core/database_commands.rb +27 -17
  111. data/lib/familia/horreum/core/serialization.rb +40 -20
  112. data/lib/familia/horreum/core/utils.rb +2 -1
  113. data/lib/familia/horreum/shared/settings.rb +2 -1
  114. data/lib/familia/horreum/subclass/definition.rb +33 -45
  115. data/lib/familia/horreum/subclass/management.rb +72 -24
  116. data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
  117. data/lib/familia/horreum.rb +196 -114
  118. data/lib/familia/json_serializer.rb +0 -1
  119. data/lib/familia/logging.rb +11 -114
  120. data/lib/familia/refinements/dear_json.rb +122 -0
  121. data/lib/familia/refinements/logger_trace.rb +20 -17
  122. data/lib/familia/refinements/stylize_words.rb +65 -0
  123. data/lib/familia/refinements/time_literals.rb +60 -52
  124. data/lib/familia/refinements.rb +2 -1
  125. data/lib/familia/secure_identifier.rb +60 -28
  126. data/lib/familia/settings.rb +83 -7
  127. data/lib/familia/utils.rb +5 -87
  128. data/lib/familia/verifiable_identifier.rb +4 -4
  129. data/lib/familia/version.rb +1 -1
  130. data/lib/familia.rb +72 -14
  131. data/lib/middleware/database_middleware.rb +56 -14
  132. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  133. data/try/configuration/scenarios_try.rb +1 -1
  134. data/try/connection/fiber_context_preservation_try.rb +250 -0
  135. data/try/connection/handler_constraints_try.rb +59 -0
  136. data/try/connection/operation_mode_guards_try.rb +208 -0
  137. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  138. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  139. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  140. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  141. data/try/connection/transaction_mode_strict_try.rb +98 -0
  142. data/try/connection/transaction_mode_warn_try.rb +131 -0
  143. data/try/connection/transaction_modes_try.rb +249 -0
  144. data/try/core/autoloader_try.rb +120 -2
  145. data/try/core/connection_try.rb +7 -7
  146. data/try/core/conventional_inheritance_try.rb +130 -0
  147. data/try/core/create_method_try.rb +15 -23
  148. data/try/core/database_consistency_try.rb +10 -10
  149. data/try/core/errors_try.rb +8 -11
  150. data/try/core/familia_extended_try.rb +2 -2
  151. data/try/core/familia_members_methods_try.rb +76 -0
  152. data/try/core/isolated_dbclient_try.rb +165 -0
  153. data/try/core/middleware_try.rb +16 -16
  154. data/try/core/persistence_operations_try.rb +4 -4
  155. data/try/core/pools_try.rb +42 -26
  156. data/try/core/secure_identifier_try.rb +28 -24
  157. data/try/core/time_utils_try.rb +10 -10
  158. data/try/core/tools_try.rb +1 -1
  159. data/try/core/utils_try.rb +2 -2
  160. data/try/data_types/boolean_try.rb +4 -4
  161. data/try/data_types/datatype_base_try.rb +0 -2
  162. data/try/data_types/list_try.rb +10 -10
  163. data/try/data_types/sorted_set_try.rb +5 -5
  164. data/try/data_types/string_try.rb +12 -12
  165. data/try/data_types/unsortedset_try.rb +33 -0
  166. data/try/debugging/cache_behavior_tracer.rb +7 -7
  167. data/try/debugging/debug_aad_process.rb +1 -1
  168. data/try/debugging/debug_concealed_internal.rb +1 -1
  169. data/try/debugging/debug_cross_context.rb +1 -1
  170. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  171. data/try/debugging/encryption_method_tracer.rb +10 -10
  172. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  173. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  174. data/try/encryption/config_persistence_try.rb +2 -2
  175. data/try/encryption/encryption_core_try.rb +19 -19
  176. data/try/encryption/instance_variable_scope_try.rb +1 -1
  177. data/try/encryption/module_loading_try.rb +2 -2
  178. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  179. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  180. data/try/encryption/secure_memory_handling_try.rb +1 -1
  181. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  182. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  183. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  184. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  185. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  186. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  187. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  188. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  189. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  190. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  191. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  192. data/try/features/feature_dependencies_try.rb +3 -3
  193. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  194. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  195. data/try/features/quantization/quantization_try.rb +1 -1
  196. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  197. data/try/features/relationships/indexing_try.rb +433 -0
  198. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  199. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  200. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  201. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  202. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  203. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  204. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  205. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  206. data/try/features/relationships/relationships_performance_try.rb +20 -20
  207. data/try/features/relationships/relationships_try.rb +27 -38
  208. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  209. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  210. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  211. data/try/helpers/test_cleanup.rb +86 -0
  212. data/try/helpers/test_helpers.rb +3 -3
  213. data/try/horreum/base_try.rb +3 -2
  214. data/try/horreum/commands_try.rb +1 -1
  215. data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
  216. data/try/horreum/initialization_try.rb +11 -7
  217. data/try/horreum/relations_try.rb +21 -13
  218. data/try/horreum/serialization_try.rb +12 -11
  219. data/try/integration/cross_component_try.rb +3 -3
  220. data/try/memory/memory_basic_test.rb +1 -1
  221. data/try/memory/memory_docker_ruby_dump.sh +1 -1
  222. data/try/models/customer_safe_dump_try.rb +1 -1
  223. data/try/models/customer_try.rb +8 -10
  224. data/try/models/datatype_base_try.rb +3 -3
  225. data/try/models/familia_object_try.rb +9 -8
  226. data/try/performance/benchmarks_try.rb +2 -2
  227. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  228. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  229. data/try/prototypes/atomic_saves_v4.rb +1 -1
  230. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  231. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  232. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  233. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  234. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  235. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  236. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  237. data/try/prototypes/pooling/pool_siege.rb +11 -11
  238. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  239. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  240. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  241. data/try/refinements/logger_trace_methods_try.rb +44 -0
  242. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  243. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  244. metadata +75 -43
  245. data/.rubocop_todo.yml +0 -208
  246. data/docs/connection_pooling.md +0 -192
  247. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  248. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  249. data/docs/guides/Feature-System-Autoloading.md +0 -198
  250. data/docs/guides/Home.md +0 -116
  251. data/docs/guides/Relationships-Guide.md +0 -737
  252. data/docs/guides/relationships-methods.md +0 -266
  253. data/docs/reference/auditing_database_commands.rb +0 -228
  254. data/examples/permissions.rb +0 -240
  255. data/lib/familia/features/relationships/cascading.rb +0 -437
  256. data/lib/familia/features/relationships/membership.rb +0 -497
  257. data/lib/familia/features/relationships/permission_management.rb +0 -264
  258. data/lib/familia/features/relationships/querying.rb +0 -615
  259. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  260. data/lib/familia/features/relationships/tracking.rb +0 -418
  261. data/lib/familia/refinements/snake_case.rb +0 -40
  262. data/lib/familia/validation/command_recorder.rb +0 -336
  263. data/lib/familia/validation/expectations.rb +0 -519
  264. data/lib/familia/validation/validation_helpers.rb +0 -443
  265. data/lib/familia/validation/validator.rb +0 -412
  266. data/lib/familia/validation.rb +0 -140
  267. data/try/data_types/set_try.rb +0 -33
  268. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  269. data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
  270. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
  271. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  272. data/try/validation/command_validation_try.rb.disabled +0 -207
  273. data/try/validation/performance_validation_try.rb.disabled +0 -324
  274. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -1,6 +1,6 @@
1
1
  # Familia v2.0.0-pre Series Technical Reference
2
2
 
3
- **Familia** is a Ruby ORM for Redis/Valkey providing object mapping, relationships, and advanced features like encryption, connection pooling, and permission systems. This technical reference covers the major classes, methods, and usage patterns introduced in the v2.0.0-pre series.
3
+ **Familia** is a Ruby ORM for Valkey/Redis providing object mapping, relationships, and advanced features like encryption, connection pooling, and permission systems. This technical reference covers the major classes, methods, and usage patterns introduced in the v2.0.0-pre series.
4
4
 
5
5
  ---
6
6
 
@@ -9,37 +9,37 @@
9
9
  ### Base Classes
10
10
 
11
11
  #### `Familia::Horreum` - Primary ORM Base Class
12
- The main base class for Redis-backed objects, similar to ActiveRecord models.
12
+ The main base class for Valkey/Redis-backed objects, similar to ActiveRecord models.
13
13
 
14
14
  ```ruby
15
15
  class User < Familia::Horreum
16
16
  # Basic field definitions
17
17
  field :name, :email, :created_at
18
18
 
19
- # Redis data types as instance variables
20
- list :sessions # Redis list
21
- set :tags # Redis set
22
- sorted_set :scores # Redis sorted set
23
- hash :settings # Redis hash
19
+ # Valkey/Redis data types as instance variables
20
+ list :sessions # Valkey/Redis list
21
+ set :tags # Valkey/Redis set
22
+ sorted_set :scores # Valkey/Redis sorted set
23
+ hash :settings # Valkey/Redis hash
24
24
  end
25
25
  ```
26
26
 
27
27
  **Key Methods:**
28
- - `save` - Persist object to Redis
28
+ - `save` - Persist object to Valkey/Redis
29
29
  - `save_if_not_exists` - Conditional persistence (v2.0.0-pre6)
30
- - `load` - Load object from Redis
31
- - `exists?` - Check if object exists in Redis
32
- - `destroy` - Remove object from Redis
30
+ - `load` - Load object from Valkey/Redis
31
+ - `exists?` - Check if object exists in Valkey/Redis
32
+ - `destroy` - Remove object from Valkey/Redis
33
33
 
34
- #### `Familia::DataType` - Redis Data Type Wrapper
35
- Base class for Redis data type implementations.
34
+ #### `Familia::DataType` - Valkey/Redis Data Type Wrapper
35
+ Base class for Valkey/Redis data type implementations.
36
36
 
37
37
  **Registered Types:**
38
- - `String` - Redis strings
39
- - `List` - Redis lists
40
- - `Set` - Redis sets
41
- - `SortedSet` - Redis sorted sets
42
- - `HashKey` - Redis hashes
38
+ - `String` - Valkey/Redis strings
39
+ - `List` - Valkey/Redis lists
40
+ - `UnsortedSet` - Valkey/Redis sets
41
+ - `SortedSet` - Valkey/Redis sorted sets
42
+ - `HashKey` - Valkey/Redis hashes
43
43
  - `Counter` - Atomic counters
44
44
  - `Lock` - Distributed locks
45
45
 
@@ -82,7 +82,7 @@ end
82
82
 
83
83
  session = Session.new(user_id: 123, token: "abc123")
84
84
  session.save
85
- session.expire_in(1.hour) # Set custom expiration
85
+ session.expire_in(1.hour) # UnsortedSet custom expiration
86
86
  session.ttl # Check remaining time
87
87
  ```
88
88
 
@@ -152,7 +152,7 @@ class LoginForm < Familia::Horreum
152
152
  feature :transient_fields
153
153
 
154
154
  field :username # Persistent
155
- transient_field :password # Never stored in Redis
155
+ transient_field :password # Never stored in Valkey/Redis
156
156
  transient_field :csrf_token # Runtime only
157
157
  end
158
158
 
@@ -188,7 +188,7 @@ class Customer < Familia::Horreum
188
188
  indexed_by :email_lookup, field: :email
189
189
 
190
190
  # Global tracking with scoring
191
- tracked_in :all_customers, type: :sorted_set, score: :created_at
191
+ participates_in :all_customers, type: :sorted_set, score: :created_at
192
192
  end
193
193
 
194
194
  class Domain < Familia::Horreum
@@ -201,8 +201,8 @@ class Domain < Familia::Horreum
201
201
  member_of Customer, :domains, type: :set
202
202
 
203
203
  # Conditional tracking with lambda scoring
204
- tracked_in :active_domains, type: :sorted_set,
205
- score: ->(domain) { domain.status == 'active' ? Time.now.to_i : 0 }
204
+ participates_in :active_domains, type: :sorted_set,
205
+ score: ->(domain) { domain.status == 'active' ? Familia.now.to_i : 0 }
206
206
  end
207
207
  ```
208
208
 
@@ -228,7 +228,7 @@ found_id = Customer.email_lookup.get(customer.email) # O(1) lookup
228
228
  # Global tracking
229
229
  Customer.add_to_all_customers(customer)
230
230
  recent = Customer.all_customers.range_by_score(
231
- (Time.now - 24.hours).to_i, '+inf'
231
+ (Familia.now - 24.hours).to_i, '+inf'
232
232
  )
233
233
  ```
234
234
 
@@ -240,7 +240,7 @@ recent = Customer.all_customers.range_by_score(
240
240
  Flexible connection pooling with provider-based architecture.
241
241
 
242
242
  ```ruby
243
- # Basic Redis connection
243
+ # Basic Valkey/Redis connection
244
244
  Familia.configure do |config|
245
245
  config.redis_uri = "redis://localhost:6379/0"
246
246
  end
@@ -331,7 +331,7 @@ end
331
331
  # Usage example
332
332
  user_id = "user123"
333
333
  doc_id = "doc456"
334
- timestamp = Time.now.to_i
334
+ timestamp = Familia.now.to_i
335
335
 
336
336
  # Grant read + write permissions
337
337
  permissions = Document::READ | Document::WRITE # 3
@@ -356,25 +356,25 @@ class ActivityTracker < Familia::Horreum
356
356
  feature :relationships
357
357
 
358
358
  # Track user activities with timestamps
359
- tracked_in :user_activities, type: :sorted_set,
359
+ participates_in :user_activities, type: :sorted_set,
360
360
  score: ->(activity) { activity.created_at }
361
361
 
362
362
  # Track by activity type
363
- tracked_in :activity_by_type, type: :sorted_set,
363
+ participates_in :activity_by_type, type: :sorted_set,
364
364
  score: ->(activity) { "#{activity.activity_type}:#{activity.created_at}".hash }
365
365
 
366
366
  field :user_id, :activity_type, :data, :created_at
367
367
  end
368
368
 
369
369
  # Query recent activities (last hour)
370
- hour_ago = (Time.now - 1.hour).to_i
370
+ hour_ago = (Familia.now - 1.hour).to_i
371
371
  recent_activities = ActivityTracker.user_activities.range_by_score(
372
372
  hour_ago, '+inf', limit: [0, 50]
373
373
  )
374
374
 
375
375
  # Get activities by type in time range
376
376
  login_activities = ActivityTracker.activity_by_type.range_by_score(
377
- "login:#{hour_ago}".hash, "login:#{Time.now.to_i}".hash
377
+ "login:#{hour_ago}".hash, "login:#{Familia.now.to_i}".hash
378
378
  )
379
379
  ```
380
380
 
@@ -382,8 +382,8 @@ login_activities = ActivityTracker.activity_by_type.range_by_score(
382
382
 
383
383
  ## Data Type Usage Patterns
384
384
 
385
- ### Advanced Sorted Set Operations
386
- Leverage Redis sorted sets for rankings, time series, and scored data.
385
+ ### Advanced Sorted UnsortedSet Operations
386
+ Leverage Valkey/Redis sorted sets for rankings, time series, and scored data.
387
387
 
388
388
  ```ruby
389
389
  class Leaderboard < Familia::Horreum
@@ -414,7 +414,7 @@ leaderboard.scores.increment("player1", 100) # Add 100 to existing score
414
414
  ```
415
415
 
416
416
  ### List-Based Queues and Feeds
417
- Use Redis lists for queues, feeds, and ordered data.
417
+ Use Valkey/Redis lists for queues, feeds, and ordered data.
418
418
 
419
419
  ```ruby
420
420
  class TaskQueue < Familia::Horreum
@@ -447,7 +447,7 @@ task_data = JSON.parse(next_task) if next_task
447
447
  feed = ActivityFeed.new(user_id: "user123")
448
448
 
449
449
  # Add activity (keep last 100)
450
- feed.activities.unshift("User logged in at #{Time.now}")
450
+ feed.activities.unshift("User logged in at #{Familia.now}")
451
451
  feed.activities.trim(0, 99) # Keep only last 100 items
452
452
 
453
453
  # Get recent activities
@@ -492,7 +492,7 @@ beta_enabled = prefs.feature_flags.get("beta_ui") == "true"
492
492
  ## Error Handling and Validation
493
493
 
494
494
  ### Connection Error Handling
495
- Robust error handling for Redis connection issues.
495
+ Robust error handling for Valkey/Redis connection issues.
496
496
 
497
497
  ```ruby
498
498
  class ResilientService < Familia::Horreum
@@ -508,7 +508,7 @@ class ResilientService < Familia::Horreum
508
508
  sleep(0.1 * (4 - retries)) # Exponential backoff
509
509
  retry
510
510
  else
511
- Familia.warn "Redis operation failed after retries: #{e.message}"
511
+ Familia.warn "Valkey/Redis operation failed after retries: #{e.message}"
512
512
  nil # Return nil or handle gracefully
513
513
  end
514
514
  end
@@ -574,7 +574,7 @@ end
574
574
  ## Performance Optimization
575
575
 
576
576
  ### Batch Operations
577
- Minimize Redis round trips with batch operations.
577
+ Minimize Valkey/Redis round trips with batch operations.
578
578
 
579
579
  ```ruby
580
580
  # Instead of multiple individual operations
@@ -584,7 +584,7 @@ users = []
584
584
  users << user
585
585
  end
586
586
 
587
- # Use Redis pipelining for batch saves
587
+ # Use Valkey/Redis pipelining for batch saves
588
588
  User.transaction do |redis|
589
589
  users.each do |user|
590
590
  # All operations batched in transaction
@@ -720,7 +720,7 @@ Common patterns for testing Familia applications.
720
720
  # test_helper.rb
721
721
  require 'familia'
722
722
 
723
- # Use separate Redis database for tests
723
+ # Use separate Valkey/Redis database for tests
724
724
  Familia.configure do |config|
725
725
  config.redis_uri = ENV.fetch('REDIS_TEST_URI', 'redis://localhost:6379/15')
726
726
  end
@@ -739,7 +739,7 @@ module TestHelpers
739
739
  User.new({
740
740
  email: "test@example.com",
741
741
  name: "Test User",
742
- created_at: Time.now.to_i
742
+ created_at: Familia.now.to_i
743
743
  }.merge(attrs))
744
744
  end
745
745
  end
@@ -787,7 +787,7 @@ Essential configuration options for Familia v2.0.0-pre.
787
787
 
788
788
  ```ruby
789
789
  Familia.configure do |config|
790
- # Basic Redis connection
790
+ # Basic Valkey/Redis connection
791
791
  config.redis_uri = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
792
792
 
793
793
  # Connection provider for pooling (optional)
@@ -17,7 +17,7 @@
17
17
 
18
18
  ### Major API Modernization
19
19
  - **Complete API redesign** for clarity and modern Ruby conventions
20
- - **Valkey compatibility** alongside traditional Redis support
20
+ - **Valkey compatibility** alongside traditional Valkey/Redis support
21
21
  - **Ruby 3.4+ modernization** with fiber and thread safety improvements
22
22
  - **Connection pooling foundation** with provider pattern architecture
23
23
 
@@ -103,7 +103,7 @@ end
103
103
 
104
104
  ### Comprehensive Relationships System
105
105
  - **Three relationship types** optimized for different use cases:
106
- - `tracked_in` - Multi-presence tracking with score encoding
106
+ - `participates_in` - Multi-presence tracking with score encoding
107
107
  - `indexed_by` - O(1) hash-based lookups
108
108
  - `member_of` - Bidirectional membership with collision-free naming
109
109
 
@@ -116,7 +116,7 @@ class Customer < Familia::Horreum
116
116
 
117
117
  # Define collections
118
118
  set :domains
119
- tracked_in :active_users, type: :sorted_set
119
+ participates_in :active_users, type: :sorted_set
120
120
  end
121
121
 
122
122
  class Domain < Familia::Horreum
@@ -1,9 +1,10 @@
1
1
  # Archived Documentation
2
2
 
3
- This directory contains original documentation files that have been migrated to the new Scriv-based changelog system and reorganized documentation structure.
3
+ This directory contains original documentation files that have either been migrated to the new Scriv-based changelog system or incorporated into other documents.
4
4
 
5
5
  ## Migration Date
6
- **September 1, 2025** - As part of implementing [Issue #84: Scriv-based changelog system](https://github.com/delano/familia/issues/84)
6
+ **Sept 22, 1015** - Deprecated api-reference.md, to reduce surface area for stale documentation to hide.
7
+ **Sept 1, 2025** - As part of implementing [Issue #84: Scriv-based changelog system](https://github.com/delano/familia/issues/84)
7
8
 
8
9
  ## Archived Files
9
10
 
@@ -1,5 +1,8 @@
1
1
  # API Reference
2
2
 
3
+ > [!NOTE]
4
+ > This document is deprecated. For comprehensive encryption API documentation, see [`docs/reference/api-technical.md`](../reference/api-technical.md) which contains complete implementation details and examples.
5
+
3
6
  ## Class Methods
4
7
 
5
8
  ### encrypted_field
@@ -7,22 +10,22 @@
7
10
  Defines an encrypted field on a Familia::Horreum class.
8
11
 
9
12
  ```ruby
10
- encrypted_field(name, **options)
13
+ encrypted_field(name, aad_fields: [], **options)
11
14
  ```
12
15
 
13
16
  **Parameters:**
14
17
  - `name` (Symbol) - Field name
15
- - `**options` (Hash) - Standard field options plus encryption-specific options
16
-
17
- **Options:**
18
- - `:as` - Custom accessor method name
19
- - `:on_conflict` - Conflict resolution (always `:raise` for encrypted fields)
18
+ - `aad_fields` (Array<Symbol>) - Additional fields to include in authentication
19
+ - `**options` (Hash) - Standard field options
20
20
 
21
21
  **Example:**
22
22
  ```ruby
23
23
  class User < Familia::Horreum
24
+ feature :encrypted_fields
25
+
24
26
  encrypted_field :favorite_snack
25
- encrypted_field :api_key, as: :secret_key
27
+ encrypted_field :api_key
28
+ encrypted_field :notes, aad_fields: [:user_id, :email] # With tamper protection
26
29
  end
27
30
  ```
28
31
 
@@ -38,63 +41,60 @@ User.encrypted_fields # => [:favorite_snack, :api_key]
38
41
 
39
42
  ### Field Accessors
40
43
 
41
- Encrypted fields provide standard accessors:
44
+ Encrypted fields provide standard accessors that return ConcealedString objects:
42
45
 
43
46
  ```ruby
44
- user.favorite_snack # Get decrypted value
47
+ user.favorite_snack # Returns ConcealedString (safe for logging)
48
+ user.favorite_snack.reveal # Get actual decrypted value
45
49
  user.favorite_snack = value # Set and encrypt value
46
50
  user.favorite_snack! # Fast write (still encrypted)
47
51
  ```
48
52
 
49
- ### Passphrase-Protected Access
50
-
53
+ **ConcealedString Methods:**
51
54
  ```ruby
52
- # For passphrase-protected fields
53
- vault.secret_data(passphrase_value: "user_passphrase")
55
+ concealed = user.favorite_snack
56
+ concealed.to_s # => "[CONCEALED]" (safe for logging)
57
+ concealed.reveal # => "actual value"
58
+ concealed.clear! # Clear from memory
59
+ concealed.cleared? # Check if cleared
54
60
  ```
55
61
 
56
62
  ## Familia::Encryption Module
57
63
 
58
- ### manager
64
+ ### with_request_cache
59
65
 
60
- Creates a manager instance with optional algorithm selection.
66
+ Enables key derivation caching for performance optimization:
61
67
 
62
68
  ```ruby
63
- # Use best available provider
64
- mgr = Familia::Encryption.manager
65
-
66
- # Use specific algorithm
67
- mgr = Familia::Encryption.manager(algorithm: 'xchacha20poly1305')
69
+ Familia::Encryption.with_request_cache do
70
+ # Multiple encryption operations reuse derived keys
71
+ user.secret_one = "value1"
72
+ user.secret_two = "value2"
73
+ user.save
74
+ end
68
75
  ```
69
76
 
70
- ### encrypt
77
+ ### clear_request_cache!
71
78
 
72
- Encrypts plaintext using the default provider.
79
+ Manually clears the request-level key cache:
73
80
 
74
81
  ```ruby
75
- Familia::Encryption.encrypt(plaintext,
76
- context: "User:favorite_snack:user123",
77
- additional_data: nil
78
- )
82
+ Familia::Encryption.clear_request_cache!
79
83
  ```
80
84
 
81
- ### encrypt_with
85
+ ### encrypt / decrypt
82
86
 
83
- Encrypts plaintext with a specific algorithm.
87
+ Low-level encryption methods (typically used internally):
84
88
 
85
89
  ```ruby
86
- Familia::Encryption.encrypt_with('aes-256-gcm', plaintext,
90
+ # Encrypt with context for key derivation
91
+ encrypted = Familia::Encryption.encrypt(plaintext,
87
92
  context: "User:favorite_snack:user123",
88
93
  additional_data: nil
89
94
  )
90
- ```
91
95
 
92
- ### decrypt
93
-
94
- Decrypts ciphertext (auto-detects algorithm from JSON).
95
-
96
- ```ruby
97
- Familia::Encryption.decrypt(encrypted_json,
96
+ # Decrypt (auto-detects algorithm from JSON)
97
+ decrypted = Familia::Encryption.decrypt(encrypted_json,
98
98
  context: "User:favorite_snack:user123",
99
99
  additional_data: nil
100
100
  )
@@ -200,16 +200,19 @@ Familia.configure do |config|
200
200
  }
201
201
  config.current_key_version = :v1
202
202
 
203
- # Multi-version configuration
203
+ # Multi-version configuration for key rotation
204
204
  config.encryption_keys = {
205
205
  v1_2024: ENV['OLD_KEY'],
206
206
  v2_2025: ENV['NEW_KEY']
207
207
  }
208
208
  config.current_key_version = :v2_2025
209
209
 
210
- # Key cache TTL (seconds)
211
- config.key_cache_ttl = 300 # Default: 5 minutes
210
+ # Optional personalization (XChaCha20-Poly1305 only)
211
+ config.encryption_personalization = 'MyApp-2024'
212
212
  end
213
+
214
+ # Always validate configuration
215
+ Familia::Encryption.validate_configuration!
213
216
  ```
214
217
 
215
218
  ## Data Types
@@ -228,18 +231,30 @@ EncryptedData = Data.define(
228
231
  )
229
232
  ```
230
233
 
231
- ### RedactedString
234
+ ### ConcealedString
232
235
 
233
- String subclass that redacts sensitive data in output.
236
+ String-like object that conceals sensitive data in output and provides memory safety.
234
237
 
235
238
  ```ruby
236
- class RedactedString < String
239
+ class ConcealedString
240
+ def reveal
241
+ # Returns actual decrypted string value
242
+ end
243
+
237
244
  def to_s
238
- '[REDACTED]'
245
+ '[CONCEALED]'
239
246
  end
240
247
 
241
248
  def inspect
242
- '[REDACTED]'
249
+ '[CONCEALED]'
250
+ end
251
+
252
+ def clear!
253
+ # Best-effort memory wiping
254
+ end
255
+
256
+ def cleared?
257
+ # Returns true if cleared from memory
243
258
  end
244
259
  end
245
260
  ```
@@ -265,83 +280,54 @@ rescue Familia::EncryptionError => e
265
280
  end
266
281
  ```
267
282
 
268
- ## CLI Commands
269
-
270
- ### Generate Key
283
+ ## Instance Methods
271
284
 
272
- ```bash
273
- $ familia encryption:generate_key [--bits 256]
274
- # Outputs Base64-encoded key
275
- ```
285
+ ### encrypted_data?
276
286
 
277
- ### Verify Encryption
287
+ Check if any encrypted fields have values:
278
288
 
279
- ```bash
280
- $ familia encryption:verify [--model User] [--field favorite_snack]
281
- # Verifies field encryption is working
289
+ ```ruby
290
+ user.encrypted_data? # => true if any encrypted fields have values
282
291
  ```
283
292
 
284
- ### Rotate Keys
293
+ ### clear_encrypted_fields!
294
+
295
+ Clear all encrypted field values from memory:
285
296
 
286
- ```bash
287
- $ familia encryption:rotate [--from v1] [--to v2]
288
- # Migrates encrypted fields to new key
297
+ ```ruby
298
+ user.clear_encrypted_fields! # Clear all ConcealedString values
289
299
  ```
290
300
 
291
- ## Testing Helpers
301
+ ### encrypted_fields_cleared?
292
302
 
293
- ### EncryptionTestHelpers
303
+ Check if all encrypted fields have been cleared:
294
304
 
295
305
  ```ruby
296
- module Familia::EncryptionTestHelpers
297
- # Set up test encryption keys
298
- def with_test_encryption_keys(&block)
299
-
300
- # Verify field is encrypted in storage
301
- def assert_field_encrypted(model, field)
302
-
303
- # Verify decryption works
304
- def assert_decryption_works(model, field, expected)
305
- end
306
+ user.encrypted_fields_cleared? # => true if all cleared
306
307
  ```
307
308
 
308
- ### RSpec Example
309
-
310
- ```ruby
311
- RSpec.describe User do
312
- include Familia::EncryptionTestHelpers
309
+ ### re_encrypt_fields!
313
310
 
314
- it "encrypts favorite snack field" do
315
- with_test_encryption_keys do
316
- user = User.create(favorite_snack: "chocolate chip cookies")
311
+ Re-encrypt all encrypted fields with current key version:
317
312
 
318
- assert_field_encrypted(user, :favorite_snack)
319
- assert_decryption_works(user, :favorite_snack, "chocolate chip cookies")
320
- end
321
- end
322
- end
313
+ ```ruby
314
+ user.re_encrypt_fields! # Uses current_key_version
315
+ user.save
323
316
  ```
324
317
 
325
- ## Performance Considerations
318
+ ### encrypted_fields_status
326
319
 
327
- ### Key Derivation Caching
320
+ Get encryption status for debugging:
328
321
 
329
322
  ```ruby
330
- # Automatic in web requests
331
- class ApplicationController
332
- around_action :with_encryption_cache
333
-
334
- def with_encryption_cache
335
- Familia::Encryption.with_key_cache { yield }
336
- end
337
- end
323
+ user.encrypted_fields_status
324
+ # => {
325
+ # ssn: { encrypted: true, cleared: false },
326
+ # credit_card: { encrypted: true, cleared: true }
327
+ # }
338
328
  ```
339
329
 
340
- ### Batch Operations
330
+ ---
341
331
 
342
- ```ruby
343
- # Efficient for bulk operations
344
- User.batch_decrypt(:favorite_snack) do |users|
345
- users.each { |u| process(u.favorite_snack) }
346
- end
347
- ```
332
+ > [!IMPORTANT]
333
+ > For complete implementation details, configuration examples, and advanced usage patterns, see the comprehensive documentation in [`docs/reference/api-technical.md`](../reference/api-technical.md).
data/docs/conf.py ADDED
@@ -0,0 +1,29 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # For the full list of built-in configuration values, see the documentation:
4
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
+
6
+ # -- Project information -----------------------------------------------------
7
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
+
9
+ project = "Familia"
10
+ copyright = "2010-ongoing delano, contributors"
11
+ author = "Delano Mandelbaum"
12
+
13
+ # -- General configuration ---------------------------------------------------
14
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
15
+
16
+ extensions = [
17
+ "sphinx.ext.autodoc",
18
+ "sphinx.ext.viewcode",
19
+ "sphinx.ext.napoleon",
20
+ ]
21
+
22
+ templates_path = ["_templates"]
23
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
24
+
25
+ # -- Options for HTML output -------------------------------------------------
26
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
27
+
28
+ html_theme = "alabaster"
29
+ html_static_path = ["_static"]
@@ -376,10 +376,10 @@ profile = UserProfile.new(user_id: 'user_123')
376
376
  profile.save
377
377
 
378
378
  # Regular setter: updates instance variable only
379
- profile.last_login_at = Time.now # Not yet in database
379
+ profile.last_login_at = Familia.now # Not yet in database
380
380
 
381
381
  # Fast method: immediate database write
382
- profile.last_login_at!(Time.now) # Written to database immediately
382
+ profile.last_login_at!(Familia.now) # Written to database immediately
383
383
 
384
384
  # Reading from database
385
385
  profile.last_login_at # => reads from instance variable
@@ -417,8 +417,8 @@ class AuditedFieldType < Familia::FieldType
417
417
  field: field_name,
418
418
  old_value: old_value,
419
419
  new_value: value,
420
- changed_at: Time.now.to_f,
421
- changed_by: Thread.current[:current_user]&.id
420
+ changed_at: Familia.now,
421
+ changed_by: Fiber[:current_user]&.id
422
422
  }
423
423
 
424
424
  # Store audit trail
@@ -449,7 +449,7 @@ end
449
449
  doc = AuditedDocument.new(doc_id: 'doc_123')
450
450
  doc.save
451
451
 
452
- Thread.current[:current_user] = OpenStruct.new(id: 'user_456')
452
+ Fiber[:current_user] = OpenStruct.new(id: 'user_456')
453
453
  doc.content!("Initial content") # Audited change
454
454
  doc.status!("draft") # Audited change
455
455
 
@@ -705,22 +705,22 @@ RSpec.describe TimestampFieldType do
705
705
  instance.created_at = "2023-06-15 14:30:00"
706
706
  expect(instance.created_at).to be_a(Time)
707
707
 
708
- instance.created_at = Time.now
708
+ instance.created_at = Familia.now
709
709
  expect(instance.created_at).to be_a(Time)
710
710
 
711
- instance.created_at = Time.now.to_i
711
+ instance.created_at = Familia.now.to_i
712
712
  expect(instance.created_at).to be_a(Time)
713
713
  end
714
714
 
715
715
  it "serializes to integer" do
716
- time_value = Time.now
716
+ time_value = Familia.now
717
717
  serialized = field_type.serialize(time_value)
718
718
  expect(serialized).to be_a(Integer)
719
719
  expect(serialized).to eq(time_value.to_i)
720
720
  end
721
721
 
722
722
  it "deserializes from integer" do
723
- timestamp = Time.now.to_i
723
+ timestamp = Familia.now.to_i
724
724
  deserialized = field_type.deserialize(timestamp)
725
725
  expect(deserialized).to be_a(Time)
726
726
  expect(deserialized.to_i).to eq(timestamp)