familia 2.0.0.pre18 → 2.0.0.pre21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +4 -9
  3. data/.github/workflows/code-smells.yml +64 -3
  4. data/.pre-commit-config.yaml +8 -6
  5. data/.reek.yml +10 -9
  6. data/.rubocop.yml +4 -0
  7. data/CHANGELOG.rst +205 -88
  8. data/CLAUDE.md +62 -10
  9. data/Gemfile +3 -3
  10. data/Gemfile.lock +27 -62
  11. data/README.md +39 -0
  12. data/bin/try +16 -0
  13. data/bin/tryouts +16 -0
  14. data/changelog.d/20251105_flexible_external_identifier_format.rst +66 -0
  15. data/changelog.d/20251107_112554_delano_179_participation_asymmetry.rst +44 -0
  16. data/changelog.d/20251107_213121_delano_fix_thread_safety_races_011CUumCP492Twxm4NLt2FvL.rst +20 -0
  17. data/changelog.d/20251107_fix_participates_in_symbol_resolution.rst +91 -0
  18. data/changelog.d/20251107_optimized_redis_exists_checks.rst +94 -0
  19. data/changelog.d/20251108_frozen_string_literal_pragma.rst +44 -0
  20. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  21. data/docs/guides/encryption.md +486 -0
  22. data/docs/guides/feature-encrypted-fields.md +123 -7
  23. data/docs/guides/feature-expiration.md +177 -133
  24. data/docs/guides/feature-external-identifiers.md +415 -443
  25. data/docs/guides/feature-object-identifiers.md +400 -269
  26. data/docs/guides/feature-quantization.md +120 -6
  27. data/docs/guides/feature-relationships-indexing.md +318 -0
  28. data/docs/guides/feature-relationships-methods.md +146 -604
  29. data/docs/guides/feature-relationships-participation.md +263 -0
  30. data/docs/guides/feature-relationships.md +118 -136
  31. data/docs/guides/feature-system-devs.md +176 -693
  32. data/docs/guides/feature-system.md +119 -6
  33. data/docs/guides/feature-transient-fields.md +81 -0
  34. data/docs/guides/field-system.md +778 -0
  35. data/docs/guides/index.md +32 -15
  36. data/docs/guides/logging.md +187 -0
  37. data/docs/guides/optimized-loading.md +674 -0
  38. data/docs/guides/thread-safety-monitoring.md +61 -0
  39. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  40. data/docs/migrating/v2.0.0-pre19.md +197 -0
  41. data/docs/migrating/v2.0.0-pre22.md +241 -0
  42. data/docs/overview.md +7 -9
  43. data/docs/reference/api-technical.md +267 -320
  44. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  45. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  46. data/examples/autoloader/mega_customer.rb +2 -0
  47. data/examples/datatype_standalone.rb +282 -0
  48. data/examples/encrypted_fields.rb +2 -1
  49. data/examples/json_usage_patterns.rb +2 -0
  50. data/examples/relationships.rb +3 -0
  51. data/examples/safe_dump.rb +2 -1
  52. data/examples/sampling_demo.rb +53 -0
  53. data/examples/single_connection_transaction_confusions.rb +2 -1
  54. data/familia.gemspec +2 -1
  55. data/lib/familia/base.rb +2 -0
  56. data/lib/familia/connection/behavior.rb +254 -0
  57. data/lib/familia/connection/handlers.rb +97 -0
  58. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  59. data/lib/familia/connection/middleware.rb +34 -24
  60. data/lib/familia/connection/operation_core.rb +3 -1
  61. data/lib/familia/connection/operations.rb +2 -0
  62. data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +4 -2
  63. data/lib/familia/connection/transaction_core.rb +75 -9
  64. data/lib/familia/connection.rb +21 -5
  65. data/lib/familia/data_type/class_methods.rb +3 -1
  66. data/lib/familia/data_type/connection.rb +153 -7
  67. data/lib/familia/data_type/database_commands.rb +9 -4
  68. data/lib/familia/data_type/serialization.rb +10 -4
  69. data/lib/familia/data_type/settings.rb +2 -0
  70. data/lib/familia/data_type/types/counter.rb +2 -0
  71. data/lib/familia/data_type/types/hashkey.rb +8 -6
  72. data/lib/familia/data_type/types/listkey.rb +2 -0
  73. data/lib/familia/data_type/types/lock.rb +2 -0
  74. data/lib/familia/data_type/types/sorted_set.rb +2 -0
  75. data/lib/familia/data_type/types/stringkey.rb +2 -0
  76. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  77. data/lib/familia/data_type.rb +2 -0
  78. data/lib/familia/encryption/encrypted_data.rb +4 -2
  79. data/lib/familia/encryption/manager.rb +2 -0
  80. data/lib/familia/encryption/provider.rb +2 -0
  81. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  82. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  83. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  84. data/lib/familia/encryption/registry.rb +2 -0
  85. data/lib/familia/encryption/request_cache.rb +2 -0
  86. data/lib/familia/encryption.rb +9 -2
  87. data/lib/familia/errors.rb +53 -14
  88. data/lib/familia/features/autoloader.rb +2 -0
  89. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  90. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  91. data/lib/familia/features/encrypted_fields.rb +2 -2
  92. data/lib/familia/features/expiration/extensions.rb +11 -11
  93. data/lib/familia/features/expiration.rb +29 -21
  94. data/lib/familia/features/external_identifier.rb +33 -7
  95. data/lib/familia/features/object_identifier.rb +2 -0
  96. data/lib/familia/features/quantization.rb +3 -1
  97. data/lib/familia/features/relationships/README.md +3 -1
  98. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  99. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +177 -47
  100. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  101. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +203 -63
  102. data/lib/familia/features/relationships/indexing.rb +40 -42
  103. data/lib/familia/features/relationships/indexing_relationship.rb +17 -5
  104. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  105. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  106. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  107. data/lib/familia/features/relationships/participation.rb +155 -69
  108. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  109. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  110. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  111. data/lib/familia/features/relationships.rb +5 -3
  112. data/lib/familia/features/safe_dump.rb +2 -0
  113. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  114. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  115. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  116. data/lib/familia/features/transient_fields.rb +2 -0
  117. data/lib/familia/features.rb +2 -0
  118. data/lib/familia/field_type.rb +5 -2
  119. data/lib/familia/horreum/connection.rb +28 -36
  120. data/lib/familia/horreum/database_commands.rb +131 -10
  121. data/lib/familia/horreum/definition.rb +18 -7
  122. data/lib/familia/horreum/management.rb +233 -57
  123. data/lib/familia/horreum/persistence.rb +314 -122
  124. data/lib/familia/horreum/related_fields.rb +2 -0
  125. data/lib/familia/horreum/serialization.rb +26 -4
  126. data/lib/familia/horreum/settings.rb +2 -0
  127. data/lib/familia/horreum/utils.rb +2 -8
  128. data/lib/familia/horreum.rb +46 -13
  129. data/lib/familia/identifier_extractor.rb +2 -0
  130. data/lib/familia/instrumentation.rb +156 -0
  131. data/lib/familia/json_serializer.rb +2 -0
  132. data/lib/familia/logging.rb +94 -37
  133. data/lib/familia/refinements/dear_json.rb +2 -0
  134. data/lib/familia/refinements/stylize_words.rb +2 -14
  135. data/lib/familia/refinements/time_literals.rb +2 -0
  136. data/lib/familia/refinements.rb +2 -0
  137. data/lib/familia/secure_identifier.rb +10 -2
  138. data/lib/familia/settings.rb +9 -7
  139. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  140. data/lib/familia/thread_safety/monitor.rb +328 -0
  141. data/lib/familia/utils.rb +13 -0
  142. data/lib/familia/verifiable_identifier.rb +3 -1
  143. data/lib/familia/version.rb +3 -1
  144. data/lib/familia.rb +31 -4
  145. data/lib/middleware/database_command_counter.rb +152 -0
  146. data/lib/middleware/database_logger.rb +325 -129
  147. data/lib/multi_result.rb +2 -0
  148. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  149. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  150. data/try/edge_cases/json_serialization_try.rb +2 -0
  151. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  152. data/try/edge_cases/race_conditions_try.rb +4 -0
  153. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  154. data/try/edge_cases/string_coercion_try.rb +6 -4
  155. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  156. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  157. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  158. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  159. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  160. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  161. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  162. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  163. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  164. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  165. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  166. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  167. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  168. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  169. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  170. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  171. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  172. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  173. data/try/features/encryption/config_persistence_try.rb +4 -0
  174. data/try/features/encryption/core_try.rb +4 -0
  175. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  176. data/try/features/encryption/module_loading_try.rb +4 -0
  177. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  178. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  179. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  180. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  181. data/try/features/expiration/expiration_try.rb +5 -1
  182. data/try/features/external_identifier/external_identifier_try.rb +171 -8
  183. data/try/features/feature_dependencies_try.rb +2 -0
  184. data/try/features/feature_improvements_try.rb +2 -0
  185. data/try/features/field_groups_try.rb +2 -0
  186. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  187. data/try/features/object_identifier/object_identifier_try.rb +2 -0
  188. data/try/features/quantization/quantization_try.rb +4 -0
  189. data/try/features/real_feature_integration_try.rb +2 -0
  190. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  191. data/try/features/relationships/indexing_rebuild_try.rb +600 -0
  192. data/try/features/relationships/indexing_try.rb +30 -4
  193. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  194. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  195. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  196. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  197. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  198. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  199. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  200. data/try/features/relationships/relationships_api_changes_try.rb +6 -4
  201. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  202. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  203. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  204. data/try/features/relationships/relationships_performance_try.rb +4 -0
  205. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  206. data/try/features/relationships/relationships_try.rb +6 -4
  207. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  208. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  209. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  210. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  211. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  212. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  213. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  214. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  215. data/try/integration/connection/fiber_context_preservation_try.rb +7 -3
  216. data/try/integration/connection/handler_constraints_try.rb +4 -0
  217. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  218. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  219. data/try/integration/connection/operation_mode_guards_try.rb +5 -1
  220. data/try/integration/connection/pipeline_fallback_integration_try.rb +15 -12
  221. data/try/integration/connection/pools_try.rb +4 -0
  222. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  223. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  224. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  225. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  226. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  227. data/try/integration/connection/transaction_modes_try.rb +4 -0
  228. data/try/integration/conventional_inheritance_try.rb +4 -0
  229. data/try/integration/create_method_try.rb +26 -22
  230. data/try/integration/cross_component_try.rb +4 -0
  231. data/try/integration/data_types/datatype_pipelines_try.rb +108 -0
  232. data/try/integration/data_types/datatype_transactions_try.rb +251 -0
  233. data/try/integration/database_consistency_try.rb +4 -0
  234. data/try/integration/familia_extended_try.rb +4 -0
  235. data/try/integration/familia_members_methods_try.rb +4 -0
  236. data/try/integration/models/customer_safe_dump_try.rb +9 -1
  237. data/try/integration/models/customer_try.rb +4 -0
  238. data/try/integration/models/datatype_base_try.rb +4 -0
  239. data/try/integration/models/familia_object_try.rb +5 -1
  240. data/try/integration/persistence_operations_try.rb +166 -10
  241. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  242. data/try/integration/save_methods_consistency_try.rb +241 -0
  243. data/try/integration/scenarios_try.rb +4 -0
  244. data/try/integration/secure_identifier_try.rb +4 -0
  245. data/try/integration/transaction_safety_core_try.rb +176 -0
  246. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  247. data/try/integration/verifiable_identifier_try.rb +4 -0
  248. data/try/investigation/pipeline_routing/README.md +228 -0
  249. data/try/performance/benchmarks_try.rb +4 -0
  250. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  251. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  252. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  253. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  254. data/try/support/debugging/debug_aad_process.rb +3 -0
  255. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  256. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  257. data/try/support/debugging/debug_context_aad.rb +3 -0
  258. data/try/support/debugging/debug_context_simple.rb +3 -0
  259. data/try/support/debugging/debug_cross_context.rb +3 -0
  260. data/try/support/debugging/debug_database_load.rb +3 -0
  261. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  262. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  263. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  264. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  265. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  266. data/try/support/debugging/debug_load_path.rb +3 -0
  267. data/try/support/debugging/debug_method_definition.rb +3 -0
  268. data/try/support/debugging/debug_method_resolution.rb +3 -0
  269. data/try/support/debugging/debug_minimal.rb +3 -0
  270. data/try/support/debugging/debug_provider.rb +3 -0
  271. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  272. data/try/support/debugging/debug_string_class.rb +3 -0
  273. data/try/support/debugging/debug_test.rb +3 -0
  274. data/try/support/debugging/debug_test_design.rb +3 -0
  275. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  276. data/try/support/debugging/provider_diagnostics.rb +4 -0
  277. data/try/support/helpers/test_cleanup.rb +4 -0
  278. data/try/support/helpers/test_helpers.rb +5 -0
  279. data/try/support/memory/memory_basic_test.rb +4 -0
  280. data/try/support/memory/memory_detailed_test.rb +4 -0
  281. data/try/support/memory/memory_search_for_string.rb +4 -0
  282. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  283. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  284. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  285. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  286. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  287. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  288. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  289. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  290. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  291. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  292. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  293. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  294. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  295. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  296. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  297. data/try/thread_safety/README.md +496 -0
  298. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  299. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  300. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  301. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  302. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  303. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  304. data/try/thread_safety/field_registration_race_try.rb +222 -0
  305. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  306. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  307. data/try/thread_safety/module_config_race_try.rb +175 -0
  308. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  309. data/try/unit/core/autoloader_try.rb +4 -0
  310. data/try/unit/core/base_enhancements_try.rb +4 -0
  311. data/try/unit/core/connection_try.rb +4 -0
  312. data/try/unit/core/errors_try.rb +4 -0
  313. data/try/unit/core/extensions_try.rb +4 -0
  314. data/try/unit/core/familia_logger_try.rb +2 -0
  315. data/try/unit/core/familia_try.rb +4 -0
  316. data/try/unit/core/middleware_sampling_try.rb +335 -0
  317. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  318. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  319. data/try/unit/core/middleware_try.rb +4 -0
  320. data/try/unit/core/settings_try.rb +4 -0
  321. data/try/unit/core/time_utils_try.rb +4 -0
  322. data/try/unit/core/tools_try.rb +4 -0
  323. data/try/unit/core/utils_try.rb +37 -0
  324. data/try/unit/data_types/boolean_try.rb +5 -1
  325. data/try/unit/data_types/counter_try.rb +4 -0
  326. data/try/unit/data_types/datatype_base_try.rb +4 -0
  327. data/try/unit/data_types/hash_try.rb +4 -0
  328. data/try/unit/data_types/list_try.rb +4 -0
  329. data/try/unit/data_types/lock_try.rb +4 -0
  330. data/try/unit/data_types/sorted_set_try.rb +4 -0
  331. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  332. data/try/unit/data_types/string_try.rb +5 -1
  333. data/try/unit/data_types/unsortedset_try.rb +4 -0
  334. data/try/unit/familia_resolve_class_try.rb +116 -0
  335. data/try/unit/horreum/auto_indexing_on_save_try.rb +36 -16
  336. data/try/unit/horreum/automatic_index_validation_try.rb +255 -0
  337. data/try/unit/horreum/base_try.rb +5 -1
  338. data/try/unit/horreum/class_methods_try.rb +6 -2
  339. data/try/unit/horreum/commands_try.rb +4 -0
  340. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  341. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +4 -0
  342. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  343. data/try/unit/horreum/field_categories_try.rb +4 -0
  344. data/try/unit/horreum/field_definition_try.rb +4 -0
  345. data/try/unit/horreum/initialization_try.rb +5 -1
  346. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  347. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  348. data/try/unit/horreum/relations_try.rb +8 -4
  349. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  350. data/try/unit/horreum/serialization_try.rb +6 -2
  351. data/try/unit/horreum/settings_try.rb +4 -0
  352. data/try/unit/horreum/unique_index_edge_cases_try.rb +380 -0
  353. data/try/unit/horreum/unique_index_guard_validation_try.rb +283 -0
  354. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  355. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  356. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  357. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  358. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  359. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  360. data/try/unit/thread_safety_monitor_try.rb +149 -0
  361. metadata +81 -14
  362. data/.github/workflows/code-quality.yml +0 -138
  363. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  364. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  365. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  366. data/docs/archive/README.md +0 -64
  367. data/docs/archive/api-reference.md +0 -333
  368. data/docs/guides/core-field-system.md +0 -806
  369. data/docs/guides/implementation.md +0 -276
  370. data/docs/guides/security-model.md +0 -183
