familia 2.0.0.pre15 → 2.0.0.pre17
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/.github/workflows/ci.yml +2 -2
- data/.github/workflows/code-quality.yml +138 -0
- data/.github/workflows/code-smells.yml +85 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +3 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +54 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +86 -4
- data/CLAUDE.md +39 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +99 -23
- data/LICENSE.txt +1 -1
- data/README.md +285 -85
- data/changelog.d/README.md +2 -2
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
- data/docs/archive/FAMILIA_TECHNICAL.md +42 -42
- data/docs/archive/FAMILIA_UPDATE.md +3 -3
- data/docs/archive/README.md +3 -2
- data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
- data/docs/conf.py +29 -0
- data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
- data/docs/guides/feature-encrypted-fields.md +785 -0
- data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
- data/docs/guides/feature-external-identifiers.md +637 -0
- data/docs/guides/feature-object-identifiers.md +435 -0
- data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
- data/docs/guides/feature-relationships-methods.md +684 -0
- data/docs/guides/feature-relationships.md +200 -0
- data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
- data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
- data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
- data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
- data/docs/guides/index.md +176 -0
- data/docs/guides/{Security-Model.md → security-model.md} +1 -1
- data/docs/migrating/v2.0.0-pre.md +1 -1
- data/docs/migrating/v2.0.0-pre11.md +2 -2
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre5.md +33 -12
- data/docs/migrating/v2.0.0-pre6.md +2 -2
- data/docs/migrating/v2.0.0-pre7.md +8 -8
- data/docs/overview.md +624 -20
- data/docs/reference/api-technical.md +1365 -0
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
- data/examples/autoloader/mega_customer.rb +3 -1
- data/examples/encrypted_fields.rb +378 -0
- data/examples/json_usage_patterns.rb +144 -0
- data/examples/relationships.rb +13 -13
- data/examples/safe_dump.rb +7 -7
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +51 -10
- data/lib/familia/connection/handlers.rb +223 -0
- data/lib/familia/connection/individual_command_proxy.rb +64 -0
- data/lib/familia/connection/middleware.rb +75 -0
- data/lib/familia/connection/operation_core.rb +93 -0
- data/lib/familia/connection/operations.rb +277 -0
- data/lib/familia/connection/pipeline_core.rb +87 -0
- data/lib/familia/connection/transaction_core.rb +100 -0
- data/lib/familia/connection.rb +60 -186
- data/lib/familia/data_type/class_methods.rb +63 -0
- data/lib/familia/data_type/commands.rb +53 -51
- data/lib/familia/data_type/connection.rb +83 -0
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/settings.rb +96 -0
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +15 -11
- data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
- data/lib/familia/data_type/types/lock.rb +3 -2
- data/lib/familia/data_type/types/sorted_set.rb +128 -14
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -9
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +12 -171
- data/lib/familia/distinguisher.rb +85 -0
- data/lib/familia/encryption/encrypted_data.rb +15 -24
- data/lib/familia/encryption/manager.rb +6 -4
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
- data/lib/familia/encryption/request_cache.rb +7 -7
- data/lib/familia/encryption.rb +2 -3
- data/lib/familia/errors.rb +9 -3
- data/lib/familia/features/autoloader.rb +30 -12
- data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
- data/lib/familia/features/encrypted_fields.rb +71 -66
- data/lib/familia/features/expiration/extensions.rb +1 -1
- data/lib/familia/features/expiration.rb +31 -26
- data/lib/familia/features/external_identifier.rb +57 -19
- data/lib/familia/features/object_identifier.rb +134 -25
- data/lib/familia/features/quantization.rb +16 -21
- data/lib/familia/features/relationships/README.md +97 -0
- data/lib/familia/features/relationships/collection_operations.rb +104 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +306 -0
- data/lib/familia/features/relationships/indexing.rb +182 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +164 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
- data/lib/familia/features/relationships/participation.rb +656 -0
- data/lib/familia/features/relationships/participation_relationship.rb +31 -0
- data/lib/familia/features/relationships/score_encoding.rb +20 -20
- data/lib/familia/features/relationships.rb +65 -266
- data/lib/familia/features/safe_dump.rb +127 -130
- data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
- data/lib/familia/features/transient_fields.rb +10 -7
- data/lib/familia/features.rb +10 -14
- data/lib/familia/field_type.rb +6 -4
- data/lib/familia/horreum/connection.rb +297 -0
- data/lib/familia/horreum/{core/database_commands.rb → database_commands.rb} +27 -17
- data/lib/familia/horreum/{subclass/definition.rb → definition.rb} +139 -74
- data/lib/familia/horreum/{subclass/management.rb → management.rb} +73 -27
- data/lib/familia/horreum/{core/serialization.rb → persistence.rb} +108 -185
- data/lib/familia/horreum/{subclass/related_fields_management.rb → related_fields.rb} +104 -23
- data/lib/familia/horreum/serialization.rb +172 -0
- data/lib/familia/horreum/{shared/settings.rb → settings.rb} +2 -1
- data/lib/familia/horreum/{core/utils.rb → utils.rb} +2 -1
- data/lib/familia/horreum.rb +222 -119
- data/lib/familia/json_serializer.rb +0 -1
- data/lib/familia/logging.rb +11 -114
- data/lib/familia/refinements/dear_json.rb +122 -0
- data/lib/familia/refinements/logger_trace.rb +20 -17
- data/lib/familia/refinements/stylize_words.rb +65 -0
- data/lib/familia/refinements/time_literals.rb +60 -52
- data/lib/familia/refinements.rb +2 -1
- data/lib/familia/secure_identifier.rb +60 -28
- data/lib/familia/settings.rb +83 -7
- data/lib/familia/utils.rb +5 -87
- data/lib/familia/verifiable_identifier.rb +4 -4
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +72 -14
- data/lib/middleware/database_middleware.rb +56 -14
- data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
- data/try/configuration/scenarios_try.rb +2 -2
- data/try/connection/fiber_context_preservation_try.rb +250 -0
- data/try/connection/handler_constraints_try.rb +59 -0
- data/try/connection/operation_mode_guards_try.rb +208 -0
- data/try/connection/pipeline_fallback_integration_try.rb +128 -0
- data/try/connection/responsibility_chain_tracking_try.rb +72 -0
- data/try/connection/transaction_fallback_integration_try.rb +288 -0
- data/try/connection/transaction_mode_permissive_try.rb +153 -0
- data/try/connection/transaction_mode_strict_try.rb +98 -0
- data/try/connection/transaction_mode_warn_try.rb +131 -0
- data/try/connection/transaction_modes_try.rb +249 -0
- data/try/core/autoloader_try.rb +120 -2
- data/try/core/connection_try.rb +10 -10
- data/try/core/conventional_inheritance_try.rb +130 -0
- data/try/core/create_method_try.rb +15 -23
- data/try/core/database_consistency_try.rb +11 -10
- data/try/core/errors_try.rb +11 -14
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- data/try/core/familia_try.rb +1 -1
- data/try/core/isolated_dbclient_try.rb +165 -0
- data/try/core/middleware_try.rb +16 -16
- data/try/core/persistence_operations_try.rb +4 -4
- data/try/core/pools_try.rb +42 -26
- data/try/core/secure_identifier_try.rb +28 -24
- data/try/core/time_utils_try.rb +10 -10
- data/try/core/tools_try.rb +3 -3
- data/try/core/utils_try.rb +2 -2
- data/try/data_types/boolean_try.rb +4 -4
- data/try/data_types/datatype_base_try.rb +0 -2
- data/try/data_types/list_try.rb +10 -10
- data/try/data_types/sorted_set_try.rb +5 -5
- data/try/data_types/sorted_set_zadd_options_try.rb +625 -0
- data/try/data_types/string_try.rb +12 -12
- data/try/data_types/unsortedset_try.rb +33 -0
- data/try/debugging/cache_behavior_tracer.rb +7 -7
- data/try/debugging/debug_aad_process.rb +1 -1
- data/try/debugging/debug_concealed_internal.rb +1 -1
- data/try/debugging/debug_cross_context.rb +1 -1
- data/try/debugging/debug_fresh_cross_context.rb +1 -1
- data/try/debugging/encryption_method_tracer.rb +10 -10
- data/try/edge_cases/hash_symbolization_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/encryption/config_persistence_try.rb +2 -2
- data/try/encryption/encryption_core_try.rb +19 -19
- data/try/encryption/instance_variable_scope_try.rb +1 -1
- data/try/encryption/module_loading_try.rb +2 -2
- data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
- data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/encryption/secure_memory_handling_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
- data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
- data/try/features/encrypted_fields/fresh_key_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 +7 -7
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +3 -3
- data/try/features/field_groups_try.rb +244 -0
- data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
- data/try/features/object_identifier/object_identifier_try.rb +10 -0
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
- data/try/features/relationships/indexing_try.rb +443 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
- data/try/features/relationships/participation_commands_verification_try.rb +105 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
- data/try/features/relationships/participation_reverse_index_try.rb +196 -0
- data/try/features/relationships/relationships_api_changes_try.rb +72 -71
- data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
- data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
- data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
- data/try/features/relationships/relationships_performance_try.rb +20 -20
- data/try/features/relationships/relationships_try.rb +27 -38
- data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
- data/try/features/transient_fields/refresh_reset_try.rb +3 -1
- data/try/features/transient_fields/simple_refresh_test.rb +1 -1
- data/try/helpers/test_cleanup.rb +86 -0
- data/try/helpers/test_helpers.rb +6 -7
- data/try/horreum/auto_indexing_on_save_try.rb +212 -0
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +3 -1
- data/try/horreum/defensive_initialization_try.rb +86 -0
- data/try/horreum/destroy_related_fields_cleanup_try.rb +332 -0
- data/try/horreum/initialization_try.rb +11 -7
- data/try/horreum/relations_try.rb +21 -13
- data/try/horreum/serialization_try.rb +12 -11
- data/try/horreum/settings_try.rb +2 -0
- data/try/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +2 -2
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +13 -15
- data/try/models/datatype_base_try.rb +3 -3
- data/try/models/familia_object_try.rb +9 -8
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
- data/try/prototypes/atomic_saves_v4.rb +1 -1
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
- data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
- data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
- data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
- data/try/prototypes/pooling/pool_siege.rb +11 -11
- data/try/prototypes/pooling/run_stress_tests.rb +7 -7
- data/try/refinements/dear_json_array_methods_try.rb +53 -0
- data/try/refinements/dear_json_hash_methods_try.rb +54 -0
- data/try/refinements/logger_trace_methods_try.rb +44 -0
- data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
- data/try/refinements/time_literals_string_methods_try.rb +80 -0
- data/try/valkey.conf +26 -0
- metadata +92 -52
- data/.rubocop_todo.yml +0 -208
- data/docs/connection_pooling.md +0 -192
- data/docs/guides/Connection-Pooling-Guide.md +0 -437
- data/docs/guides/Encrypted-Fields-Overview.md +0 -101
- data/docs/guides/Feature-System-Autoloading.md +0 -198
- data/docs/guides/Home.md +0 -116
- data/docs/guides/Relationships-Guide.md +0 -737
- data/docs/guides/relationships-methods.md +0 -266
- data/docs/reference/auditing_database_commands.rb +0 -228
- data/examples/permissions.rb +0 -240
- data/lib/familia/features/relationships/cascading.rb +0 -437
- data/lib/familia/features/relationships/membership.rb +0 -497
- data/lib/familia/features/relationships/permission_management.rb +0 -264
- data/lib/familia/features/relationships/querying.rb +0 -615
- data/lib/familia/features/relationships/redis_operations.rb +0 -274
- data/lib/familia/features/relationships/tracking.rb +0 -418
- data/lib/familia/horreum/core/connection.rb +0 -73
- data/lib/familia/horreum/core.rb +0 -21
- data/lib/familia/refinements/snake_case.rb +0 -40
- data/lib/familia/validation/command_recorder.rb +0 -336
- data/lib/familia/validation/expectations.rb +0 -519
- data/lib/familia/validation/validation_helpers.rb +0 -443
- data/lib/familia/validation/validator.rb +0 -412
- data/lib/familia/validation.rb +0 -140
- data/try/data_types/set_try.rb +0 -33
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
- data/try/validation/atomic_operations_try.rb.disabled +0 -320
- data/try/validation/command_validation_try.rb.disabled +0 -207
- data/try/validation/performance_validation_try.rb.disabled +0 -324
- data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -0,0 +1,332 @@
|
|
1
|
+
# try/horreum/destroy_related_fields_cleanup_try.rb
|
2
|
+
|
3
|
+
# Horreum destroy! Related Fields Cleanup Tryouts
|
4
|
+
#
|
5
|
+
# Tests that when a Horreum instance is destroyed, all its related fields
|
6
|
+
# (lists, sets, sorted sets, hashes, etc.) are also properly cleaned up
|
7
|
+
# to prevent orphaned Redis keys.
|
8
|
+
#
|
9
|
+
# This addresses the bug where destroy! only deleted the main object key
|
10
|
+
# but left related field keys in the database.
|
11
|
+
|
12
|
+
require_relative '../helpers/test_helpers'
|
13
|
+
|
14
|
+
MANY_FIELD_MULTIPLIER = 10
|
15
|
+
|
16
|
+
# Test model with various related fields
|
17
|
+
class ::DestroyTestUser < Familia::Horreum
|
18
|
+
identifier_field :user_id
|
19
|
+
field :user_id
|
20
|
+
field :name
|
21
|
+
field :email
|
22
|
+
|
23
|
+
# Various DataType relations to test cleanup
|
24
|
+
list :activity_log
|
25
|
+
set :tags
|
26
|
+
zset :scores
|
27
|
+
hashkey :settings
|
28
|
+
string :status_message
|
29
|
+
end
|
30
|
+
|
31
|
+
class ::CustomOptionsUser < Familia::Horreum
|
32
|
+
identifier_field :user_id
|
33
|
+
field :user_id
|
34
|
+
|
35
|
+
# Related fields with various options
|
36
|
+
list :custom_log, ttl: 3600
|
37
|
+
set :custom_tags, default: ['default_tag']
|
38
|
+
zset :custom_scores, class: self
|
39
|
+
end
|
40
|
+
|
41
|
+
class ::ParentModel < Familia::Horreum
|
42
|
+
identifier_field :parent_id
|
43
|
+
field :parent_id
|
44
|
+
list :children_ids
|
45
|
+
set :child_tags
|
46
|
+
end
|
47
|
+
|
48
|
+
class ::ChildModel < Familia::Horreum
|
49
|
+
identifier_field :child_id
|
50
|
+
field :child_id
|
51
|
+
field :parent_id
|
52
|
+
list :child_activities
|
53
|
+
end
|
54
|
+
|
55
|
+
class ::ManyFieldsModel < Familia::Horreum
|
56
|
+
identifier_field :model_id
|
57
|
+
field :model_id
|
58
|
+
|
59
|
+
# Create many different types of related fields
|
60
|
+
MANY_FIELD_MULTIPLIER.times do |i|
|
61
|
+
list :"list_#{i}"
|
62
|
+
set :"set_#{i}"
|
63
|
+
zset :"zset_#{i}"
|
64
|
+
hashkey :"hash_#{i}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# From transaction_fallback_integration_try.rb for bug verification test
|
69
|
+
class ::IntegrationTestUser < Familia::Horreum
|
70
|
+
identifier_field :user_id
|
71
|
+
field :user_id
|
72
|
+
field :name
|
73
|
+
field :email
|
74
|
+
field :status
|
75
|
+
list :activity_log
|
76
|
+
set :tags
|
77
|
+
zset :scores
|
78
|
+
end
|
79
|
+
|
80
|
+
## Related fields are cleaned up when instance is destroyed
|
81
|
+
user = DestroyTestUser.new(user_id: 'cleanup_test_001', name: 'Test User')
|
82
|
+
|
83
|
+
# Populate related fields with data
|
84
|
+
user.activity_log.add('login')
|
85
|
+
user.activity_log.add('profile_update')
|
86
|
+
|
87
|
+
user.tags.add('premium')
|
88
|
+
user.tags.add('verified')
|
89
|
+
|
90
|
+
user.scores.add('game_score', 100)
|
91
|
+
user.scores.add('quiz_score', 85)
|
92
|
+
|
93
|
+
user.settings['theme'] = 'dark'
|
94
|
+
user.settings['notifications'] = 'enabled'
|
95
|
+
|
96
|
+
user.status_message.value = 'Online'
|
97
|
+
|
98
|
+
user.save
|
99
|
+
|
100
|
+
# Verify data exists before destruction
|
101
|
+
keys_before = [
|
102
|
+
user.dbkey,
|
103
|
+
user.activity_log.dbkey,
|
104
|
+
user.tags.dbkey,
|
105
|
+
user.scores.dbkey,
|
106
|
+
user.settings.dbkey,
|
107
|
+
user.status_message.dbkey,
|
108
|
+
]
|
109
|
+
|
110
|
+
keys_exist_before = keys_before.all? { |key| user.dbclient.exists(key) > 0 }
|
111
|
+
|
112
|
+
# Destroy the user
|
113
|
+
destroy_result = user.destroy!
|
114
|
+
|
115
|
+
# Verify all keys are cleaned up after destruction
|
116
|
+
keys_exist_after = keys_before.any? { |key| user.dbclient.exists(key) > 0 }
|
117
|
+
|
118
|
+
# Should successfully destroy and clean up all related keys
|
119
|
+
destroy_result && keys_exist_before && !keys_exist_after
|
120
|
+
#=> true
|
121
|
+
|
122
|
+
## Class-level destroy! also cleans up related fields
|
123
|
+
|
124
|
+
user = DestroyTestUser.new(user_id: 'class_destroy_test_001')
|
125
|
+
|
126
|
+
# Add some related field data
|
127
|
+
user.activity_log.add('created')
|
128
|
+
user.tags.add('new_user')
|
129
|
+
user.scores.add('initial_score', 0)
|
130
|
+
user.save
|
131
|
+
|
132
|
+
# Verify keys exist
|
133
|
+
keys_before = [
|
134
|
+
user.dbkey,
|
135
|
+
user.activity_log.dbkey,
|
136
|
+
user.tags.dbkey,
|
137
|
+
user.scores.dbkey,
|
138
|
+
]
|
139
|
+
keys_exist_before = keys_before.all? { |key| user.dbclient.exists(key) > 0 }
|
140
|
+
|
141
|
+
# Use class-level destroy!
|
142
|
+
destroy_result = DestroyTestUser.destroy!('class_destroy_test_001')
|
143
|
+
|
144
|
+
# Verify all keys are cleaned up
|
145
|
+
keys_exist_after = keys_before.any? { |key| user.dbclient.exists(key) > 0 }
|
146
|
+
|
147
|
+
destroy_result && keys_exist_before && !keys_exist_after
|
148
|
+
#=> true
|
149
|
+
|
150
|
+
## Empty related fields don't cause errors during cleanup
|
151
|
+
user = DestroyTestUser.new(user_id: 'empty_fields_test_001')
|
152
|
+
user.save
|
153
|
+
|
154
|
+
# Don't add any data to related fields - they should be empty
|
155
|
+
|
156
|
+
# Destroy should work without errors even with empty related fields
|
157
|
+
result = user.destroy!
|
158
|
+
|
159
|
+
# Verify main key is gone
|
160
|
+
main_key_gone = user.dbclient.exists(user.dbkey) == 0
|
161
|
+
|
162
|
+
result && main_key_gone
|
163
|
+
#=> true
|
164
|
+
|
165
|
+
## Related fields with custom options are handled properly
|
166
|
+
user = CustomOptionsUser.new(user_id: 'custom_options_test_001')
|
167
|
+
|
168
|
+
# Add data to custom related fields
|
169
|
+
user.custom_log.add('custom_event')
|
170
|
+
user.custom_tags.add('custom_tag')
|
171
|
+
user.custom_scores.add('custom_score', 50)
|
172
|
+
user.save
|
173
|
+
|
174
|
+
# Verify keys exist
|
175
|
+
keys_before = [
|
176
|
+
user.dbkey,
|
177
|
+
user.custom_log.dbkey,
|
178
|
+
user.custom_tags.dbkey,
|
179
|
+
user.custom_scores.dbkey,
|
180
|
+
]
|
181
|
+
keys_exist_before = keys_before.all? { |key| user.dbclient.exists(key) > 0 }
|
182
|
+
|
183
|
+
# Destroy and verify cleanup
|
184
|
+
destroy_result = user.destroy!
|
185
|
+
keys_exist_after = keys_before.any? { |key| user.dbclient.exists(key) > 0 }
|
186
|
+
|
187
|
+
# Clean up the test class to avoid pollution
|
188
|
+
Object.send(:remove_const, :CustomOptionsUser) if Object.const_defined?(:CustomOptionsUser)
|
189
|
+
|
190
|
+
destroy_result && keys_exist_before && !keys_exist_after
|
191
|
+
#=> true
|
192
|
+
|
193
|
+
## Nested destruction handles complex related field hierarchies
|
194
|
+
|
195
|
+
parent = ParentModel.new(parent_id: 'parent_001')
|
196
|
+
child1 = ChildModel.new(child_id: 'child_001', parent_id: 'parent_001')
|
197
|
+
child2 = ChildModel.new(child_id: 'child_002', parent_id: 'parent_001')
|
198
|
+
|
199
|
+
# Set up relationships
|
200
|
+
parent.children_ids.add('child_001')
|
201
|
+
parent.children_ids.add('child_002')
|
202
|
+
parent.child_tags.add('family_tag')
|
203
|
+
|
204
|
+
child1.child_activities.add('child1_activity')
|
205
|
+
child2.child_activities.add('child2_activity')
|
206
|
+
|
207
|
+
parent.save
|
208
|
+
child1.save
|
209
|
+
child2.save
|
210
|
+
|
211
|
+
# Verify all keys exist
|
212
|
+
all_keys = [
|
213
|
+
parent.dbkey, parent.children_ids.dbkey, parent.child_tags.dbkey,
|
214
|
+
child1.dbkey, child1.child_activities.dbkey,
|
215
|
+
child2.dbkey, child2.child_activities.dbkey
|
216
|
+
]
|
217
|
+
keys_exist_before = all_keys.all? { |key| parent.dbclient.exists(key) > 0 }
|
218
|
+
|
219
|
+
# Destroy parent - should clean up all parent's related fields
|
220
|
+
parent_destroy_result = parent.destroy!
|
221
|
+
|
222
|
+
# Parent and its related fields should be gone
|
223
|
+
parent_keys = [parent.dbkey, parent.children_ids.dbkey, parent.child_tags.dbkey]
|
224
|
+
parent_keys_gone = parent_keys.none? { |key| parent.dbclient.exists(key) > 0 }
|
225
|
+
|
226
|
+
# Child objects should still exist (they're separate objects)
|
227
|
+
child_keys = [child1.dbkey, child1.child_activities.dbkey, child2.dbkey, child2.child_activities.dbkey]
|
228
|
+
child_keys_exist = child_keys.all? { |key| child1.dbclient.exists(key) > 0 }
|
229
|
+
|
230
|
+
# Clean up children
|
231
|
+
child1.destroy!
|
232
|
+
child2.destroy!
|
233
|
+
|
234
|
+
# Clean up test classes
|
235
|
+
Object.send(:remove_const, :ParentModel) if Object.const_defined?(:ParentModel)
|
236
|
+
Object.send(:remove_const, :ChildModel) if Object.const_defined?(:ChildModel)
|
237
|
+
|
238
|
+
parent_destroy_result && keys_exist_before && parent_keys_gone && child_keys_exist
|
239
|
+
#=> true
|
240
|
+
|
241
|
+
## Performance check - destroying object with many related fields
|
242
|
+
model = ManyFieldsModel.new(model_id: 'many_fields_001')
|
243
|
+
|
244
|
+
# Add data to some of the fields
|
245
|
+
MANY_FIELD_MULTIPLIER.times do |i|
|
246
|
+
model.send(:"list_#{i}").add("item_#{i}")
|
247
|
+
model.send(:"set_#{i}").add("tag_#{i}")
|
248
|
+
model.send(:"zset_#{i}").add("score_#{i}", i * 10)
|
249
|
+
model.send(:"hash_#{i}")["key_#{i}"] = "value_#{i}"
|
250
|
+
end
|
251
|
+
|
252
|
+
model.save
|
253
|
+
|
254
|
+
destroy_result = model.destroy!
|
255
|
+
|
256
|
+
# Should result in success and also complete in a reasonable amount of
|
257
|
+
# time (under 100ms for this test). I acknowledge this is flaky.
|
258
|
+
[destroy_result.class, destroy_result.successful?, destroy_result.results.size]
|
259
|
+
#=> [MultiResult, true, 41]
|
260
|
+
#=%> 100
|
261
|
+
|
262
|
+
## Verify transaction_fallback_integration_try.rb bug is fixed
|
263
|
+
# Recreate the scenario from the failing test
|
264
|
+
user = IntegrationTestUser.new(user_id: 'bugfix_test_001')
|
265
|
+
|
266
|
+
# Add data to related fields like the original test
|
267
|
+
user.activity_log.add('user_created')
|
268
|
+
user.activity_log.add('profile_updated')
|
269
|
+
user.tags.add('premium')
|
270
|
+
user.tags.add('verified')
|
271
|
+
user.scores.add('game_score', 100)
|
272
|
+
user.scores.add('quiz_score', 85)
|
273
|
+
|
274
|
+
# Save the user so the main object key exists
|
275
|
+
user.save
|
276
|
+
|
277
|
+
# Verify keys exist before destruction
|
278
|
+
keys_before = [
|
279
|
+
user.dbkey,
|
280
|
+
user.activity_log.dbkey,
|
281
|
+
user.tags.dbkey,
|
282
|
+
user.scores.dbkey,
|
283
|
+
]
|
284
|
+
keys_exist_before = keys_before.all? { |key| user.dbclient.exists(key) > 0 }
|
285
|
+
|
286
|
+
# The original destroy! call that was leaving orphaned keys
|
287
|
+
destroy_result = user.destroy!
|
288
|
+
|
289
|
+
# Now all related keys should be properly cleaned up
|
290
|
+
keys_exist_after = keys_before.any? { |key| user.dbclient.exists(key) > 0 }
|
291
|
+
|
292
|
+
Object.send(:remove_const, :ManyFieldsModel) if Object.const_defined?(:ManyFieldsModel)
|
293
|
+
|
294
|
+
destroy_result && keys_exist_before && !keys_exist_after
|
295
|
+
#=> true
|
296
|
+
|
297
|
+
## Test destroy! with init hook that depends on identifier
|
298
|
+
# This verifies that the temp instance initialization fix works correctly
|
299
|
+
class TestModelWithInit < Familia::Horreum
|
300
|
+
identifier_field :user_id
|
301
|
+
field :user_id
|
302
|
+
field :region
|
303
|
+
list :activities
|
304
|
+
|
305
|
+
def init(*args, **kwargs)
|
306
|
+
# Set region based on user_id (simulates real-world logic)
|
307
|
+
self.region = user_id.split('-').first if user_id
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
# Create object - init should set region based on user_id
|
312
|
+
init_obj = TestModelWithInit.new(user_id: "us-west-123")
|
313
|
+
init_obj.save
|
314
|
+
init_obj.activities << "login"
|
315
|
+
init_obj.activities << "purchase"
|
316
|
+
|
317
|
+
# Verify init worked and region is set
|
318
|
+
region_set_correctly = init_obj.region == "us"
|
319
|
+
|
320
|
+
# Verify related field key includes region (would be nil without fix)
|
321
|
+
activities_key = init_obj.activities.dbkey
|
322
|
+
|
323
|
+
# Destroy using class method - temp instance init should execute with identifier
|
324
|
+
TestModelWithInit.destroy!("us-west-123")
|
325
|
+
|
326
|
+
# Verify all keys are cleaned up (including activities with correct key)
|
327
|
+
activities_cleaned = TestModelWithInit.dbclient.exists(activities_key).zero?
|
328
|
+
|
329
|
+
Object.send(:remove_const, :TestModelWithInit) if Object.const_defined?(:TestModelWithInit)
|
330
|
+
|
331
|
+
region_set_correctly && activities_cleaned
|
332
|
+
#=> true
|
@@ -10,9 +10,9 @@ Familia.debug = false
|
|
10
10
|
#=> ["tryouts-29@test.com", "John Doe"]
|
11
11
|
|
12
12
|
## Keyword argument initialization works (order independent)
|
13
|
-
@customer2 = Customer.new(name: 'Jane
|
13
|
+
@customer2 = Customer.new(name: 'Jane Windows', custid: 'jane@test.com', email: 'jane@example.com')
|
14
14
|
[@customer2.custid, @customer2.name, @customer2.email]
|
15
|
-
#=> ["jane@test.com", "Jane
|
15
|
+
#=> ["jane@test.com", "Jane Windows", "jane@example.com"]
|
16
16
|
|
17
17
|
## Keyword arguments are order independent (different order, same result)
|
18
18
|
@customer3 = Customer.new(email: 'bob@example.com', custid: 'bob@test.com', name: 'Bob Jones')
|
@@ -50,11 +50,11 @@ Familia.debug = false
|
|
50
50
|
|
51
51
|
## to_h works correctly with keyword-initialized objects
|
52
52
|
@customer2.to_h["name"]
|
53
|
-
#=> "Jane
|
53
|
+
#=> "Jane Windows"
|
54
54
|
|
55
55
|
## to_a works correctly with keyword-initialized objects
|
56
56
|
@customer2.to_a[4] # name field should be the fifth field defined in the class
|
57
|
-
#=> "Jane
|
57
|
+
#=> "Jane Windows"
|
58
58
|
|
59
59
|
## Session has limited fields (only sessid defined)
|
60
60
|
@session1 = Session.new('sess123')
|
@@ -103,12 +103,16 @@ Familia.debug = false
|
|
103
103
|
[@customer6, @complex].map(&:delete!)
|
104
104
|
#=> [true, true]
|
105
105
|
|
106
|
-
## "Cleaning up" test objects that were never saved returns
|
106
|
+
## "Cleaning up" test objects that were never saved returns true regardless
|
107
|
+
## b/c it takes place in a transaction and it's the transaction's success
|
108
|
+
## that successful? is based on. If you look at the MultiResult#results,
|
109
|
+
## it's an array of 0s, except for the @customer1 that had been saved.
|
110
|
+
## That's b/c DEL command returns the number of keys deleted.
|
107
111
|
@customer1.save
|
108
112
|
[
|
109
113
|
@customer1, @customer2, @customer3, @customer4, @customer6, @customer7,
|
110
114
|
@session1, @session2, @session3,
|
111
115
|
@domain1, @domain2,
|
112
116
|
@partial, @complex
|
113
|
-
].map(&:destroy!)
|
114
|
-
|
117
|
+
].map(&:destroy!).map(&:areyouhappynow?)
|
118
|
+
#==> result.all?(true)
|
@@ -56,7 +56,7 @@ tags = @test_user.tags
|
|
56
56
|
scores = @test_user.scores
|
57
57
|
prefs = @test_user.preferences
|
58
58
|
[sessions.class.name, tags.class.name, scores.class.name, prefs.class.name]
|
59
|
-
#=> ["Familia::
|
59
|
+
#=> ["Familia::ListKey", "Familia::UnsortedSet", "Familia::SortedSet", "Familia::HashKey"]
|
60
60
|
|
61
61
|
## Database types use correct dbkeys
|
62
62
|
@test_user.sessions.dbkey
|
@@ -72,7 +72,7 @@ prefs = @test_user.preferences
|
|
72
72
|
@test_user.sessions.size
|
73
73
|
#=> 2
|
74
74
|
|
75
|
-
## Can work with
|
75
|
+
## Can work with UnsortedSet Database type
|
76
76
|
@test_user.tags.clear
|
77
77
|
@test_user.tags.add('ruby', 'valkey', 'web')
|
78
78
|
@test_user.tags.size
|
@@ -80,8 +80,8 @@ prefs = @test_user.preferences
|
|
80
80
|
|
81
81
|
## Can work with SortedSet Database type
|
82
82
|
@test_user.scores.clear
|
83
|
-
@test_user.scores.add(
|
84
|
-
@test_user.scores.add(
|
83
|
+
@test_user.scores.add('level1', 100)
|
84
|
+
@test_user.scores.add('level2', 200)
|
85
85
|
@test_user.scores.size
|
86
86
|
#=> 2
|
87
87
|
|
@@ -108,16 +108,21 @@ prefs = @test_user.preferences
|
|
108
108
|
@test_product.views.value
|
109
109
|
#=> 6
|
110
110
|
|
111
|
-
## Database types maintain parent
|
112
|
-
@test_user.sessions.parent
|
113
|
-
|
111
|
+
## Database types maintain ParentDefinition reference, not the parent itself
|
112
|
+
@test_user.sessions.parent
|
113
|
+
#=/> @test_user
|
114
|
+
#=:> Familia::Horreum::ParentDefinition
|
115
|
+
|
116
|
+
## Database types maintain ParentDefinition reference, not the parent itself
|
117
|
+
@test_user.sessions.parent
|
118
|
+
#=> Familia::Horreum::ParentDefinition.from_parent(@test_user)
|
114
119
|
|
115
120
|
## Database types know their field name
|
116
121
|
@test_user.tags.keystring
|
117
122
|
#=> :tags
|
118
123
|
|
119
124
|
## Can check if Database types exist
|
120
|
-
@test_user.scores.add(
|
125
|
+
@test_user.scores.add('test', 50)
|
121
126
|
before_exists = @test_user.scores.exists?
|
122
127
|
@test_user.scores.clear
|
123
128
|
after_exists = @test_user.scores.exists?
|
@@ -130,15 +135,18 @@ after_exists = @test_user.scores.exists?
|
|
130
135
|
@test_user.preferences.exists?
|
131
136
|
#=> false
|
132
137
|
|
133
|
-
## Parent object destruction
|
134
|
-
@test_user.sessions.
|
138
|
+
## Parent object destruction DOES clean up relations (since v2.0.0.pre16)
|
139
|
+
@test_user.sessions.push('cleanup_test')
|
135
140
|
@test_user.destroy!
|
136
141
|
@test_user.sessions.exists?
|
137
|
-
#=>
|
142
|
+
#=> false
|
138
143
|
|
139
144
|
## If the parent instance is still in memory, can use it
|
140
145
|
## to access and clear the child field.
|
141
|
-
@test_user.sessions.
|
142
|
-
|
146
|
+
@test_user.sessions.add(Familia.now)
|
147
|
+
@test_user.sessions.size
|
148
|
+
#=> 1
|
149
|
+
|
150
|
+
@test_user.sessions.delete!
|
143
151
|
|
144
152
|
@test_product.destroy!
|
@@ -13,8 +13,8 @@ Familia.debug = false
|
|
13
13
|
#=> true
|
14
14
|
|
15
15
|
## save_if_not_exists saves new customer successfully
|
16
|
-
Familia.dbclient.set('debug:starting_save_if_not_exists_tests',
|
17
|
-
@test_id = "#{
|
16
|
+
Familia.dbclient.set('debug:starting_save_if_not_exists_tests', Familia.now.to_s)
|
17
|
+
@test_id = "#{Familia.now.to_i}-#{rand(1000)}"
|
18
18
|
@new_customer = Customer.new "new-customer-#{@test_id}@test.com"
|
19
19
|
@new_customer.name = 'New Customer'
|
20
20
|
@new_customer.save_if_not_exists
|
@@ -34,7 +34,7 @@ Familia.dbclient.set('debug:starting_save_if_not_exists_tests', Time.now.to_s)
|
|
34
34
|
#=> true
|
35
35
|
|
36
36
|
## End of save_if_not_exists tests
|
37
|
-
Familia.dbclient.set('debug:ending_save_if_not_exists_tests',
|
37
|
+
Familia.dbclient.set('debug:ending_save_if_not_exists_tests', Familia.now.to_s)
|
38
38
|
|
39
39
|
## save_if_not_exists persists data correctly
|
40
40
|
@another_new_customer.refresh!
|
@@ -62,7 +62,7 @@ Familia.dbclient.set('debug:ending_save_if_not_exists_tests', Time.now.to_s)
|
|
62
62
|
#=> "John Doe"
|
63
63
|
|
64
64
|
## batch_update can update multiple fields atomically, to_h
|
65
|
-
@result = @customer.batch_update(name: 'Jane
|
65
|
+
@result = @customer.batch_update(name: 'Jane Windows', email: 'jane@example.com')
|
66
66
|
@result.to_h
|
67
67
|
#=> {:success=>true, :results=>[0, 0]}
|
68
68
|
|
@@ -80,12 +80,12 @@ Familia.dbclient.set('debug:ending_save_if_not_exists_tests', Time.now.to_s)
|
|
80
80
|
|
81
81
|
## batch_update updates object fields in memory, confirm fields changed
|
82
82
|
[@customer.name, @customer.email]
|
83
|
-
#=> ["Jane
|
83
|
+
#=> ["Jane Windows", "jane@example.com"]
|
84
84
|
|
85
|
-
## batch_update persists to Redis
|
85
|
+
## batch_update persists to Valkey/Redis
|
86
86
|
@customer.refresh!
|
87
87
|
[@customer.name, @customer.email]
|
88
|
-
#=> ["Jane
|
88
|
+
#=> ["Jane Windows", "jane@example.com"]
|
89
89
|
|
90
90
|
## batch_update with update_expiration: false works
|
91
91
|
@customer.batch_update(name: 'Bob Jones', update_expiration: false)
|
@@ -139,7 +139,7 @@ end
|
|
139
139
|
result.size
|
140
140
|
#=> 2
|
141
141
|
|
142
|
-
## refresh! reloads from Redis
|
142
|
+
## refresh! reloads from Valkey/Redis
|
143
143
|
@customer.refresh!
|
144
144
|
@customer.hget('temp_field')
|
145
145
|
#=> "temp_value"
|
@@ -151,13 +151,14 @@ result.successful?
|
|
151
151
|
|
152
152
|
## destroy! removes object from Database (1 of 2)
|
153
153
|
@customer.destroy!
|
154
|
-
|
154
|
+
#=:> MultiResult
|
155
|
+
#==> result.successful?
|
155
156
|
|
156
157
|
## After destroy!, dbkey no longer exists (2 of 2)
|
157
158
|
@customer.exists?
|
158
159
|
#=> false
|
159
160
|
|
160
|
-
## destroy! removes object from Redis, not the in-memory object (2 of 2)
|
161
|
+
## destroy! removes object from Valkey/Redis, not the in-memory object (2 of 2)
|
161
162
|
@customer.refresh!
|
162
163
|
@customer.name
|
163
164
|
#=> "Bob Jones"
|
@@ -183,7 +184,7 @@ result.successful?
|
|
183
184
|
[@fresh_customer.role, @fresh_customer.planid]
|
184
185
|
#=> ["admin", "premium"]
|
185
186
|
|
186
|
-
## Fresh customer changes persist to Redis
|
187
|
+
## Fresh customer changes persist to Valkey/Redis
|
187
188
|
@fresh_customer.refresh!
|
188
189
|
[@fresh_customer.role, @fresh_customer.planid]
|
189
190
|
#=> ["admin", "premium"]
|
data/try/horreum/settings_try.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative '../helpers/test_helpers'
|
4
4
|
|
5
5
|
class TestUser < Familia::Horreum
|
6
|
-
using Familia::Refinements::
|
6
|
+
using Familia::Refinements::StylizeWords
|
7
7
|
|
8
8
|
identifier_field :email
|
9
9
|
field :email
|
@@ -40,14 +40,14 @@ end
|
|
40
40
|
user_class.prefix
|
41
41
|
#=!> Familia::Problem
|
42
42
|
|
43
|
-
##
|
43
|
+
## DataType relations with Horreum expiration
|
44
44
|
user_class = TestUser
|
45
45
|
|
46
46
|
user = user_class.new(email: "test@example.com")
|
47
47
|
user.save
|
48
48
|
user.expire(1800)
|
49
49
|
|
50
|
-
# Create related
|
50
|
+
# Create related DataType
|
51
51
|
tags = user.tags
|
52
52
|
tags << "ruby" << "redis"
|
53
53
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# See example output at end.
|
7
7
|
|
8
|
-
#
|
8
|
+
# UnsortedSet CONTAINER_ID to $CONTAINER_ID or the first argument
|
9
9
|
CONTAINER_ID=${CONTAINER_ID:-$1}
|
10
10
|
|
11
11
|
if [ -z "$CONTAINER_ID" ]; then
|
@@ -59,7 +59,7 @@ docker exec $CONTAINER_ID bash -c '
|
|
59
59
|
# $
|
60
60
|
# $ docker run --rm -d -p 3000:3000 \
|
61
61
|
# -e SECRET=$SECRET \
|
62
|
-
# -e REDIS_URL=redis://host.docker.internal:
|
62
|
+
# -e REDIS_URL=redis://host.docker.internal:2525/0 \
|
63
63
|
# ghcr.io/onetimesecret/devtimesecret-lite:latest
|
64
64
|
#
|
65
65
|
# abcd1234
|
data/try/models/customer_try.rb
CHANGED
@@ -46,7 +46,7 @@ Customer.find_by_id(ident).planid
|
|
46
46
|
#=> 1
|
47
47
|
|
48
48
|
## Customer can add custom domain via add method
|
49
|
-
@customer.custom_domains.add(
|
49
|
+
@customer.custom_domains.add('example.org', @now)
|
50
50
|
@customer.custom_domains.members.include?('example.org')
|
51
51
|
#=> true
|
52
52
|
|
@@ -69,7 +69,7 @@ Customer.find_by_id(ident).planid
|
|
69
69
|
#=> true
|
70
70
|
|
71
71
|
## Customer can be added to class-level sorted set
|
72
|
-
Customer.instances
|
72
|
+
Customer.instances.add(@customer.identifier)
|
73
73
|
Customer.instances.member?(@customer)
|
74
74
|
#=> true
|
75
75
|
|
@@ -89,27 +89,29 @@ Customer.instances.member?(@customer)
|
|
89
89
|
#=> "reset123"
|
90
90
|
|
91
91
|
## Customer can be destroyed
|
92
|
-
|
92
|
+
multi_result = @customer.destroy!
|
93
93
|
cust = Customer.find_by_id('test@example.com')
|
94
94
|
exists = Customer.exists?('test@example.com')
|
95
|
-
[
|
96
|
-
#=> [
|
95
|
+
[multi_result.results, cust.nil?, exists]
|
96
|
+
#=> [[1, 0, 1, 1, 1, 1, 1], true, false]
|
97
97
|
|
98
98
|
## Customer.destroy! can be called on an already destroyed object
|
99
99
|
@customer.destroy!
|
100
|
-
|
100
|
+
#=:> MultiResult
|
101
|
+
#==> result.successful?
|
102
|
+
#=*> result.results
|
101
103
|
|
102
104
|
## Customer.logical_database returns the correct database number
|
103
105
|
Customer.logical_database
|
104
|
-
#=>
|
106
|
+
#=> 3
|
105
107
|
|
106
108
|
## Customer.logical_database returns the correct database number
|
107
109
|
@customer.logical_database
|
108
|
-
#=>
|
110
|
+
#=> 3
|
109
111
|
|
110
112
|
## @customer.dbclient.connection returns the correct database URI
|
111
113
|
@customer.dbclient.connection
|
112
|
-
#=> {:host=>"127.0.0.1", :port=>
|
114
|
+
#=> {:host=>"127.0.0.1", :port=>2525, :db=>3, :id=>"redis://127.0.0.1:2525/3", :location=>"127.0.0.1:2525"}
|
113
115
|
|
114
116
|
## @customer.dbclient.uri returns the correct database URI
|
115
117
|
@customer.secrets_created.logical_database
|
@@ -117,23 +119,19 @@ Customer.logical_database
|
|
117
119
|
|
118
120
|
## @customer.dbclient.uri returns the correct database URI
|
119
121
|
@customer.secrets_created.dbclient.connection
|
120
|
-
#=> {:host=>"127.0.0.1", :port=>
|
122
|
+
#=> {:host=>"127.0.0.1", :port=>2525, :db=>3, :id=>"redis://127.0.0.1:2525/3", :location=>"127.0.0.1:2525"}
|
121
123
|
|
122
124
|
## Customer.url is nil by default
|
123
125
|
Customer.uri
|
124
126
|
#=> nil
|
125
127
|
|
126
|
-
## Customer.destroy! makes only one call to Redis
|
127
|
-
DatabaseCommandCounter.count_commands { @customer.destroy! }
|
128
|
-
#=> 1
|
129
|
-
|
130
128
|
## Customer.logical_database returns the correct database number
|
131
129
|
Customer.instances.logical_database
|
132
130
|
#=> nil
|
133
131
|
|
134
132
|
## Customer.logical_database returns the correct database number
|
135
133
|
Customer.instances.uri.to_s
|
136
|
-
#=> 'redis://127.0.0.1/
|
134
|
+
#=> 'redis://127.0.0.1/3'
|
137
135
|
|
138
136
|
# Teardown
|
139
137
|
Customer.instances.delete!
|