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
@@ -0,0 +1,200 @@
1
+ # Relationships Feature Guide
2
+
3
+ The Relationships feature transforms how you manage object associations in Familia applications. Instead of manually maintaining foreign keys and indexes, relationships provide automatic bidirectional links, efficient queries, and Ruby-like collection syntax that makes working with related objects feel natural and intuitive.
4
+
5
+ This guide provides a breadth-first introduction to the four core relationship capabilities: **participation**, **indexing**, **querying**, and **cascading operations**.
6
+
7
+ > [!TIP]
8
+ > Enable relationships with `feature :relationships`, define associations with `participates_in`, and use clean Ruby syntax like `customer.domains << domain` to manage relationships.
9
+
10
+ ## What Are Relationships?
11
+
12
+ Relationships automate object associations in Familia, eliminating manual foreign key management:
13
+
14
+ ```ruby
15
+ # Without relationships - manual and error-prone
16
+ customer.domain_ids.add(domain.identifier)
17
+ domain.customer_id = customer.identifier
18
+
19
+ # With relationships - automatic and clean
20
+ customer.domains << domain # Updates both sides automatically
21
+ ```
22
+
23
+ **Key Benefits:**
24
+ - **Automatic bidirectional updates** - no manual synchronization
25
+ - **Ruby-like syntax** - familiar `<<` and collection operations
26
+ - **O(1) lookups** - efficient Valkey/Redis-backed indexing
27
+ - **Lifecycle management** - automatic cleanup and maintenance
28
+
29
+ ## Core Relationship Capabilities
30
+
31
+ ### 1. Participation - Bidirectional Object Links
32
+
33
+ Connect objects with automatic synchronization:
34
+
35
+ ```ruby
36
+ class User < Familia::Horreum
37
+ feature :relationships
38
+ set :teams # Collection holder
39
+ end
40
+
41
+ class Team < Familia::Horreum
42
+ feature :relationships
43
+ participates_in User, :teams # Declares participation
44
+ end
45
+
46
+ # Usage - automatic bidirectional updates
47
+ user.teams << team
48
+ team.in_user_teams?(user) # => true
49
+ ```
50
+
51
+ **Many-to-Many Example:**
52
+ ```ruby
53
+ class Project < Familia::Horreum
54
+ set :contributors, :reviewers
55
+ end
56
+
57
+ class Developer < Familia::Horreum
58
+ participates_in Project, :contributors
59
+ participates_in Project, :reviewers
60
+ end
61
+
62
+ project.contributors << alice # Alice contributes
63
+ project.reviewers << bob # Bob reviews
64
+ ```
65
+
66
+ ### 2. Indexing - Automatic Object Tracking
67
+
68
+ Enable O(1) lookups with automatic index management:
69
+
70
+ ```ruby
71
+ class User < Familia::Horreum
72
+ feature :relationships
73
+ field :email, :created_at
74
+
75
+ # Global unique lookups
76
+ class_indexed_by :email, :email_lookup
77
+
78
+ # Scored tracking collections
79
+ class_participates_in :all_users, score: :created_at
80
+ end
81
+
82
+ # Automatic on save/destroy
83
+ User.find_by_email("alice@example.com") # O(1) lookup
84
+ User.all_users.range(0, 9) # Most recent 10 users
85
+ ```
86
+
87
+ **Relationship-Scoped Indexing:**
88
+ ```ruby
89
+ class Domain < Familia::Horreum
90
+ participates_in Customer, :domains
91
+ indexed_by :name, :domain_index, target: Customer # Unique per customer
92
+ end
93
+
94
+ customer.find_by_name("example.com") # Find domain within this customer
95
+ ```
96
+
97
+ ### 3. Querying - Ruby-like Collection Operations
98
+
99
+ Work with relationships like standard Ruby collections:
100
+
101
+ ```ruby
102
+ # Standard collection operations
103
+ org.members << alice # Add relationship
104
+ org.members.merge([id1, id2, id3]) # Bulk additions
105
+ org.members.size # Count relationships
106
+ org.members.empty? # Check if any exist
107
+
108
+ # Set operations
109
+ common = org1.members & org2.members # Intersection
110
+ all = org1.members | org2.members # Union
111
+
112
+ # Load actual objects when needed
113
+ member_ids = org.members.to_a
114
+ members = Person.multiget(*member_ids) # Efficient bulk loading
115
+ ```
116
+
117
+ > [!WARNING]
118
+ > Collections store identifiers, not objects. Use `multiget` for efficient bulk loading.
119
+
120
+ ### 4. Cascading Operations - Scored Relationships
121
+
122
+ Use scores for time-based tracking and priority systems:
123
+
124
+ ```ruby
125
+ class Timeline < Familia::Horreum
126
+ feature :relationships
127
+ sorted_set :events # Scored collection
128
+ end
129
+
130
+ class Event < Familia::Horreum
131
+ feature :relationships
132
+ field :timestamp, :priority
133
+ participates_in Timeline, :events, score: :timestamp
134
+ end
135
+
136
+ # Automatic scoring when relationships established
137
+ timeline.events << event # Uses event.timestamp as score
138
+
139
+ # Time-based and priority queries
140
+ recent = timeline.events.range_by_score((Time.now - 1.hour).to_i, '+inf')
141
+ top_priority = project.tasks.range(0, 4, order: 'DESC') # Highest priority first
142
+ ```
143
+
144
+ **Common Scoring Patterns:**
145
+ - **Timestamps** for chronological ordering
146
+ - **Priority levels** for ranking systems
147
+ - **User ratings** for recommendation systems
148
+ - **Custom lambdas** for complex scoring logic
149
+
150
+ ## Best Practices
151
+
152
+ **Performance:**
153
+ - Use `merge([id1, id2, id3])` for bulk additions
154
+ - Use `multiget(*ids)` for efficient bulk loading
155
+ - Use pagination: `collection.range(0, 9)` instead of loading all
156
+
157
+ **Lifecycle Management:**
158
+ ```ruby
159
+ def destroy
160
+ cleanup_relationships # Remove from all relationships first
161
+ super
162
+ end
163
+ ```
164
+
165
+ **Validation:**
166
+ ```ruby
167
+ def add_member(user_id)
168
+ raise "Team is full" if members.size >= max_members
169
+ members << user_id
170
+ end
171
+ ```
172
+
173
+ ## Common Patterns
174
+
175
+ **Conditional Scoring:**
176
+ ```ruby
177
+ class_participates_in :active_users,
178
+ score: ->(user) { user.active? ? user.last_activity : 0 }
179
+ ```
180
+
181
+ **Bidirectional Updates:**
182
+ ```ruby
183
+ # ✅ Automatic bidirectional
184
+ customer.domains << domain
185
+
186
+ # ❌ Manual (avoid)
187
+ customer.domains.add(domain.identifier)
188
+ ```
189
+
190
+ ---
191
+
192
+ ## See Also
193
+
194
+ - **[Technical Reference](../reference/api-technical.md#relationships-feature-v200-pre7)** - Implementation details and advanced patterns
195
+ - **[Relationship Methods Guide](feature-relationships-methods.md)** - Complete method reference
196
+ - **[Feature System Guide](feature-system.md)** - Understanding Familia's feature architecture
197
+ - **[Implementation Guide](implementation.md)** - Production deployment and configuration patterns
198
+
199
+ > [!NOTE]
200
+ > **Next Steps:** Once you're comfortable with basic relationships, explore the [Relationship Methods Guide](feature-relationships-methods.md) for detailed method references, or check the [Technical Reference](../reference/api-technical.md#relationships-feature-v200-pre7) for advanced patterns like permission encoding and performance optimization.
@@ -776,11 +776,11 @@ module Familia::Features::DebugLogging
776
776
 
777
777
  base.define_singleton_method(:feature) do |name|
778
778
  Familia.ld "[DEBUG] Loading feature #{name} on #{self}"
779
- start_time = Time.now
779
+ start_time = Familia.now
780
780
 
781
781
  result = original_feature_method.call(name)
782
782
 
783
- load_time = (Time.now - start_time) * 1000
783
+ load_time = (Familia.now - start_time) * 1000
784
784
  Familia.ld "[DEBUG] Feature #{name} loaded in #{load_time.round(2)}ms"
785
785
 
786
786
  result
@@ -792,11 +792,11 @@ module Familia::Features::DebugLogging
792
792
  return block.call unless Familia.debug?
793
793
 
794
794
  Familia.ld "[DEBUG] Calling #{method_name} on #{self}"
795
- start_time = Time.now
795
+ start_time = Familia.now
796
796
 
797
797
  result = block.call
798
798
 
799
- duration = (Time.now - start_time) * 1000
799
+ duration = (Familia.now - start_time) * 1000
800
800
  Familia.ld "[DEBUG] #{method_name} completed in #{duration.round(2)}ms"
801
801
 
802
802
  result
@@ -113,7 +113,7 @@ class Customer < Familia::Horreum
113
113
  field :custid, :name, :email
114
114
 
115
115
  # Define relationship collections
116
- tracked_in :active_users, type: :sorted_set
116
+ participates_in :active_users, type: :sorted_set
117
117
  indexed_by :email_lookup, field: :email
118
118
  set :domains
119
119
  end
@@ -125,7 +125,7 @@ class Domain < Familia::Horreum
125
125
  field :domain_id, :name
126
126
 
127
127
  # Declare membership in customer collections
128
- member_of Customer, :domains, type: :set
128
+ participates_in Customer, :domains, type: :set
129
129
  end
130
130
 
131
131
  # Usage
@@ -165,7 +165,7 @@ module Familia
165
165
  module Features
166
166
  module MyCustomFeature
167
167
  def self.included(base)
168
- Familia.trace :LOADED, self, base, caller(1..1) if Familia.debug?
168
+ Familia.trace :LOADED, self, base if Familia.debug?
169
169
  base.extend ClassMethods
170
170
  base.prepend InstanceMethods # Use prepend for method interception
171
171
  end
@@ -213,12 +213,12 @@ module Familia
213
213
 
214
214
  module ClassMethods
215
215
  def enable_audit_for(*field_names)
216
- @audited_fields ||= Set.new
216
+ @audited_fields ||= ::Set.new
217
217
  @audited_fields.merge(field_names.map(&:to_sym))
218
218
  end
219
219
 
220
220
  def audited_fields
221
- @audited_fields || Set.new
221
+ @audited_fields || ::Set.new
222
222
  end
223
223
  end
224
224
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Transient fields provide secure handling of sensitive runtime data that should never be persisted to Redis/Valkey. Unlike encrypted fields, transient fields exist only in memory and are automatically wrapped in `RedactedString` for security.
5
+ Transient fields provide secure handling of sensitive runtime data that should never be persisted to Valkey/Redis. Unlike encrypted fields, transient fields exist only in memory and are automatically wrapped in `RedactedString` for security.
6
6
 
7
7
  ## When to Use Transient Fields
8
8
 
@@ -270,7 +270,7 @@ end
270
270
 
271
271
  | Feature | Encrypted Fields | Transient Fields |
272
272
  |---------|------------------|------------------|
273
- | **Persistence** | Saved to Redis/Valkey | Memory only |
273
+ | **Persistence** | Saved to Valkey/Redis | Memory only |
274
274
  | **Encryption** | AES/XChaCha20 | None (not stored) |
275
275
  | **Use Case** | Long-term secrets | Runtime secrets |
276
276
  | **Access** | Automatic decrypt | RedactedString wrapper |
@@ -5,8 +5,8 @@
5
5
  The encrypted fields feature uses a modular provider system with field transformation hooks:
6
6
 
7
7
  ```
8
- User Input → Field Setter → Provider Selection → Encryption → Redis/Valkey
9
- Redis/Valkey → Algorithm Detection → Decryption → Field Getter → User Output
8
+ User Input → Field Setter → Provider Selection → Encryption → Valkey/Redis
9
+ Valkey/Redis → Algorithm Detection → Decryption → Field Getter → User Output
10
10
  ```
11
11
 
12
12
  ### Provider Architecture
@@ -247,7 +247,7 @@ it "encrypts sensitive fields", :encryption do
247
247
  user = User.create(favorite_snack: "leftover pizza")
248
248
 
249
249
  # Verify encryption in Redis
250
- raw_value = redis.hget(user.rediskey, "favorite_snack")
250
+ raw_value = redis.hget(user.dbkey, "favorite_snack")
251
251
  expect(raw_value).not_to include("leftover pizza")
252
252
  expect(JSON.parse(raw_value)).to have_key("ciphertext")
253
253
  end
@@ -0,0 +1,176 @@
1
+ # Familia v2.0 Documentation
2
+
3
+ Welcome to the comprehensive documentation for Familia v2.0. This guide collection provides detailed explanations of all major features including security, connection management, architecture, and object relationships.
4
+
5
+ > **📖 Documentation Layers**
6
+ > - **[Overview](../overview.md)** - Conceptual introduction and getting started
7
+ > - **[Technical Reference](../reference/api-technical.md)** - Implementation patterns and technical details
8
+ > - **This Guide Collection** - Deep-dive topic guides with detailed prose and examples
9
+
10
+ ## 📚 Guide Structure
11
+
12
+ ### 🏗️ Architecture & System Design
13
+
14
+ 4. **[Feature System](feature-system.md)** - Modular architecture with dependencies and autoloader patterns
15
+ 5. **[Feature System for Developers](feature-system-devs.md)** - Advanced feature development patterns
16
+ 3. **[Security Model](security-model.md)** - Cryptographic design and Ruby memory considerations
17
+ 6. **[Connection Pooling](config-connection-pooling.md)** - Provider pattern for efficient Redis/Valkey pooling
18
+ 7. **[Core Field System](core-field-system.md)** - Field definitions and data type mappings
19
+
20
+
21
+ ### 🔐 Special Field Types
22
+
23
+ 1. **[Encrypted Fields](feature-encrypted-fields.md)** - Persistent encrypted storage with modular providers
24
+ 2. **[Transient Fields](feature-transient-fields.md)** - Non-persistent secure data handling with RedactedString
25
+ 10. **[Object Identifiers](feature-object-identifiers.md)** - Automatic ID generation with configurable strategies _(new!)_
26
+ 11. **[External Identifiers](feature-external-identifiers.md)** - Integration with external systems and legacy data _(new!)_
27
+
28
+
29
+ ### 🔗 Object Relationships & Identifiers
30
+
31
+ 8. **[Relationships](feature-relationships.md)** - Object relationships and membership system
32
+ 9. **[Relationship Methods](feature-relationships-methods.md)** - Detailed method reference for relationships
33
+
34
+ ### ⏱️ Time & Analytics Features
35
+
36
+ 12. **[Expiration](feature-expiration.md)** - TTL management and cascading expiration
37
+ 13. **[Quantization](feature-quantization.md)** - Time-based data bucketing for analytics
38
+ 14. **[Time Utilities](time-utilities.md)** - Time manipulation and formatting utilities
39
+
40
+ ### 🛠️ Implementation & Usage
41
+
42
+ 15. **[Implementation Guide](implementation.md)** - Advanced configuration and usage patterns
43
+
44
+ ## 🚀 Quick Start Examples
45
+
46
+ ### Encrypted Fields (Persistent)
47
+ ```ruby
48
+ class User < Familia::Horreum
49
+ feature :encrypted_fields
50
+ encrypted_field :secret_recipe
51
+ end
52
+
53
+ # Configure encryption
54
+ Familia.configure do |config|
55
+ config.encryption_keys = { v1: ENV['FAMILIA_ENCRYPTION_KEY'] }
56
+ config.current_key_version = :v1
57
+ end
58
+
59
+ user = User.new(secret_recipe: "donna's cookies")
60
+ user.save
61
+ user.secret_recipe # => "donna's cookies" (automatically decrypted)
62
+ ```
63
+
64
+ ### Feature System (Modular)
65
+ ```ruby
66
+ class Customer < Familia::Horreum
67
+ feature :safe_dump # API-safe serialization
68
+ feature :expiration # TTL support
69
+ feature :encrypted_fields # Secure storage
70
+
71
+ field :name, :email
72
+ encrypted_field :api_key
73
+ default_expiration 24.hours
74
+ safe_dump_fields :name, :email
75
+ end
76
+ ```
77
+
78
+ ### Connection Pooling (Performance)
79
+ ```ruby
80
+ # Configure connection provider for multi-database pooling
81
+ Familia.connection_provider = lambda do |uri|
82
+ parsed = URI.parse(uri) # => URI::Redis
83
+ pool_key = "#{parsed.host}:#{parsed.port}/#{parsed.db || 0}"
84
+
85
+ @pools[pool_key] ||= ConnectionPool.new(size: 10) do
86
+ Redis.new(host: parsed.host, port: parsed.port, db: parsed.db || 0)
87
+ end
88
+
89
+ @pools[pool_key].with { |conn| conn }
90
+ end
91
+ ```
92
+
93
+ ### Object Relationships
94
+ ```ruby
95
+ class Customer < Familia::Horreum
96
+ feature :relationships
97
+ identifier_field :custid
98
+ field :custid, :name, :email
99
+ set :domains # Customer collections
100
+ end
101
+
102
+ class Domain < Familia::Horreum
103
+ feature :relationships
104
+ identifier_field :domain_id
105
+ field :domain_id, :name, :dns_zone
106
+ participates_in Customer, :domains # Bidirectional membership
107
+ end
108
+
109
+ # Create objects and establish relationships
110
+ customer = Customer.new(custid: "cust123", name: "Acme Corp")
111
+ domain = Domain.new(domain_id: "dom456", name: "acme.com")
112
+
113
+ # Ruby-like syntax for relationships
114
+ customer.domains << domain # Clean collection syntax
115
+
116
+ # Query relationships
117
+ domain.in_customer_domains?(customer.custid) # => true
118
+ customer.domains.member?(domain.identifier) # => true
119
+ ```
120
+
121
+ ### Object Identifiers (Auto-generation)
122
+ ```ruby
123
+ class Document < Familia::Horreum
124
+ feature :object_identifier, generator: :uuid_v4
125
+ field :title, :content
126
+ end
127
+
128
+ class Session < Familia::Horreum
129
+ feature :object_identifier, generator: :hex
130
+ field :user_id, :data
131
+ end
132
+
133
+ # Automatic ID generation
134
+ doc = Document.create(title: "My Document")
135
+ doc.objid # => "f47ac10b-58cc-4372-a567-0e02b2c3d479"
136
+
137
+ session = Session.create(user_id: "123")
138
+ session.objid # => "a1b2c3d4e5f6"
139
+ ```
140
+
141
+ ### External Identifiers (Legacy Integration)
142
+ ```ruby
143
+ class ExternalUser < Familia::Horreum
144
+ feature :external_identifier
145
+ field :internal_id, :external_id, :name
146
+ end
147
+
148
+ # Map external system IDs to internal objects
149
+ user = ExternalUser.create(
150
+ internal_id: SecureRandom.uuid,
151
+ external_id: "ext_12345",
152
+ name: "Legacy User"
153
+ )
154
+
155
+ # Find by external ID
156
+ found = ExternalUser.find_by_external_id("ext_12345")
157
+ ```
158
+
159
+ ### Quantization (Analytics)
160
+ ```ruby
161
+ class MetricsBucket < Familia::Horreum
162
+ feature :quantization
163
+ field :metric_key, :value_count
164
+ string :counter, quantize: [10.minutes, '%H:%M']
165
+ end
166
+
167
+ # Automatic time bucketing for analytics
168
+ MetricsBucket.record_event("page_view") # Groups into 10-min buckets
169
+ ```
170
+
171
+
172
+ ## Related Resources
173
+
174
+ - [Familia README](https://github.com/delano/familia) - Main project documentation
175
+ - [Issue #57](https://github.com/delano/familia/issues/57) - Original feature proposal
176
+ - [Issue #58](https://github.com/delano/familia/issues/58) - Wiki documentation tracking
@@ -138,7 +138,7 @@ Ruby provides **NO** memory safety guarantees for cryptographic secrets. This af
138
138
 
139
139
  Both providers attempt best-effort memory clearing:
140
140
  - Call `.clear` on sensitive strings after use
141
- - Set variables to `nil` when done
141
+ - UnsortedSet variables to `nil` when done
142
142
  - Use finalizers for cleanup (no guarantees)
143
143
 
144
144
  **Recommendation**: For production systems with high-security requirements, consider:
@@ -6,7 +6,7 @@ This guide covers migrating from Familia v1.x to the v2.0.0-pre foundation relea
6
6
 
7
7
  The v2.0.0-pre series represents a major modernization of Familia with:
8
8
  - Complete API redesign for clarity and consistency
9
- - Valkey compatibility alongside Redis support
9
+ - Valkey compatibility alongside Valkey/Redis support
10
10
  - Ruby 3.4+ modernization with improved thread safety
11
11
  - New connection pooling architecture
12
12
 
@@ -124,7 +124,7 @@ end
124
124
 
125
125
  class Customer < Familia::Horreum
126
126
  module Features
127
- include Familia::Autoloader
127
+ include Familia::Features::Autoloader
128
128
  # Automatically discovers and loads all *.rb files from customer/features/
129
129
  end
130
130
  end
@@ -172,7 +172,7 @@ module CommonFields
172
172
  field :version
173
173
 
174
174
  def touch_updated
175
- self.updated = Time.now.to_i
175
+ self.updated = Familia.now.to_i
176
176
  end
177
177
  end
178
178
 
@@ -202,7 +202,7 @@ safe_dump_field :field2
202
202
  safe_dump_field :field3, ->(obj) { ... }
203
203
  ```
204
204
 
205
- ### 2. Set Up Auto-loading (Optional)
205
+ ### 2. UnsortedSet Up Auto-loading (Optional)
206
206
  If you have project-specific features, set up auto-loading:
207
207
 
208
208
  ```ruby
@@ -210,7 +210,7 @@ If you have project-specific features, set up auto-loading:
210
210
  module YourProject
211
211
  class ModelName < Familia::Horreum
212
212
  module Features
213
- include Familia::Autoloader
213
+ include Familia::Features::Autoloader
214
214
  end
215
215
  end
216
216
  end
@@ -14,7 +14,7 @@ The new `Familia::VerifiableIdentifier` module allows applications to create and
14
14
  class Customer < Familia::Horreum
15
15
  feature :verifiable_identifier
16
16
 
17
- # Required: Set the HMAC secret (do this once in your app initialization)
17
+ # Required: UnsortedSet the HMAC secret (do this once in your app initialization)
18
18
  # Generate with: SecureRandom.hex(64)
19
19
  ENV['VERIFIABLE_ID_HMAC_SECRET'] = 'your_64_character_hex_secret'
20
20
  end
@@ -85,7 +85,7 @@ puts "VERIFIABLE_ID_HMAC_SECRET=#{secret}"
85
85
  #### Environment Configuration
86
86
  ```ruby
87
87
  # config/application.rb or equivalent
88
- # Set this BEFORE any VerifiableIdentifier usage
88
+ # UnsortedSet this BEFORE any VerifiableIdentifier usage
89
89
  ENV['VERIFIABLE_ID_HMAC_SECRET'] = Rails.application.credentials.verifiable_id_secret
90
90
 
91
91
  # Or configure programmatically
@@ -68,7 +68,7 @@ Common issues:
68
68
 
69
69
  The Feature Autoloading System consists of two key components:
70
70
 
71
- ### Familia::Autoloader
71
+ ### Familia::Features::Autoloader
72
72
  A utility module providing shared file loading functionality:
73
73
  - Handles Dir.glob pattern matching and file loading
74
74
  - Provides consistent debug logging across all autoloading scenarios
@@ -51,7 +51,7 @@ end
51
51
 
52
52
  ### 3. Update Serialization Code
53
53
 
54
- Handle `RedactedString` in serialization:
54
+ Handle `ConcealedString` in serialization:
55
55
 
56
56
  **Before:**
57
57
  ```ruby
@@ -63,16 +63,16 @@ end
63
63
  **After:**
64
64
  ```ruby
65
65
  def to_json
66
- # RedactedString automatically excluded from serialization
66
+ # ConcealedString automatically excluded from serialization
67
67
  { name: name }.to_json # password field omitted if transient
68
68
  end
69
69
  ```
70
70
 
71
- **Manual RedactedString Handling:**
71
+ **Manual ConcealedString Handling:**
72
72
  ```ruby
73
73
  # Access original value when needed
74
74
  password.reveal # Returns actual string value
75
- password.redacted? # Returns true if redacted
75
+ password.cleared? # Returns true if cleared from memory
76
76
  ```
77
77
 
78
78
  ### 4. Implement Key Rotation Procedures
@@ -80,19 +80,38 @@ password.redacted? # Returns true if redacted
80
80
  **Rotation Process:**
81
81
  1. Add new key version to configuration
82
82
  2. Update `current_key_version`
83
- 3. Re-encrypt existing data gradually
84
- 4. Remove old keys after migration complete
83
+ 3. Re-encrypt existing data using `re_encrypt_fields!`
84
+ 4. Verify migration completion
85
+ 5. Remove old keys after migration complete
85
86
 
86
87
  **Example Rotation Script:**
87
88
  ```ruby
88
- # Add new key version
89
- Familia.config.encryption_keys[:v3] = 'new-key'
90
- Familia.config.current_key_version = :v3
89
+ # Step 1: Add new key version
90
+ Familia.configure do |config|
91
+ config.encryption_keys = {
92
+ v2: ENV['OLD_ENCRYPTION_KEY'],
93
+ v3: ENV['NEW_ENCRYPTION_KEY']
94
+ }
95
+ config.current_key_version = :v3
96
+ end
97
+
98
+ # Step 2: Validate configuration
99
+ Familia::Encryption.validate_configuration!
91
100
 
92
- # Re-encrypt existing records
101
+ # Step 3: Re-encrypt existing records
93
102
  Vault.all.each do |vault|
94
- vault.save # Automatically uses new key version
103
+ vault.re_encrypt_fields! # Re-encrypts with current key
104
+ vault.save
95
105
  end
106
+
107
+ # Step 4: Verify migration
108
+ Vault.all.each do |vault|
109
+ status = vault.encrypted_fields_status
110
+ puts "Vault #{vault.identifier}: #{status}"
111
+ end
112
+
113
+ # Step 5: Remove old key (after verification)
114
+ Familia.config.encryption_keys.delete(:v2)
96
115
  ```
97
116
 
98
117
  ## Security Best Practices
@@ -101,7 +120,9 @@ end
101
120
  - **Key Rotation:** Rotate encryption keys regularly (quarterly/annually)
102
121
  - **Field Selection:** Only encrypt fields that truly need protection
103
122
  - **Memory Clearing:** Use transient fields for temporary sensitive data
104
- - **Logging:** Verify RedactedString prevents accidental logging
123
+ - **Logging:** Verify ConcealedString prevents accidental logging
124
+ - **Configuration Validation:** Use `validate_configuration!` before production
125
+ - **Monitoring:** Use `encrypted_fields_status` to track encryption state
105
126
 
106
127
  ## Next Steps
107
128
 
@@ -35,7 +35,7 @@ The Horreum class structure was reorganized for better maintainability:
35
35
  - `Familia::Horreum::Core` - Essential functionality
36
36
  - `Familia::Horreum::ClassMethods` - Class-level methods
37
37
  - `Familia::Horreum::Serialization` - Object serialization
38
- - `Familia::Horreum::Commands` - Redis command wrappers
38
+ - `Familia::Horreum::Commands` - Valkey/Redis command wrappers
39
39
 
40
40
  **Feature System Improvements:**
41
41
  - Dependency management between features
@@ -56,7 +56,7 @@ end
56
56
  ```
57
57
 
58
58
  **Improved Data Consistency:**
59
- - Automatic retry for transient Redis connection issues
59
+ - Automatic retry for transient Valkey/Redis connection issues
60
60
  - Better handling of concurrent modifications
61
61
  - Enhanced validation before persistence operations
62
62