@@ -1,5 +1,7 @@
1
1
  # lib/familia/horreum/utils.rb
2
2
  #
3
+ # frozen_string_literal: true
4
+
3
5
  module Familia
4
6
  # InstanceMethods - Module containing instance-level methods for Familia
5
7
  #
@@ -11,14 +13,6 @@ module Familia
11
13
  # Provides identifier handling, dbkey generation, and object inspection
12
14
  #
13
15
  module Utils
14
- # def uri
15
- # base_uri = self.class.uri || Familia.uri
16
- # u = base_uri.dup # make a copy to modify safely
17
- # u.logical_database = logical_database if logical_database
18
- # u.key = dbkey
19
- # u
20
- # end
21
-
22
16
  # +suffix+ is the value to be used at the end of the db key
23
17
  # (e.g. `customer:customer_id:scores` would have `scores` as the suffix
24
18
  # and `customer_id` would have been the identifier in that case).
@@ -1,4 +1,6 @@
1
1
  # lib/familia/horreum.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require_relative 'horreum/settings'
4
6
  require_relative 'horreum/connection'
@@ -51,7 +53,6 @@ module Familia
51
53
  include Familia::Base
52
54
  include Familia::Horreum::Persistence
53
55
  include Familia::Horreum::Serialization
54
- include Familia::Horreum::Connection
55
56
  include Familia::Horreum::DatabaseCommands
