familia 2.0.0.pre17 → 2.0.0.pre19
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.
- checksums.yaml +4 -4
- data/CHANGELOG.rst +118 -6
- data/CLAUDE.md +43 -11
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -47
- data/README.md +52 -0
- data/bin/irb +1 -1
- data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +91 -0
- data/changelog.d/20251011_203905_delano_next.rst +30 -0
- data/changelog.d/20251011_212633_delano_next.rst +13 -0
- data/changelog.d/20251011_221253_delano_next.rst +26 -0
- data/docs/guides/core-field-system.md +48 -26
- data/docs/guides/feature-expiration.md +18 -18
- data/docs/migrating/v2.0.0-pre18.md +58 -0
- data/docs/migrating/v2.0.0-pre19.md +197 -0
- data/docs/qodo-merge-compliance.md +96 -0
- data/examples/datatype_standalone.rb +281 -0
- data/lib/familia/base.rb +0 -2
- data/lib/familia/connection/behavior.rb +252 -0
- data/lib/familia/connection/handlers.rb +95 -0
- data/lib/familia/connection/middleware.rb +58 -4
- data/lib/familia/connection/operation_core.rb +1 -1
- data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +2 -2
- data/lib/familia/connection/transaction_core.rb +7 -9
- data/lib/familia/connection.rb +2 -1
- data/lib/familia/data_type/connection.rb +151 -7
- data/lib/familia/data_type/{commands.rb → database_commands.rb} +9 -6
- data/lib/familia/data_type/serialization.rb +9 -5
- data/lib/familia/data_type/types/hashkey.rb +1 -1
- data/lib/familia/data_type.rb +2 -2
- data/lib/familia/encryption/encrypted_data.rb +12 -2
- data/lib/familia/encryption/manager.rb +11 -4
- data/lib/familia/errors.rb +51 -14
- data/lib/familia/features/autoloader.rb +3 -1
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +11 -3
- data/lib/familia/features/expiration/extensions.rb +8 -10
- data/lib/familia/features/expiration.rb +19 -19
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +45 -44
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +151 -65
- data/lib/familia/features/relationships/indexing.rb +37 -42
- data/lib/familia/features/relationships/indexing_relationship.rb +14 -4
- data/lib/familia/features/safe_dump.rb +2 -3
- data/lib/familia/field_type.rb +2 -1
- data/lib/familia/horreum/connection.rb +11 -35
- data/lib/familia/horreum/database_commands.rb +130 -11
- data/lib/familia/horreum/definition.rb +8 -38
- data/lib/familia/horreum/management.rb +38 -27
- data/lib/familia/horreum/persistence.rb +191 -67
- data/lib/familia/horreum/serialization.rb +94 -73
- data/lib/familia/horreum/utils.rb +0 -8
- data/lib/familia/horreum.rb +41 -18
- data/lib/familia/identifier_extractor.rb +60 -0
- data/lib/familia/logging.rb +268 -112
- data/lib/familia/refinements.rb +0 -1
- data/lib/familia/settings.rb +7 -7
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +2 -2
- data/lib/middleware/{database_middleware.rb → database_logger.rb} +118 -14
- data/pr_agent.toml +31 -0
- data/pr_compliance_checklist.yaml +45 -0
- data/try/edge_cases/empty_identifiers_try.rb +1 -1
- data/try/edge_cases/hash_symbolization_try.rb +31 -31
- data/try/edge_cases/json_serialization_try.rb +2 -2
- data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +170 -0
- data/try/edge_cases/race_conditions_try.rb +1 -1
- data/try/edge_cases/reserved_keywords_try.rb +1 -1
- data/try/edge_cases/string_coercion_try.rb +5 -5
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/features/encrypted_fields/aad_protection_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +1 -1
- data/try/features/encrypted_fields/context_isolation_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +1 -1
- data/try/features/encrypted_fields/error_conditions_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_derivation_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/key_rotation_try.rb +1 -1
- data/try/features/encrypted_fields/memory_security_try.rb +1 -1
- data/try/features/encrypted_fields/missing_current_key_version_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +1 -1
- data/try/features/encrypted_fields/thread_safety_try.rb +1 -1
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +1 -1
- data/try/{encryption → features/encryption}/config_persistence_try.rb +1 -1
- data/try/{encryption/encryption_core_try.rb → features/encryption/core_try.rb} +2 -2
- data/try/{encryption → features/encryption}/instance_variable_scope_try.rb +1 -1
- data/try/{encryption → features/encryption}/module_loading_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/aes_gcm_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/roundtrip_validation_try.rb +1 -1
- data/try/{encryption → features/encryption}/secure_memory_handling_try.rb +2 -2
- data/try/features/expiration/expiration_try.rb +2 -2
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +1 -1
- data/try/features/feature_improvements_try.rb +1 -1
- data/try/features/object_identifier/object_identifier_integration_try.rb +1 -1
- data/try/features/object_identifier/object_identifier_try.rb +1 -1
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/real_feature_integration_try.rb +17 -14
- data/try/features/relationships/indexing_commands_verification_try.rb +8 -3
- data/try/features/relationships/indexing_try.rb +34 -5
- data/try/features/relationships/participation_commands_verification_spec.rb +1 -1
- data/try/features/relationships/participation_commands_verification_try.rb +4 -4
- data/try/features/relationships/participation_performance_improvements_try.rb +1 -1
- data/try/features/relationships/participation_reverse_index_try.rb +1 -1
- data/try/features/relationships/relationships_api_changes_try.rb +5 -5
- data/try/features/relationships/relationships_edge_cases_try.rb +3 -3
- data/try/features/relationships/relationships_performance_minimal_try.rb +1 -1
- data/try/features/relationships/relationships_performance_simple_try.rb +1 -1
- data/try/features/relationships/relationships_performance_try.rb +1 -1
- data/try/features/relationships/relationships_performance_working_try.rb +1 -1
- data/try/features/relationships/relationships_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_advanced_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_try.rb +1 -1
- data/try/features/transient_fields/redacted_string_try.rb +1 -1
- data/try/features/transient_fields/refresh_reset_try.rb +1 -1
- data/try/features/transient_fields/single_use_redacted_string_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_core_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/fiber_context_preservation_try.rb +4 -4
- data/try/{connection → integration/connection}/handler_constraints_try.rb +1 -1
- data/try/{core → integration/connection}/isolated_dbclient_try.rb +1 -1
- data/try/integration/connection/middleware_reconnect_try.rb +87 -0
- data/try/{connection → integration/connection}/operation_mode_guards_try.rb +2 -2
- data/try/{connection → integration/connection}/pipeline_fallback_integration_try.rb +13 -13
- data/try/{core → integration/connection}/pools_try.rb +1 -1
- data/try/{connection → integration/connection}/responsibility_chain_tracking_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_fallback_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_permissive_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_strict_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_warn_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_modes_try.rb +1 -1
- data/try/{core → integration}/conventional_inheritance_try.rb +1 -1
- data/try/{core → integration}/create_method_try.rb +23 -23
- data/try/integration/cross_component_try.rb +1 -1
- data/try/integration/data_types/datatype_pipelines_try.rb +104 -0
- data/try/integration/data_types/datatype_transactions_try.rb +247 -0
- data/try/{core → integration}/database_consistency_try.rb +11 -8
- data/try/{core → integration}/familia_extended_try.rb +1 -1
- data/try/{core → integration}/familia_members_methods_try.rb +1 -1
- data/try/{models → integration/models}/customer_safe_dump_try.rb +6 -2
- data/try/{models → integration/models}/customer_try.rb +1 -1
- data/try/{models → integration/models}/datatype_base_try.rb +1 -1
- data/try/{models → integration/models}/familia_object_try.rb +2 -2
- data/try/{core → integration}/persistence_operations_try.rb +163 -11
- data/try/integration/relationships_persistence_round_trip_try.rb +441 -0
- data/try/{configuration → integration}/scenarios_try.rb +1 -1
- data/try/{core → integration}/secure_identifier_try.rb +1 -1
- data/try/{core → integration}/verifiable_identifier_try.rb +1 -1
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/support/benchmarks/deserialization_benchmark.rb +180 -0
- data/try/support/benchmarks/deserialization_correctness_test.rb +237 -0
- data/try/{helpers → support/helpers}/test_helpers.rb +12 -3
- data/try/{core → unit/core}/autoloader_try.rb +1 -1
- data/try/{core → unit/core}/base_enhancements_try.rb +1 -9
- data/try/{core → unit/core}/connection_try.rb +1 -1
- data/try/{core → unit/core}/errors_try.rb +1 -1
- data/try/{core → unit/core}/extensions_try.rb +1 -1
- data/try/unit/core/familia_logger_try.rb +110 -0
- data/try/{core → unit/core}/familia_try.rb +1 -1
- data/try/{core → unit/core}/middleware_try.rb +41 -1
- data/try/{core → unit/core}/settings_try.rb +1 -1
- data/try/{core → unit/core}/time_utils_try.rb +1 -1
- data/try/{core → unit/core}/tools_try.rb +1 -1
- data/try/{core → unit/core}/utils_try.rb +17 -14
- data/try/{data_types → unit/data_types}/boolean_try.rb +2 -2
- data/try/{data_types → unit/data_types}/counter_try.rb +1 -1
- data/try/{data_types → unit/data_types}/datatype_base_try.rb +1 -1
- data/try/{data_types → unit/data_types}/hash_try.rb +1 -1
- data/try/{data_types → unit/data_types}/list_try.rb +1 -1
- data/try/{data_types → unit/data_types}/lock_try.rb +1 -1
- data/try/{data_types → unit/data_types}/sorted_set_try.rb +1 -1
- data/try/{data_types → unit/data_types}/sorted_set_zadd_options_try.rb +1 -1
- data/try/{data_types → unit/data_types}/string_try.rb +2 -2
- data/try/{data_types → unit/data_types}/unsortedset_try.rb +1 -1
- data/try/{horreum → unit/horreum}/auto_indexing_on_save_try.rb +33 -17
- data/try/unit/horreum/automatic_index_validation_try.rb +253 -0
- data/try/{horreum → unit/horreum}/base_try.rb +4 -4
- data/try/{horreum → unit/horreum}/class_methods_try.rb +3 -3
- data/try/{horreum → unit/horreum}/commands_try.rb +1 -1
- data/try/{horreum → unit/horreum}/defensive_initialization_try.rb +1 -1
- data/try/{horreum → unit/horreum}/destroy_related_fields_cleanup_try.rb +1 -1
- data/try/{horreum → unit/horreum}/enhanced_conflict_handling_try.rb +1 -1
- data/try/{horreum → unit/horreum}/field_categories_try.rb +27 -18
- data/try/{horreum → unit/horreum}/field_definition_try.rb +1 -1
- data/try/{horreum → unit/horreum}/initialization_try.rb +3 -3
- data/try/unit/horreum/json_type_preservation_try.rb +248 -0
- data/try/{horreum → unit/horreum}/relations_try.rb +5 -5
- data/try/{horreum → unit/horreum}/serialization_persistent_fields_try.rb +24 -18
- data/try/{horreum → unit/horreum}/serialization_try.rb +6 -6
- data/try/{horreum → unit/horreum}/settings_try.rb +1 -1
- data/try/unit/horreum/unique_index_edge_cases_try.rb +376 -0
- data/try/unit/horreum/unique_index_guard_validation_try.rb +281 -0
- data/try/{refinements → unit/refinements}/dear_json_array_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/dear_json_hash_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_numeric_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_string_methods_try.rb +1 -1
- metadata +147 -126
- data/lib/familia/distinguisher.rb +0 -85
- data/lib/familia/refinements/logger_trace.rb +0 -60
- data/try/refinements/logger_trace_methods_try.rb +0 -44
- /data/try/{debugging → support/debugging}/README.md +0 -0
- /data/try/{debugging → support/debugging}/cache_behavior_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_aad_process.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_internal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_reveal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_aad.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_simple.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_database_load.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_check.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_step_by_step.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_exists_lifecycle.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_field_decrypt.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_fresh_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_load_path.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_definition.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_resolution.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_minimal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_provider.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_secure_behavior.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_string_class.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test_design.rb +0 -0
- /data/try/{debugging → support/debugging}/encryption_method_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/provider_diagnostics.rb +0 -0
- /data/try/{helpers → support/helpers}/test_cleanup.rb +0 -0
- /data/try/{memory → support/memory}/memory_basic_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_detailed_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_docker_ruby_dump.sh +0 -0
- /data/try/{memory → support/memory}/memory_search_for_string.rb +0 -0
- /data/try/{memory → support/memory}/test_actual_redactedstring_protection.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v1_context_proxy.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v2_connection_switching.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v3_connection_pool.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v4.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v2_connection_switching_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/README.md +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/configurable_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_metrics.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_threading_models.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/visualize_stress_results.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/pool_siege.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/run_stress_tests.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4250fd7b94da275c6cfe9ebc7515e14fc6bead4bb25597aa5e433bf6c9368727
|
4
|
+
data.tar.gz: 50818f7fce2464d3a4349d4de6a1fd7992900e45d8774f6d6d40d047d71a1842
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf19202fe2ae0176698fa3ed5b5bd5cb4c1536de2e091a0978d75d8d9964c05cf2285cbcbbdf2d8deb20500a00b83f4fc4d7091e80f08fcaa29840053788da4f
|
7
|
+
data.tar.gz: c7a5810d3c84cca3c8f51e7449d0049c13eae86300f4703b5af9a492329a1a1c6ff01142529351e705f527c4b4f1ad8d07b15e5280b956a3c5a4fa2971ff2b08
|
data/CHANGELOG.rst
CHANGED
@@ -1,17 +1,129 @@
|
|
1
1
|
CHANGELOG.rst
|
2
2
|
=============
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
The format is based on `Keep a
|
7
|
-
Changelog <https://keepachangelog.com/en/1.1.0/>`__, and this project
|
8
|
-
adheres to `Semantic
|
9
|
-
Versioning <https://semver.org/spec/v2.0.0.html>`__.
|
4
|
+
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.1.0/>`__, and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`__.
|
10
5
|
|
11
6
|
.. raw:: html
|
12
7
|
|
13
8
|
<!--scriv-insert-here-->
|
14
9
|
|
10
|
+
.. _changelog-2.0.0.pre19:
|
11
|
+
|
12
|
+
2.0.0.pre19 — 2025-10-11
|
13
|
+
=========================
|
14
|
+
|
15
|
+
Added
|
16
|
+
-----
|
17
|
+
|
18
|
+
- **DataType Transaction and Pipeline Support** - DataType objects can now initiate transactions and pipelines independently, enabling atomic operations and batch command execution for both parent-owned and standalone DataType objects. `PR #160 <https://github.com/familia/familia/pull/160>`__. Key capabilities added:
|
19
|
+
|
20
|
+
* ``transaction`` and ``pipelined`` methods for atomic MULTI/EXEC operations and batched command execution on all DataType classes
|
21
|
+
* Connection chain pattern with Chain of Responsibility for DataType objects
|
22
|
+
* Two new connection handlers: ``ParentDelegationHandler`` for owned DataTypes and ``StandaloneConnectionHandler`` for independent DataTypes
|
23
|
+
* Enhanced ``direct_access`` method with automatic transaction/pipeline context detection
|
24
|
+
* Shared ``Familia::Connection::Behavior`` module extracting common connection functionality
|
25
|
+
|
26
|
+
- New error hierarchy with ``PersistenceError``, ``HorreumError``, ``CreationError``, and ``OptimisticLockError`` classes for better error categorization and handling
|
27
|
+
- ``watch``, ``unwatch``, and ``discard`` Redis commands for optimistic locking support
|
28
|
+
- Enhanced database command logging with structured format for pipelined and transaction operations
|
29
|
+
- ``save_fields`` method in Persistence module for selective field updates
|
30
|
+
|
31
|
+
Changed
|
32
|
+
-------
|
33
|
+
|
34
|
+
- **Connection Architecture Refactored** - The ``Horreum::Connection`` module now includes ``Familia::Connection::Behavior``, eliminating code duplication by sharing URI normalization and connection creation methods between Horreum and DataType. DataType objects with ``logical_database`` settings now return clean URIs without custom port information (e.g., ``redis://127.0.0.1/3`` instead of ``redis://127.0.0.1:2525/3``), ensuring consistent URI representation across the library.
|
35
|
+
|
36
|
+
- **BREAKING**: Renamed ``Management.create`` to ``create!`` to follow Rails conventions and indicate potential exceptions
|
37
|
+
- **BREAKING**: Updated ``save_if_not_exists`` to ``save_if_not_exists!`` with optimistic locking and automatic retry logic (up to 3 attempts)
|
38
|
+
- Improved ``save`` method to use single atomic transaction encompassing field updates, expiration setting, index updates, and instance collection management
|
39
|
+
- Enhanced ``delete!`` methods to work correctly within Redis transactions
|
40
|
+
- Updated timestamp fields (``created``, ``updated``) to use float values instead of integers for higher precision
|
41
|
+
- Refined log message formatting for better readability and debugging
|
42
|
+
- Removed deprecated Connection instance methods for Horreum models in favor of class-level database operations
|
43
|
+
- Clarified "pipelined" terminology throughout codebase (renamed from "pipeline" for consistency with Redis documentation)
|
44
|
+
|
45
|
+
Fixed
|
46
|
+
-----
|
47
|
+
|
48
|
+
- Resolved atomicity issues and race conditions in save operations by consolidating all related operations into single Redis transaction with proper watch/multi/exec pattern and optimistic locking
|
49
|
+
- Corrected transaction handling to ensure proper cleanup and error propagation
|
50
|
+
|
51
|
+
Documentation
|
52
|
+
-------------
|
53
|
+
|
54
|
+
- Added comprehensive parameter documentation for database command methods including return value specifications and usage examples
|
55
|
+
|
56
|
+
AI Assistance
|
57
|
+
-------------
|
58
|
+
|
59
|
+
This feature was implemented with AI assistance from Claude Sonnet 4.5, Opus 4.1 (Anthropic).
|
60
|
+
|
61
|
+
* Architectural design of the connection chain pattern and shared Behavior module
|
62
|
+
* Implementation of DataType-specific connection handlers (ParentDelegationHandler, StandaloneConnectionHandler) and comprehensive test coverage
|
63
|
+
* Error hierarchy design and transaction atomicity optimization
|
64
|
+
* Documentation enhancement and URI formatting debugging
|
65
|
+
|
66
|
+
|
67
|
+
.. _changelog-2.0.0.pre18:
|
68
|
+
|
69
|
+
2.0.0.pre18 — 2025-10-05
|
70
|
+
========================
|
71
|
+
|
72
|
+
Added
|
73
|
+
-----
|
74
|
+
|
75
|
+
- Added ``Familia.reconnect!`` method to refresh connection pools with current middleware configuration. This solves issues in test suites where middleware (like DatabaseLogger) is enabled after connection pools are created. The method clears the connection chain, increments the middleware version, and clears fiber-local connections, ensuring new connections include the latest middleware. See ``lib/familia/connection/middleware.rb:81-117``.
|
76
|
+
|
77
|
+
Changed
|
78
|
+
-------
|
79
|
+
|
80
|
+
- **BREAKING**: Implemented type-preserving JSON serialization for Horreum field values. Non-string values (Integer, Boolean, Float, nil, Hash, Array) are JSON-encoded for storage and JSON-decoded on retrieval. **Strings are stored as-is without JSON encoding** to avoid double-quoting and maintain Redis baseline simplicity. Type preservation is achieved through smart deserialization: values that parse as JSON restore to their original types, otherwise remain as strings.
|
81
|
+
|
82
|
+
- **BREAKING**: Changed default Hash key format from symbols to strings throughout the codebase (``symbolize: false`` default). This eliminates ambiguity with HTTP request parameters and IndifferentHash-style implementations, providing strict adherence to JSON parsing rules and avoiding key duplication issues.
|
83
|
+
|
84
|
+
- **BREAKING**: Fixed ``initialize_with_keyword_args`` to properly handle ``false`` and ``0`` values during object initialization. Previously, falsy values were incorrectly skipped due to truthiness checks. Now uses explicit nil checking with ``fetch`` to preserve all non-nil values including ``false`` and ``0``.
|
85
|
+
|
86
|
+
- **String serialization now uses JSON encoding**: All string values are JSON-encoded during storage (wrapped in quotes) for consistent type preservation. The lenient deserializer handles both new JSON-encoded strings and legacy plain strings automatically. PR #152
|
87
|
+
|
88
|
+
Removed
|
89
|
+
-------
|
90
|
+
|
91
|
+
- **BREAKING**: Removed ``dump_method`` and ``load_method`` configuration options from ``Familia::Base`` and ``Familia::Horreum::Definition``. JSON serialization is now hard-coded for consistency and type safety. Custom serialization methods are no longer supported.
|
92
|
+
|
93
|
+
Fixed
|
94
|
+
-----
|
95
|
+
|
96
|
+
- Fixed type coercion bugs where Integer fields (e.g., ``age: 35``) became Strings (``"35"``) and Boolean fields (e.g., ``active: true``) became Strings (``"true"``) after database round-trips. All primitive types now maintain their original types through ``find_by_dbkey``, ``refresh!``, and ``batch_update`` operations.
|
97
|
+
|
98
|
+
- Fixed ``deserialize_value`` to return all JSON-parsed types instead of filtering to Hash/Array only. This enables proper deserialization of primitive types (Integer, Boolean, Float, String) from Redis storage.
|
99
|
+
|
100
|
+
- Added JSON deserialization in ``find_by_dbkey`` using existing ``initialize_with_keyword_args_deserialize_value`` helper method to maintain DRY principles and ensure loaded objects receive properly typed field values rather than raw Redis strings.
|
101
|
+
|
102
|
+
- Optimized serialization to avoid double-encoding strings - strings stored directly in Redis as-is, only non-string types use JSON encoding. This reduces storage overhead and maintains Redis's string baseline semantics.
|
103
|
+
|
104
|
+
- Fixed encrypted fields with ``category: :encrypted`` appearing in ``to_h()`` output. These fields now correctly set ``loggable: false`` to prevent accidental exposure in logs, APIs, or external interfaces. PR #152
|
105
|
+
|
106
|
+
- Fixed middleware registration to only set ``@middleware_registered`` flag when middleware is actually enabled and registered. Previously, calling ``create_dbclient`` before enabling middleware would set the flag to ``true`` without registering anything, preventing later middleware enablement from working. The fix ensures ``register_middleware_once`` only sets the flag after successful registration. See ``lib/familia/connection/middleware.rb:124-146``.
|
107
|
+
|
108
|
+
Security
|
109
|
+
--------
|
110
|
+
|
111
|
+
- Encrypted fields defined via ``field :name, category: :encrypted`` now properly excluded from ``to_h()`` serialization, matching the security behavior of ``encrypted_field``. PR #152
|
112
|
+
|
113
|
+
Documentation
|
114
|
+
-------------
|
115
|
+
|
116
|
+
- Added comprehensive type preservation test suite (``try/unit/horreum/json_type_preservation_try.rb``) with 30 test cases covering Integer, Boolean, String, Float, Hash, Array, nested structures, nil handling, empty strings, zero values, round-trip consistency, ``batch_update``, and ``refresh!`` operations.
|
117
|
+
|
118
|
+
AI Assistance
|
119
|
+
-------------
|
120
|
+
|
121
|
+
- Claude Code (claude-sonnet-4-5) provided implementation guidance, identified the ``initialize_with_keyword_args`` falsy value bug, wrote comprehensive test suite, and coordinated multi-file changes across serialization, management, and base modules.
|
122
|
+
|
123
|
+
- Issue analysis, implementation guidance, test verification, and documentation for JSON serialization changes and encrypted field security fix.
|
124
|
+
|
125
|
+
- Claude Code (Sonnet 4.5) provided architecture analysis, implementation design, and identified critical issues through the second-opinion agent. Key contributions included recommending the simplified approach without pool shutdown lifecycle management, identifying the race condition risk in clearing ``@middleware_registered``, and suggesting the use of natural pool aging instead of explicit shutdown.
|
126
|
+
|
15
127
|
.. _changelog-2.0.0.pre17:
|
16
128
|
|
17
129
|
2.0.0.pre17 — 2025-10-03
|
data/CLAUDE.md
CHANGED
@@ -105,30 +105,55 @@ class User < Familia::Horreum
|
|
105
105
|
end
|
106
106
|
```
|
107
107
|
|
108
|
-
**Good - use the `init` hook
|
108
|
+
**Good - use the `init` hook to apply defaults (use `||=` not `=`):**
|
109
109
|
```ruby
|
110
110
|
class User < Familia::Horreum
|
111
|
-
|
112
|
-
|
111
|
+
field :objid
|
112
|
+
field :email
|
113
|
+
|
114
|
+
# Called after Horreum sets fields from kwargs
|
115
|
+
# IMPORTANT: Use ||= to apply defaults, not = to override
|
116
|
+
def init
|
117
|
+
@objid ||= SecureRandom.uuid # Apply default only if not already set
|
118
|
+
_run_post_init_hooks # Additional setup logic
|
113
119
|
end
|
114
120
|
end
|
121
|
+
|
122
|
+
# This works correctly:
|
123
|
+
user = User.new(email: 'test@example.com')
|
124
|
+
user.objid # → generated UUID (applied by init)
|
125
|
+
user.email # → 'test@example.com' (set by Horreum from kwargs)
|
115
126
|
```
|
116
127
|
|
117
|
-
**
|
128
|
+
**Okay - if absolutely necessary, override and call super explicitly:**
|
118
129
|
```ruby
|
119
130
|
class User < Familia::Horreum
|
120
131
|
def initialize(email = nil, **kwargs)
|
121
|
-
super
|
122
|
-
@email
|
132
|
+
super # Initializes related fields here and also calls init
|
133
|
+
@email ||= generate_email if email.nil?
|
123
134
|
end
|
124
135
|
end
|
125
136
|
```
|
126
137
|
|
127
|
-
**Why this matters**: Familia's `initialize` method calls `initialize_relatives`
|
138
|
+
**Why this matters**: Familia's `initialize` method processes kwargs FIRST (setting fields), then calls `initialize_relatives` (setting up DataType objects), then calls your `init` hook. By the time `init` runs, kwargs have already been consumed and fields are set.
|
139
|
+
|
140
|
+
**The ||= Pattern Explained**:
|
141
|
+
```ruby
|
142
|
+
# WRONG - overwrites what Horreum already set
|
143
|
+
def init
|
144
|
+
@email = generate_email # Overwrites the correct value
|
145
|
+
end
|
146
|
+
|
147
|
+
# RIGHT - applies default only if not already set
|
148
|
+
def init
|
149
|
+
@email ||= email # Preserves value Horreum set from kwargs
|
150
|
+
@email ||= 'default@example.com' # Apply fallback default if still nil
|
151
|
+
end
|
152
|
+
```
|
128
153
|
|
129
154
|
**When to use each approach:**
|
130
|
-
- **Use `init` hook
|
131
|
-
- **Use explicit `super`**:
|
155
|
+
- **Use `init` hook with `||=`** (preferred): Apply defaults, run validations, setup callbacks - any logic that should run after field initialization. Follows standard ORM lifecycle hook patterns.
|
156
|
+
- **Use explicit `super`**: Only when you need to intercept or transform arguments before Horreum processes them (rare).
|
132
157
|
|
133
158
|
**DataType Definition**: Use class methods to define keystore database-backed attributes:
|
134
159
|
```ruby
|
@@ -154,8 +179,15 @@ end
|
|
154
179
|
|
155
180
|
### Important Implementation Notes
|
156
181
|
|
157
|
-
**Field Initialization**: Objects can be initialized with positional args (brittle) or keyword args (robust). Keyword args are recommended.
|
158
|
-
|
182
|
+
**Field Initialization**: Objects can be initialized with positional args (brittle) or keyword args (robust). Keyword args are recommended. All non-nil values including `false` and `0` are preserved during initialization.
|
183
|
+
|
184
|
+
**Serialization**: All field values are JSON-encoded for storage and JSON-decoded on retrieval to preserve Ruby types (Integer, Boolean, String, Float, Hash, Array, nil). This ensures type preservation across the Redis storage boundary. For example:
|
185
|
+
- `age: 35` (Integer) stores as `"35"` in Redis and loads back as Integer `35`
|
186
|
+
- `active: true` (Boolean) stores as `"true"` in Redis and loads back as Boolean `true`
|
187
|
+
- `metadata: {key: "value"}` (Hash) stores as JSON and loads back as Hash with proper types
|
188
|
+
|
159
189
|
**Database Key Generation**: Automatic key generation using class name, identifier, and field/type names (aka dbkey). Pattern: `classname:identifier:fieldname`
|
190
|
+
|
160
191
|
**Memory Efficiency**: Only non-nil values are stored in keystore database to optimize memory usage.
|
192
|
+
|
161
193
|
**Thread Safety**: Data types are frozen after instantiation to ensure immutability.
|
data/Gemfile
CHANGED
@@ -17,10 +17,10 @@ group :development, :test do
|
|
17
17
|
gem 'irb', '~> 1.15.2', require: false
|
18
18
|
gem 'redcarpet', require: false
|
19
19
|
gem 'reek', require: false
|
20
|
-
gem 'rubocop', require: false
|
20
|
+
gem 'rubocop', '~> 1.81.1', require: false
|
21
21
|
gem 'rubocop-performance', require: false
|
22
22
|
gem 'rubocop-thread_safety', require: false
|
23
|
-
gem '
|
23
|
+
gem 'ruby-lsp', require: false
|
24
24
|
gem 'yard', '~> 0.9', require: false
|
25
25
|
end
|
26
26
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
familia (2.0.0.
|
4
|
+
familia (2.0.0.pre19)
|
5
5
|
benchmark (~> 0.4)
|
6
6
|
connection_pool (~> 2.5)
|
7
7
|
csv (~> 3.3)
|
@@ -15,7 +15,6 @@ GEM
|
|
15
15
|
remote: https://rubygems.org/
|
16
16
|
specs:
|
17
17
|
ast (2.4.3)
|
18
|
-
backport (1.2.0)
|
19
18
|
base64 (0.3.0)
|
20
19
|
benchmark (0.4.1)
|
21
20
|
bigdecimal (3.2.3)
|
@@ -64,23 +63,11 @@ GEM
|
|
64
63
|
pp (>= 0.6.0)
|
65
64
|
rdoc (>= 4.0.0)
|
66
65
|
reline (>= 0.4.2)
|
67
|
-
|
68
|
-
json (2.15.0)
|
69
|
-
kramdown (2.5.1)
|
70
|
-
rexml (>= 3.3.9)
|
71
|
-
kramdown-parser-gfm (1.1.0)
|
72
|
-
kramdown (~> 2.0)
|
66
|
+
json (2.15.1)
|
73
67
|
language_server-protocol (3.17.0.5)
|
74
68
|
lint_roller (1.1.0)
|
75
69
|
logger (1.7.0)
|
76
|
-
mini_portile2 (2.8.9)
|
77
70
|
minitest (5.25.5)
|
78
|
-
nokogiri (1.18.10)
|
79
|
-
mini_portile2 (~> 2.8.2)
|
80
|
-
racc (~> 1.4)
|
81
|
-
nokogiri (1.18.10-arm64-darwin)
|
82
|
-
racc (~> 1.4)
|
83
|
-
observer (0.1.2)
|
84
71
|
oj (3.16.11)
|
85
72
|
bigdecimal (>= 3.0)
|
86
73
|
ostruct (>= 0.2)
|
@@ -94,7 +81,7 @@ GEM
|
|
94
81
|
pp (0.6.2)
|
95
82
|
prettyprint
|
96
83
|
prettyprint (0.2.0)
|
97
|
-
prism (1.5.
|
84
|
+
prism (1.5.2)
|
98
85
|
psych (5.2.6)
|
99
86
|
date
|
100
87
|
stringio
|
@@ -121,8 +108,6 @@ GEM
|
|
121
108
|
regexp_parser (2.11.3)
|
122
109
|
reline (0.6.2)
|
123
110
|
io-console (~> 0.5)
|
124
|
-
reverse_markdown (3.0.0)
|
125
|
-
nokogiri
|
126
111
|
rexml (3.4.1)
|
127
112
|
rspec (3.13.1)
|
128
113
|
rspec-core (~> 3.13.0)
|
@@ -159,34 +144,15 @@ GEM
|
|
159
144
|
lint_roller (~> 1.1)
|
160
145
|
rubocop (~> 1.72, >= 1.72.1)
|
161
146
|
rubocop-ast (>= 1.44.0, < 2.0)
|
147
|
+
ruby-lsp (0.26.1)
|
148
|
+
language_server-protocol (~> 3.17.0)
|
149
|
+
prism (>= 1.2, < 2.0)
|
150
|
+
rbs (>= 3, < 5)
|
162
151
|
ruby-prof (1.7.2)
|
163
152
|
base64
|
164
153
|
ruby-progressbar (1.13.0)
|
165
|
-
solargraph (0.57.0)
|
166
|
-
backport (~> 1.2)
|
167
|
-
benchmark (~> 0.4)
|
168
|
-
bundler (~> 2.0)
|
169
|
-
diff-lcs (~> 1.4)
|
170
|
-
jaro_winkler (~> 1.6, >= 1.6.1)
|
171
|
-
kramdown (~> 2.3)
|
172
|
-
kramdown-parser-gfm (~> 1.1)
|
173
|
-
logger (~> 1.6)
|
174
|
-
observer (~> 0.1)
|
175
|
-
ostruct (~> 0.6)
|
176
|
-
parser (~> 3.0)
|
177
|
-
prism (~> 1.4)
|
178
|
-
rbs (>= 3.6.1, <= 4.0.0.dev.4)
|
179
|
-
reverse_markdown (~> 3.0)
|
180
|
-
rubocop (~> 1.76)
|
181
|
-
thor (~> 1.0)
|
182
|
-
tilt (~> 2.0)
|
183
|
-
yard (~> 0.9, >= 0.9.24)
|
184
|
-
yard-activesupport-concern (~> 0.0)
|
185
|
-
yard-solargraph (~> 0.1)
|
186
154
|
stackprof (0.2.27)
|
187
155
|
stringio (3.1.7)
|
188
|
-
thor (1.4.0)
|
189
|
-
tilt (2.6.1)
|
190
156
|
timecop (0.9.10)
|
191
157
|
tryouts (3.6.0)
|
192
158
|
concurrent-ruby (~> 1.0)
|
@@ -205,10 +171,6 @@ GEM
|
|
205
171
|
unicode-emoji (4.1.0)
|
206
172
|
uri-valkey (1.4.0)
|
207
173
|
yard (0.9.37)
|
208
|
-
yard-activesupport-concern (0.0.1)
|
209
|
-
yard (>= 0.8)
|
210
|
-
yard-solargraph (0.1.0)
|
211
|
-
yard (~> 0.9)
|
212
174
|
zeitwerk (2.7.3)
|
213
175
|
|
214
176
|
PLATFORMS
|
@@ -223,11 +185,11 @@ DEPENDENCIES
|
|
223
185
|
rbnacl (~> 7.1, >= 7.1.1)
|
224
186
|
redcarpet
|
225
187
|
reek
|
226
|
-
rubocop
|
188
|
+
rubocop (~> 1.81.1)
|
227
189
|
rubocop-performance
|
228
190
|
rubocop-thread_safety
|
191
|
+
ruby-lsp
|
229
192
|
ruby-prof
|
230
|
-
solargraph
|
231
193
|
stackprof
|
232
194
|
timecop
|
233
195
|
tryouts (~> 3.6.0)
|
data/README.md
CHANGED
@@ -280,6 +280,7 @@ Flower.multiget("prose", "tulip", "daisy")
|
|
280
280
|
|
281
281
|
### Transactional Operations
|
282
282
|
|
283
|
+
**Horreum Model Transactions:**
|
283
284
|
```ruby
|
284
285
|
user.transaction do |conn|
|
285
286
|
conn.set("user:#{user.id}:status", "active")
|
@@ -287,6 +288,44 @@ user.transaction do |conn|
|
|
287
288
|
end
|
288
289
|
```
|
289
290
|
|
291
|
+
**DataType Transactions** (standalone or parent-owned):
|
292
|
+
```ruby
|
293
|
+
# Recommended: Use DataType methods for clean, automatic key handling
|
294
|
+
user.scores.transaction do
|
295
|
+
user.scores.add('level1', 100)
|
296
|
+
user.scores.add('level2', 200)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Standalone DataType transaction (e.g., session storage)
|
300
|
+
session_key = Familia::StringKey.new('session:abc123')
|
301
|
+
session_key.transaction do
|
302
|
+
session_key.set(session_data)
|
303
|
+
session_key.expire(3600) # Atomic: both succeed or both fail
|
304
|
+
end
|
305
|
+
|
306
|
+
# Advanced: Connection available for low-level Redis commands
|
307
|
+
user.scores.transaction do |conn|
|
308
|
+
conn.zadd(user.scores.dbkey, 100, 'level1')
|
309
|
+
conn.hset(user.profile.dbkey, 'status', 'active')
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
**Pipeline Operations** (batch commands for performance):
|
314
|
+
```ruby
|
315
|
+
# Recommended: Use DataType methods
|
316
|
+
leaderboard.pipelined do
|
317
|
+
leaderboard.add('player1', 500)
|
318
|
+
leaderboard.add('player2', 600)
|
319
|
+
leaderboard.size
|
320
|
+
end
|
321
|
+
|
322
|
+
# Advanced: Raw Redis commands for fine-grained control
|
323
|
+
leaderboard.pipelined do |conn|
|
324
|
+
conn.zadd(leaderboard.dbkey, 500, 'player1')
|
325
|
+
conn.zadd(leaderboard.dbkey, 600, 'player2')
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
290
329
|
### Advanced Patterns
|
291
330
|
|
292
331
|
**Time-based Expiration:**
|
@@ -438,6 +477,19 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
438
477
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
439
478
|
5. Open a Pull Request
|
440
479
|
|
480
|
+
### PR Compliance Checks
|
481
|
+
|
482
|
+
Pull requests are automatically reviewed by [Qodo Merge](https://qodo.ai) with compliance checks for:
|
483
|
+
- **Error Handling** - External API calls and database operations must have proper error handling
|
484
|
+
- **Test Coverage** - New features must include tests using the Tryouts framework
|
485
|
+
- **Changelog Fragments** - User-facing changes should include a changelog entry
|
486
|
+
- **Documentation** - API changes must update documentation
|
487
|
+
- **Backward Compatibility** - Breaking changes must be documented
|
488
|
+
- **Thread Safety** - Shared state must be properly synchronized
|
489
|
+
- **Database Key Naming** - Keys must follow Familia conventions
|
490
|
+
|
491
|
+
See [docs/qodo-merge-compliance.md](docs/qodo-merge-compliance.md) for details.
|
492
|
+
|
441
493
|
## License
|
442
494
|
|
443
495
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
data/bin/irb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
.. Added
|
2
|
+
.. -----
|
3
|
+
.. New features and capabilities that have been added.
|
4
|
+
|
5
|
+
.. Changed
|
6
|
+
.. -------
|
7
|
+
.. Changes to existing functionality.
|
8
|
+
|
9
|
+
.. Deprecated
|
10
|
+
.. ----------
|
11
|
+
.. Soon-to-be removed features.
|
12
|
+
|
13
|
+
.. Removed
|
14
|
+
.. -------
|
15
|
+
.. Now removed features.
|
16
|
+
|
17
|
+
.. Fixed
|
18
|
+
.. -----
|
19
|
+
.. Bug fixes.
|
20
|
+
|
21
|
+
.. Security
|
22
|
+
.. --------
|
23
|
+
.. Security-related improvements.
|
24
|
+
|
25
|
+
Added
|
26
|
+
-----
|
27
|
+
|
28
|
+
- **DataType Transaction and Pipeline Support** - DataType objects can now initiate transactions and pipelines independently, enabling atomic operations and batch command execution for both parent-owned and standalone DataType objects. `PR #159 <https://github.com/familia/familia/pull/159>`_
|
29
|
+
|
30
|
+
Key capabilities added:
|
31
|
+
|
32
|
+
* ``transaction`` method for atomic MULTI/EXEC operations on all DataType classes
|
33
|
+
* ``pipelined`` method for batched command execution on all DataType classes
|
34
|
+
* Connection chain pattern with Chain of Responsibility for DataType objects
|
35
|
+
* Two new connection handlers: ``ParentDelegationHandler`` for owned DataTypes and ``StandaloneConnectionHandler`` for independent DataTypes
|
36
|
+
* Enhanced ``direct_access`` method with automatic transaction/pipeline context detection
|
37
|
+
* Shared ``Familia::Connection::Behavior`` module extracting common connection functionality
|
38
|
+
|
39
|
+
This enhancement addresses a critical gap where standalone DataType objects could not guarantee atomicity across multiple operations. A prime example is session storage implementations (similar to Rack::Session stores) where setting session data and expiration must be atomic to prevent memory leaks or security issues. Both parent-owned DataTypes (delegating to parent Horreum objects) and standalone DataTypes now support the full transaction and pipeline API.
|
40
|
+
|
41
|
+
Example usage:
|
42
|
+
|
43
|
+
.. code-block:: ruby
|
44
|
+
|
45
|
+
# Recommended: Use DataType methods for clean, key-free syntax
|
46
|
+
# Parent-owned DataType transaction
|
47
|
+
user.scores.transaction do
|
48
|
+
user.scores.add('level1', 100)
|
49
|
+
user.scores.add('level2', 200)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Standalone DataType transaction (e.g., session storage)
|
53
|
+
session_store = Familia::StringKey.new('session:abc123')
|
54
|
+
session_store.transaction do
|
55
|
+
session_store.set(session_data)
|
56
|
+
session_store.update_expiration(expiration: 3600)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Pipeline for performance optimization
|
60
|
+
leaderboard.pipelined do
|
61
|
+
leaderboard.add('player1', 500)
|
62
|
+
leaderboard.add('player2', 600)
|
63
|
+
leaderboard.size
|
64
|
+
end
|
65
|
+
|
66
|
+
# Advanced: Connection available for low-level Redis commands when needed
|
67
|
+
user.scores.transaction do |conn|
|
68
|
+
conn.zadd(user.scores.dbkey, 100, 'level1')
|
69
|
+
conn.hset(user.profile.dbkey, 'status', 'active')
|
70
|
+
end
|
71
|
+
|
72
|
+
Changed
|
73
|
+
-------
|
74
|
+
|
75
|
+
- **DataType URI Construction** - DataType objects with ``logical_database`` settings now return clean URIs without custom port information (e.g., ``redis://127.0.0.1/3`` instead of ``redis://127.0.0.1:2525/3``), ensuring consistent URI representation across the library.
|
76
|
+
|
77
|
+
- **Horreum::Connection Refactored** - The ``Horreum::Connection`` module now includes ``Familia::Connection::Behavior``, eliminating code duplication by sharing URI normalization and connection creation methods between Horreum and DataType. This refactoring improves maintainability while preserving all existing functionality.
|
78
|
+
|
79
|
+
AI Assistance
|
80
|
+
-------------
|
81
|
+
|
82
|
+
This feature was implemented with significant AI assistance from Claude (Anthropic). The AI helped with:
|
83
|
+
|
84
|
+
* Architectural design of the connection chain pattern for DataType objects
|
85
|
+
* Implementation of the shared Behavior module to extract common functionality
|
86
|
+
* Creation of DataType-specific connection handlers (ParentDelegationHandler, StandaloneConnectionHandler)
|
87
|
+
* Comprehensive test coverage including transaction and pipeline integration tests
|
88
|
+
* Documentation and changelog preparation
|
89
|
+
* Debugging and fixing URI formatting edge cases
|
90
|
+
|
91
|
+
The implementation preserves backward compatibility (all 2,216 existing tests pass) while adding 27 new tests specifically for DataType transaction and pipeline support.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
.. A new scriv changelog fragment.
|
2
|
+
..
|
3
|
+
.. Uncomment the section that is right (remove the leading dots).
|
4
|
+
.. For top level release notes, leave all the headers commented out.
|
5
|
+
..
|
6
|
+
Added
|
7
|
+
-----
|
8
|
+
|
9
|
+
- Automatic validation in ``add_to_*`` methods for instance-scoped unique indexes. Previously required manual ``guard_unique_*!`` call before adding to index; now validation happens automatically with clear error messages on duplicate detection.
|
10
|
+
|
11
|
+
- Transaction detection in ``save()`` method. Raises ``Familia::OperationModeError`` when ``save()`` is called within an existing transaction, since unique index guards need to read current values which is not possible inside MULTI/EXEC blocks.
|
12
|
+
|
13
|
+
Changed
|
14
|
+
-------
|
15
|
+
|
16
|
+
- Instance-scoped unique index ``add_to_*`` methods now automatically validate uniqueness before adding to parent's index. This matches modern ORM expectations where constraint validation happens implicitly during mutation operations.
|
17
|
+
|
18
|
+
Documentation
|
19
|
+
-------------
|
20
|
+
|
21
|
+
- Enhanced ``save()`` method documentation to explain transaction restrictions and unique index validation flow.
|
22
|
+
|
23
|
+
- Updated ``UniqueIndexGenerators`` documentation to clarify that ``add_to_*`` methods perform automatic validation.
|
24
|
+
|
25
|
+
- Added comprehensive test suite (21 test cases) demonstrating automatic validation behavior, transaction detection, and error handling patterns.
|
26
|
+
|
27
|
+
AI Assistance
|
28
|
+
-------------
|
29
|
+
|
30
|
+
- Claude Sonnet 4.5 assisted with implementation design, test coverage, and documentation for automatic unique index validation and transaction detection features.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
Changed
|
3
|
+
-------
|
4
|
+
|
5
|
+
- **IndexingRelationship**: Added explicit ``:within`` field to preserve the original DSL parameter, replacing brittle ``target_class`` equality checks with clearer ``within.nil?`` checks. This makes the distinction between class-level and instance-scoped indexes more explicit and prevents potential issues with inheritance scenarios.
|
6
|
+
|
7
|
+
AI Assistance
|
8
|
+
-------------
|
9
|
+
|
10
|
+
- Design review and architectural analysis by Claude Code (Sonnet 4.5) via second-opinion agent, identifying brittleness in class comparison logic and recommending explicit storage of the ``within`` parameter.
|
11
|
+
- Implementation of the ``within`` field addition across IndexingRelationship, generators, and usage sites by Claude Code.
|
12
|
+
- All tests verified passing with no behavioral changes.
|
13
|
+
..
|
@@ -0,0 +1,26 @@
|
|
1
|
+
.. Internal terminology refactoring for indexing relationships
|
2
|
+
|
3
|
+
Changed
|
4
|
+
-------
|
5
|
+
|
6
|
+
- **Indexing terminology refactoring**: Renamed internal field ``target_class`` to ``scope_class`` throughout the indexing system to better reflect the semantic role. The ``within:`` parameter in index declarations refers to a "scope" that provides a uniqueness boundary, not a "target" or "parent" relationship. This change affects internal code, comments, and documentation but has no user-facing API impact.
|
7
|
+
|
8
|
+
- Renamed ``IndexingRelationship.target_class`` to ``scope_class``
|
9
|
+
- Updated method parameter names from ``target_instance`` to ``scope_instance``
|
10
|
+
- Replaced "parent" terminology with "scope" in comments and documentation
|
11
|
+
- Updated cheatsheets to reflect correct terminology
|
12
|
+
|
13
|
+
**Rationale**: The term "target" created semantic confusion because it has different meanings in participation relationships (where objects target a collection owner) versus indexing relationships (where objects use a scope for uniqueness). The term "parent" was misleading because it implied an ownership relationship that doesn't exist. "Scope" accurately describes the role: Company provides the scope within which badge_number must be unique.
|
14
|
+
|
15
|
+
Documentation
|
16
|
+
-------------
|
17
|
+
|
18
|
+
- Updated indexing and relationships cheatsheets with improved terminology explanations
|
19
|
+
- Added explicit clarification of scope vs target vs parent semantics
|
20
|
+
|
21
|
+
AI Assistance
|
22
|
+
-------------
|
23
|
+
|
24
|
+
- Claude Code (Sonnet 4.5) provided second-opinion analysis on terminology confusion
|
25
|
+
- Assisted with systematic refactoring of variable names, comments, and documentation
|
26
|
+
- Helped identify all occurrences requiring updates across codebase and tests
|