56
57
  include Familia::Horreum::Settings
57
58
  include Familia::Horreum::Utils
@@ -187,6 +188,7 @@ module Familia
187
188
  # `Session.new({sessid: "abc123", custid: "user456"})` # legacy hash (robust)
188
189
  #
189
190
  def initialize(*args, **kwargs)
191
+ start_time = Familia.now_in_μs if Familia.debug?
190
192
  Familia.trace :INITIALIZE, nil, "Initializing #{self.class}" if Familia.debug?
191
193
  initialize_relatives
192
194
 
@@ -226,23 +228,54 @@ module Familia
226
228
  # Default values are intentionally NOT set here
227
229
  end
228
230
 
229
- # Implementing classes can define an init method to do any
230
- # additional initialization. Notice that this is called
231
- # after the fields are set.
231
+ # Implementing classes can define an init method to do any additional
232
+ # initialization. Notice that this is called AFTER fields are set from
233
+ # kwargs, so kwargs have been consumed and are no longer available.
234
+ #
235
+ # IMPORTANT: Use ||= in init to apply defaults without overriding:
236
+ # def init
237
+ # @email ||= email # Preserves value already set
238
+ # @status ||= 'pending' # Applies default if nil
239
+ # end
240
+ #
232
241
  init
242
+
243
+ # Structured lifecycle logging and instrumentation
244
+ if Familia.debug? && start_time
245
+ duration = Familia.now_in_μs - start_time
246
+ Familia.debug "Horreum initialized",
247
+ class: self.class.name,
248
+ duration: duration,
249
+ identifier: (identifier rescue nil)
250
+
251
+ Familia::Instrumentation.notify_lifecycle(:initialize, self, duration: duration)
252
+ end
233
253
  end
234
254
 
235
- # Override this method in subclasses for custom initialization logic.
236
- # This is called AFTER fields are set and relatives are initialized.
255
+ # Initialization method called at the end of initialize
237
256
  #
238
- # DO NOT override initialize() - use this init() hook instead.
257
+ # Override this method to apply defaults, run validations, or setup
258
+ # callbacks. It's recommended to call super as other modules like
259
+ # features can also override init.
239
260
  #
240
- # Example:
241
- # def init(name = nil)
242
- # @name = name || SecureRandom.hex(4)
261
+ # IMPORTANT: The init method receieves no arguments. By the time this runs,
262
+ # all arguments to initialize have already been consumed and used to set
263
+ # fields. Use the ||= operator to preserve values already set:
264
+ #
265
+ # def init(email: nil, user_id: nil, **kwargs)
266
+ # @email ||= email # Preserves value from new()
267
+ # @user_id ||= user_id # Preserves value from new()
268
+ # @created_at ||= Familia.now # Applies default if not set
269
+ #
270
+ # # Example of additional initialization logic
271
+ # validate_email_format if @email
272
+ # setup_callbacks
243
273
  # end
244
- def init(*args, **kwargs)
245
- # Default no-op
274
+ #
275
+ # @return [void]
276
+ #
277
+ def init
278
+ # Default no-op - override in subclasses
246
279
  end
247
280
 
248
281
  # Sets up related Database objects for the instance
@@ -319,7 +352,7 @@ module Familia
319
352
  # the object with.
320
353
  # @return [Array] The list of field names that were updated.
321
354
  def naive_refresh(**fields)
322
- Familia.ld "[naive_refresh] #{self.class} #{dbkey} #{fields.keys}"
355
+ Familia.debug "[naive_refresh] #{self.class} #{dbkey} #{fields.keys}"
323
356
  initialize_with_keyword_args_deserialize_value(**fields)
324
357
  end
325
358
 
@@ -1,4 +1,6 @@
1
1
  # lib/familia/identifier_extractor.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  # IdentifierExtractor - Extracts identifiers from Familia objects for storage
@@ -0,0 +1,156 @@
1
+ # lib/familia/instrumentation.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require 'concurrent-ruby'
6
+
7
+ module Familia
8
+ # Provides instrumentation hooks for observability into Familia operations.
9
+ #
10
+ # This module allows applications to register callbacks for various events
11
+ # in Familia's lifecycle, enabling audit trails, performance monitoring,
12
+ # and operational observability.
13
+ #
14
+ # @example Basic usage
15
+ # Familia.on_command do |cmd, duration, context|
16
+ # puts "Redis command: #{cmd} (#{duration}μs)"
17
+ # end
18
+ #
19
+ # @example Audit trail for secrets service
20
+ # Familia.on_lifecycle do |event, instance, context|
21
+ # case event
22
+ # when :save
23
+ # AuditLog.create!(
24
+ # event: 'secret_saved',
25
+ # secret_id: instance.identifier,
26
+ # user_id: RequestContext.current_user_id
27
+ # )
28
+ # end
29
+ # end
30
+ #
31
+ module Instrumentation
32
+ @hooks = {
33
+ command: Concurrent::Array.new,
34
+ pipeline: Concurrent::Array.new,
35
+ lifecycle: Concurrent::Array.new,
36
+ error: Concurrent::Array.new
37
+ }
38
+
39
+ class << self
40
+ # Register a callback for Redis command execution.
41
+ #
42
+ # @yield [cmd, duration, context] Callback block
43
+ # @yieldparam cmd [String] The Redis command name (e.g., "SET", "ZADD")
44
+ # @yieldparam duration [Integer] Command execution duration in microseconds
45
+ # @yieldparam context [Hash] Additional context including:
46
+ # - :full_command [Array] Complete command with arguments
47
+ # - :db [Integer] Database number
48
+ # - :connection_id [String] Connection identifier
49
+ #
50
+ # @example
51
+ # Familia.on_command do |cmd, duration, ctx|
52
+ # StatsD.timing("familia.command.#{cmd.downcase}", duration / 1000.0)
53
+ # end
54
+ #
55
+ def on_command(&block)
56
+ @hooks[:command] << block
57
+ end
58
+
59
+ # Register a callback for pipelined Redis operations.
60
+ #
61
+ # @yield [command_count, duration, context] Callback block
62
+ # @yieldparam command_count [Integer] Number of commands in the pipeline
63
+ # @yieldparam duration [Integer] Pipeline execution duration in microseconds
64
+ # @yieldparam context [Hash] Additional context
65
+ #
66
+ # @example
67
+ # Familia.on_pipeline do |count, duration, ctx|
68
+ # StatsD.timing("familia.pipeline", duration / 1000.0)
69
+ # StatsD.gauge("familia.pipeline.commands", count)
70
+ # end
71
+ #
72
+ def on_pipeline(&block)
73
+ @hooks[:pipeline] << block
74
+ end
75
+
76
+ # Register a callback for Horreum lifecycle events.
77
+ #
78
+ # @yield [event, instance, context] Callback block
79
+ # @yieldparam event [Symbol] Lifecycle event (:initialize, :save, :destroy)
80
+ # @yieldparam instance [Familia::Horreum] The object instance
81
+ # @yieldparam context [Hash] Additional context including:
82
+ # - :duration [Integer] Operation duration in microseconds (for initialize/save)
83
+ # - :update_expiration [Boolean] Whether TTL was updated (for save)
84
+ #
85
+ # @example
86
+ # Familia.on_lifecycle do |event, instance, ctx|
87
+ # case event
88
+ # when :destroy
89
+ # Rails.logger.info("Destroyed #{instance.class}:#{instance.identifier}")
90
+ # end
91
+ # end
92
+ #
93
+ def on_lifecycle(&block)
94
+ @hooks[:lifecycle] << block
95
+ end
96
+
97
+ # Register a callback for error conditions.
98
+ #
99
+ # @yield [error, context] Callback block
100
+ # @yieldparam error [Exception] The error that occurred
101
+ # @yieldparam context [Hash] Additional context including:
102
+ # - :operation [Symbol] Operation that failed (:serialization, etc.)
103
+ # - :field [Symbol] Field name (for serialization errors)
104
+ # - :object_class [String] Class name of the object
105
+ #
106
+ # @example
107
+ # Familia.on_error do |error, ctx|
108
+ # Sentry.capture_exception(error, extra: ctx)
109
+ # end
110
+ #
111
+ def on_error(&block)
112
+ @hooks[:error] << block
113
+ end
114
+
115
+ # Notify all registered command hooks.
116
+ # @api private
117
+ def notify_command(cmd, duration, context = {})
118
+ @hooks[:command].each do |hook|
119
+ hook.call(cmd, duration, context)
120
+ rescue => e
121
+ Familia.error("Instrumentation hook failed", error: e.message, hook_type: :command)
122
+ end
123
+ end
124
+
125
+ # Notify all registered pipeline hooks.
126
+ # @api private
127
+ def notify_pipeline(command_count, duration, context = {})
128
+ @hooks[:pipeline].each do |hook|
129
+ hook.call(command_count, duration, context)
130
+ rescue => e
131
+ Familia.error("Instrumentation hook failed", error: e.message, hook_type: :pipeline)
132
+ end
133
+ end
134
+
135
+ # Notify all registered lifecycle hooks.
136
+ # @api private
137
+ def notify_lifecycle(event, instance, context = {})
138
+ @hooks[:lifecycle].each do |hook|
139
+ hook.call(event, instance, context)
140
+ rescue => e
141
+ Familia.error("Instrumentation hook failed", error: e.message, hook_type: :lifecycle)
142
+ end
143
+ end
144
+
145
+ # Notify all registered error hooks.
146
+ # @api private
147
+ def notify_error(error, context = {})
148
+ @hooks[:error].each do |hook|
149
+ hook.call(error, context)
150
+ rescue => e
151
+ # Don't recurse on hook failures - just silently skip
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/json_serializer.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  # JsonSerializer provides a high-performance JSON interface using OJ
@@ -1,4 +1,6 @@
1
1
  # lib/familia/logging.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require 'pathname'
4
6
  require 'logger'
@@ -117,7 +119,7 @@ module Familia
117
119
  'ERROR' => 'E',
118
120
  'FATAL' => 'F',
119
121
  'UNKNOWN' => 'U',
120
- 'ANY' => 'T' # ANY is Logger's label for severity < 0, treat as TRACE
122
+ 'ANY' => 'T', # ANY is Logger's label for severity < 0, treat as TRACE
121
123
  }.freeze
122
124
 
123
125
  # Format a log message with severity, timestamp, and context.
@@ -142,12 +144,9 @@ module Familia
142
144
  SEVERITY_LETTERS.fetch(severity, severity[0])
143
145
  end
144
146
 
145
- utc_datetime = datetime.utc.strftime('%m-%d %H:%M:%S.%3N')
146
- pid = Process.pid
147
- thread_id = Thread.current.object_id
148
- fiber_id = Fiber.current.object_id
147
+ utc_datetime = datetime.utc.strftime('%H:%M:%S.%3N')
149
148
 
150
- "#{severity_letter}, #{utc_datetime} pid:#{pid} [#{thread_id}/#{fiber_id}]: #{msg}\n"
149
+ "#{severity_letter}, #{utc_datetime} #{msg}\n"
151
150
  end
152
151
  end
153
152
 
@@ -176,8 +175,16 @@ module Familia
176
175
  # Familia.trace :LOAD, redis_client, "user:123", "from cache"
177
176
  #
178
177
  module Logging
178
+ # Thread-safe mutex initialization when module is extended
179
+ def self.extended(base)
180
+ base.instance_variable_set(:@logger_mutex, Mutex.new)
181
+ end
182
+
179
183
  # Get the logger instance, initializing with defaults if not yet set
180
184
  #
185
+ # Thread-safe lazy initialization using double-checked locking to ensure
186
+ # only a single logger instance is created even under concurrent logging calls.
187
+ #
181
188
  # @return [FamiliaLogger] the logger instance
182
189
  #
183
190
  # @example Set a custom logger
@@ -187,10 +194,18 @@ module Familia
187
194
  # Familia.logger.info "Connection established"
188
195
  #
189
196
  def logger
190
- @logger ||= FamiliaLogger.new($stderr).tap do |log|
191
- log.progname = name
192
- log.formatter = LogFormatter.new
197
+ # Fast path: return existing logger if already initialized
198
+ return @logger if @logger
199
+
200
+ # Slow path: thread-safe initialization
201
+ @logger_mutex.synchronize do
202
+ @logger ||= FamiliaLogger.new($stderr).tap do |log|
203
+ log.progname = name
204
+ log.formatter = LogFormatter.new
205
+ end
193
206
  end
207
+
208
+ @logger
194
209
  end
195
210
 
196
211
  # Set a custom logger instance.
@@ -198,6 +213,9 @@ module Familia
198
213
  # Allows replacing the default FamiliaLogger with any Logger-compatible
199
214
  # object. Useful for integrating with application logging frameworks.
200
215
  #
216
+ # Automatically synchronizes the logger to DatabaseLogger if it's loaded,
217
+ # ensuring consistent logging across Familia's middleware stack.
218
+ #
201
219
  # @param new_logger [Logger] The logger to use
202
220
  # @return [Logger] The logger that was set
203
221
  #
@@ -210,20 +228,14 @@ module Familia
210
228
  # end
211
229
  #
212
230
  def logger=(new_logger)
213
- @logger = new_logger
231
+ @logger_mutex.synchronize do
232
+ @logger = new_logger
233
+ # Auto-sync to DatabaseLogger if loaded (inside mutex for atomicity)
234
+ DatabaseLogger.logger = new_logger if defined?(DatabaseLogger)
235
+ end
236
+ @logger
214
237
  end
215
238
 
216
- # Log an informational message.
217
- #
218
- # @param msg [String] The message to log
219
- # @return [true]
220
- #
221
- # @example
222
- # Familia.info "Redis connection established"
223
- #
224
- def info(msg)
225
- logger.info(msg)
226
- end
227
239
 
228
240
  # Log a warning message.
229
241
  #
@@ -237,34 +249,58 @@ module Familia
237
249
  logger.warn(msg)
238
250
  end
239
251
 
240
- # Log a debug message (only when Familia.debug? is true).
252
+ # Log a debug message with optional structured context.
241
253
  #
242
- # Short for "log debug". Only outputs when FAMILIA_DEBUG environment
243
- # variable is set to '1' or 'true'.
254
+ # Only outputs when FAMILIA_DEBUG environment variable is enabled.
255
+ # Supports both simple string messages and structured logging with
256
+ # keyword context for operational observability.
244
257
  #
245
- # @param msg [String] The message to log
258
+ # @param message [String, nil] The message to log
259
+ # @param context [Hash] Structured context (key-value pairs)
246
260
  # @return [true, nil] Returns true if logged, nil if debug disabled
247
261
  #
248
- # @example
249
- # Familia.ld "Cache lookup for user:123"
250
- # # Only outputs when FAMILIA_DEBUG=true
262
+ # @example Simple message
263
+ # Familia.debug "Cache lookup for user:123"
264
+ #
265
+ # @example Structured context
266
+ # Familia.debug "Horreum saved", class: "User", identifier: "user_123", duration: 1234
267
+ # # => "Horreum saved class=User identifier=user_123 duration=1234"
251
268
  #
252
- def ld(msg)
253
- logger.debug(msg) if Familia.debug?
269
+ def debug(message = nil, **context)
270
+ return unless Familia.debug?
271
+ logger.debug(format_log(message, context))
254
272
  end
255
273
 
256
- # Log an error message.
274
+ # Log an informational message with optional structured context.
257
275
  #
258
- # Short for "log error".
276
+ # @param message [String, nil] The message to log
277
+ # @param context [Hash] Structured context (key-value pairs)
278
+ # @return [true]
259
279
  #
260
- # @param msg [String] The message to log
280
+ # @example Simple message
281
+ # Familia.info "Connection pool initialized"
282
+ #
283
+ # @example Structured context
284
+ # Familia.info "Pipeline executed", commands: 5, duration: 2340
285
+ #
286
+ def info(message = nil, **context)
287
+ logger.info(format_log(message, context))
288
+ end
289
+
290
+ # Log an error message with optional structured context.
291
+ #
292
+ # @param message [String, nil] The message to log
293
+ # @param context [Hash] Structured context (key-value pairs)
261
294
  # @return [true]
262
295
  #
263
- # @example
264
- # Familia.le "Failed to deserialize value: #{e.message}"
296
+ # @example Simple message
297
+ # Familia.error "Failed to deserialize value"
298
+ #
299
+ # @example Structured context
300
+ # Familia.error "Serialization failed", field: :email, error: e.message, class: "User"
265
301
  #
266
- def le(msg)
267
- logger.error(msg)
302
+ def error(message = nil, **context)
303
+ logger.error(format_log(message, context))
268
304
  end
269
305
 
270
306
  # Logs a structured trace message for debugging Familia operations.
@@ -296,6 +332,27 @@ module Familia
296
332
 
297
333
  private
298
334
 
335
+ # Format a log message with optional structured context.
336
+ #
337
+ # Combines a message string with key-value context into a single
338
+ # log line. Empty context returns the message unchanged.
339
+ #
340
+ # @param message [String, nil] The message to log
341
+ # @param context [Hash] Structured context (key-value pairs)
342
+ # @return [String] Formatted log message
343
+ # @api private
344
+ #
345
+ # @example
346
+ # format_log("User saved", id: 123, duration: 1.5)
347
+ # # => "User saved id=123 duration=1.5"
348
+ #
349
+ def format_log(message, context)
350
+ return message if context.empty?
351
+ parts = [message]
352
+ parts << context.map { |k, v| "#{k}=#{v}" }.join(' ')
353
+ parts.compact.join(' ')
354
+ end
355
+
299
356
  # Check if trace logging is enabled via FAMILIA_TRACE environment variable.
300
357
  #
301
358
  # Trace logging is enabled when FAMILIA_TRACE is set to '1', 'true',
@@ -1,4 +1,6 @@
1
1
  # lib/familia/refinements/dear_json.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require 'familia/json_serializer'
4
6
 
@@ -1,4 +1,6 @@
1
1
  # lib/familia/refinements/stylize_words.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  module Refinements
@@ -16,20 +18,6 @@ module Familia
16
18
  .downcase
17
19
  end
18
20
 
19
- # Convert from plural to singular form using basic English rules
20
- def singularize
21
- word = to_s
22
- if word.end_with?('ies')
23
- "#{word[0..-4]}y"
24
- elsif word.end_with?('es') && word.length > 3
25
- word[0..-3]
26
- elsif word.end_with?('s') && word.length > 1
27
- word[0..-2]
28
- else
29
- word
30
- end
31
- end
32
-
33
21
  # Convert to camelCase
34
22
  def camelize
35
23
  _ize(:lower)
@@ -1,4 +1,6 @@
1
1
  # lib/familia/refinements/time_literals.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  module Refinements
@@ -1,4 +1,6 @@
1
1
  # lib/familia/refinements.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require_relative 'refinements/dear_json'
4
6
  require_relative 'refinements/stylize_words'
@@ -1,4 +1,6 @@
1
1
  # lib/familia/secure_identifier.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require 'securerandom'
4
6
 
@@ -118,6 +120,10 @@ module Familia
118
120
 
119
121
  # @private
120
122
  #
123
+ # Thread-safe lazy initialization using Concurrent::Map to ensure
124
+ # atomic cache population and consistent calculations across all
125
+ # concurrent ID generation requests.
126
+ #
121
127
  # @param bits [Integer] The number of bits of entropy.
122
128
  # @param base [Integer] The numeric base (2-36).
123
129
  # @return [Integer] The minimum string length required.
@@ -130,8 +136,10 @@ module Familia
130
136
  }.freeze
131
137
  return hex_lengths[bits] if base == 16 && hex_lengths.key?(bits)
132
138
 
133
- @min_length_for_bits_cache ||= {}
134
- @min_length_for_bits_cache[[bits, base]] ||= (bits * Math.log(2) / Math.log(base)).ceil
139
+ @min_length_for_bits_cache ||= Concurrent::Map.new
140
+ @min_length_for_bits_cache.fetch_or_store([bits, base]) do
141
+ (bits * Math.log(2) / Math.log(base)).ceil
142
+ end
135
143
  end
136
144
  end
137
145
  end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/settings.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  # Familia
4
6
  #
@@ -11,7 +13,7 @@ module Familia
11
13
  @encryption_keys = nil
12
14
  @current_key_version = nil
13
15
  @encryption_personalization = 'FamilialMatters'.freeze
14
- @pipeline_mode = :warn
16
+ @pipelined_mode = :warn
15
17
 
16
18
  # Familia::Settings
17
19
  #
@@ -118,24 +120,24 @@ module Familia
118
120
  #
119
121
  # @example Setting pipeline mode
120
122
  # Familia.configure do |config|
121
- # config.pipeline_mode = :permissive
123
+ # config.pipelined_mode = :permissive
122
124
  # end
123
125
  #
124
- def pipeline_mode(val = nil)
126
+ def pipelined_mode(val = nil)
125
127
  if val
126
128
  unless [:strict, :warn, :permissive].include?(val)
127
129
  raise ArgumentError, 'Pipeline mode must be :strict, :warn, or :permissive'
128
130
  end
129
- @pipeline_mode = val
131
+ @pipelined_mode = val
130
132
  end
131
- @pipeline_mode || :warn # default to warn mode
133
+ @pipelined_mode || :warn # default to warn mode
132
134
  end
133
135
 
134
- def pipeline_mode=(val)
136
+ def pipelined_mode=(val)
135
137
  unless [:strict, :warn, :permissive].include?(val)
136
138
  raise ArgumentError, 'Pipeline mode must be :strict, :warn, or :permissive'
137
139
  end
138
- @pipeline_mode = val
140
+ @pipelined_mode = val
139
141
  end
140
142
 
141
143
  # Configure Familia settings