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
@@ -1,13 +1,13 @@
|
|
1
1
|
# try/features/relationships/relationships_api_changes_try.rb
|
2
2
|
#
|
3
3
|
# Test coverage for Familia v2 relationships API changes
|
4
|
-
# Testing new
|
4
|
+
# Testing new class_participates_in and unique_index methods
|
5
5
|
# Testing breaking changes and argument validation
|
6
6
|
|
7
7
|
require_relative '../../helpers/test_helpers'
|
8
8
|
|
9
9
|
# Test classes for new API
|
10
|
-
class ApiTestUser < Familia::Horreum
|
10
|
+
class ::ApiTestUser < Familia::Horreum
|
11
11
|
feature :relationships
|
12
12
|
|
13
13
|
identifier_field :user_id
|
@@ -17,16 +17,16 @@ class ApiTestUser < Familia::Horreum
|
|
17
17
|
field :created_at
|
18
18
|
field :status
|
19
19
|
|
20
|
-
# New API:
|
21
|
-
|
22
|
-
|
20
|
+
# New API: class_participates_in for class-level participation
|
21
|
+
class_participates_in :all_users, score: :created_at
|
22
|
+
class_participates_in :active_users, score: -> { status == 'active' ? Familia.now.to_i : 0 }
|
23
23
|
|
24
|
-
# New API:
|
25
|
-
|
26
|
-
|
24
|
+
# New API: unique_index for class-level indexing
|
25
|
+
unique_index :email, :email_lookup
|
26
|
+
unique_index :username, :username_lookup, query: false
|
27
27
|
end
|
28
28
|
|
29
|
-
class ApiTestProject < Familia::Horreum
|
29
|
+
class ::ApiTestProject < Familia::Horreum
|
30
30
|
feature :relationships
|
31
31
|
|
32
32
|
identifier_field :project_id
|
@@ -35,7 +35,7 @@ class ApiTestProject < Familia::Horreum
|
|
35
35
|
field :created_at
|
36
36
|
end
|
37
37
|
|
38
|
-
class ApiTestMembership < Familia::Horreum
|
38
|
+
class ::ApiTestMembership < Familia::Horreum
|
39
39
|
feature :relationships
|
40
40
|
|
41
41
|
identifier_field :membership_id
|
@@ -45,20 +45,21 @@ class ApiTestMembership < Familia::Horreum
|
|
45
45
|
field :role
|
46
46
|
field :created_at
|
47
47
|
|
48
|
-
# New API: using
|
49
|
-
|
50
|
-
|
48
|
+
# New API: using target: parameter
|
49
|
+
multi_index :user_id, :user_memberships, within: ApiTestUser
|
50
|
+
multi_index :project_id, :project_memberships, within: ApiTestProject
|
51
51
|
|
52
|
-
#
|
53
|
-
|
52
|
+
# Participation with parent class
|
53
|
+
participates_in ApiTestProject, :memberships, score: :created_at
|
54
54
|
end
|
55
55
|
|
56
|
+
|
56
57
|
# Setup test objects
|
57
58
|
@user = ApiTestUser.new(
|
58
59
|
user_id: 'user_123',
|
59
60
|
email: 'test@example.com',
|
60
61
|
username: 'testuser',
|
61
|
-
created_at:
|
62
|
+
created_at: Familia.now.to_i,
|
62
63
|
status: 'active'
|
63
64
|
)
|
64
65
|
|
@@ -66,14 +67,14 @@ end
|
|
66
67
|
user_id: 'user_456',
|
67
68
|
email: 'inactive@example.com',
|
68
69
|
username: 'inactiveuser',
|
69
|
-
created_at:
|
70
|
+
created_at: Familia.now.to_i - 3600,
|
70
71
|
status: 'inactive'
|
71
72
|
)
|
72
73
|
|
73
74
|
@project = ApiTestProject.new(
|
74
75
|
project_id: 'proj_789',
|
75
76
|
name: 'Test Project',
|
76
|
-
created_at:
|
77
|
+
created_at: Familia.now.to_i
|
77
78
|
)
|
78
79
|
|
79
80
|
@membership = ApiTestMembership.new(
|
@@ -81,34 +82,34 @@ end
|
|
81
82
|
user_id: @user.user_id,
|
82
83
|
project_id: @project.project_id,
|
83
84
|
role: 'admin',
|
84
|
-
created_at:
|
85
|
+
created_at: Familia.now.to_i
|
85
86
|
)
|
86
87
|
|
87
88
|
# =============================================
|
88
|
-
# 1. New API:
|
89
|
+
# 1. New API: class_participates_in Method Tests
|
89
90
|
# =============================================
|
90
91
|
|
91
|
-
##
|
92
|
+
## class_participates_in generates class-level collection class methods
|
92
93
|
ApiTestUser.respond_to?(:all_users)
|
93
94
|
#=> true
|
94
95
|
|
95
|
-
##
|
96
|
+
## class_participates_in generates class-level collection access methods
|
96
97
|
ApiTestUser.respond_to?(:active_users)
|
97
98
|
#=> true
|
98
99
|
|
99
|
-
##
|
100
|
+
## class_participates_in generates class methods for adding items
|
100
101
|
ApiTestUser.respond_to?(:add_to_all_users)
|
101
102
|
#=> true
|
102
103
|
|
103
|
-
##
|
104
|
+
## class_participates_in generates class methods for removing items
|
104
105
|
ApiTestUser.respond_to?(:remove_from_all_users)
|
105
106
|
#=> true
|
106
107
|
|
107
|
-
##
|
108
|
+
## class_participates_in generates membership check methods
|
108
109
|
@user.respond_to?(:in_class_all_users?)
|
109
110
|
#=> true
|
110
111
|
|
111
|
-
##
|
112
|
+
## class_participates_in generates score retrieval methods
|
112
113
|
@user.respond_to?(:score_in_class_all_users)
|
113
114
|
#=> true
|
114
115
|
|
@@ -129,49 +130,49 @@ score.is_a?(Float) && score > 0
|
|
129
130
|
#=> true
|
130
131
|
|
131
132
|
## Score calculation works for lambda scores with active user
|
132
|
-
@user
|
133
|
+
ApiTestUser.add_to_active_users(@user) # Explicit addition to active_users collection
|
133
134
|
active_score = ApiTestUser.active_users.score(@user.identifier)
|
134
135
|
active_score > 0
|
135
136
|
#=> true
|
136
137
|
|
137
138
|
## Score calculation works for lambda scores with inactive user
|
138
|
-
@inactive_user
|
139
|
+
ApiTestUser.add_to_active_users(@inactive_user) # Explicit addition to active_users collection
|
139
140
|
ApiTestUser.active_users.member?(@inactive_user.identifier)
|
140
141
|
#=> true
|
141
142
|
|
142
143
|
# =============================================
|
143
|
-
# 2. New API:
|
144
|
+
# 2. New API: unique_index Method Tests
|
144
145
|
# =============================================
|
145
146
|
|
146
|
-
##
|
147
|
+
## unique_index with query: true generates query methods
|
147
148
|
ApiTestUser.respond_to?(:find_by_email)
|
148
149
|
#=> true
|
149
150
|
|
150
|
-
##
|
151
|
+
## unique_index with query: true generates bulk query methods
|
151
152
|
ApiTestUser.respond_to?(:find_all_by_email)
|
152
153
|
#=> true
|
153
154
|
|
154
|
-
##
|
155
|
+
## unique_index with query: false does not generate query methods
|
155
156
|
ApiTestUser.respond_to?(:find_by_username)
|
156
157
|
#=> false
|
157
158
|
|
158
|
-
##
|
159
|
+
## unique_index generates class-level index access methods
|
159
160
|
ApiTestUser.respond_to?(:email_lookup)
|
160
161
|
#=> true
|
161
162
|
|
162
|
-
##
|
163
|
+
## unique_index generates class-level index rebuild methods
|
163
164
|
ApiTestUser.respond_to?(:rebuild_email_lookup)
|
164
165
|
#=> true
|
165
166
|
|
166
|
-
##
|
167
|
+
## unique_index generates instance methods for class indexing
|
167
168
|
@user.respond_to?(:add_to_class_email_lookup)
|
168
169
|
#=> true
|
169
170
|
|
170
|
-
##
|
171
|
+
## unique_index generates removal methods
|
171
172
|
@user.respond_to?(:remove_from_class_email_lookup)
|
172
173
|
#=> true
|
173
174
|
|
174
|
-
##
|
175
|
+
## unique_index generates update methods
|
175
176
|
@user.respond_to?(:update_in_class_email_lookup)
|
176
177
|
#=> true
|
177
178
|
|
@@ -189,16 +190,16 @@ ApiTestUser.email_lookup.get(@user.email) == @user.user_id
|
|
189
190
|
# 3. New API: parent: Parameter Tests
|
190
191
|
# =============================================
|
191
192
|
|
192
|
-
##
|
193
|
-
@membership.respond_to?(:
|
193
|
+
## multi_index with within: generates context-specific methods
|
194
|
+
@membership.respond_to?(:add_to_api_test_user_user_memberships)
|
194
195
|
#=> true
|
195
196
|
|
196
|
-
##
|
197
|
-
@membership.respond_to?(:
|
197
|
+
## multi_index with within: generates removal methods
|
198
|
+
@membership.respond_to?(:remove_from_api_test_user_user_memberships)
|
198
199
|
#=> true
|
199
200
|
|
200
|
-
##
|
201
|
-
@membership.respond_to?(:
|
201
|
+
## multi_index with within: generates update methods
|
202
|
+
@membership.respond_to?(:update_in_api_test_user_user_memberships)
|
202
203
|
#=> true
|
203
204
|
|
204
205
|
## Parent class gets finder methods for indexed relationships
|
@@ -212,18 +213,18 @@ true
|
|
212
213
|
# 4. Breaking Changes: ArgumentError Tests
|
213
214
|
# =============================================
|
214
215
|
|
215
|
-
##
|
216
|
+
## class_participates_in creates class-level collections without error
|
216
217
|
test_class = Class.new(Familia::Horreum) do
|
217
218
|
feature :relationships
|
218
|
-
|
219
|
+
class_participates_in :test_collection
|
219
220
|
end
|
220
221
|
test_class.respond_to?(:test_collection)
|
221
222
|
#=> true
|
222
223
|
|
223
|
-
##
|
224
|
+
## unique_index works like class-level (old feature)
|
224
225
|
test_class = Class.new(Familia::Horreum) do
|
225
226
|
feature :relationships
|
226
|
-
|
227
|
+
unique_index :test_field, :test_index
|
227
228
|
end
|
228
229
|
test_class.respond_to?(:indexing_relationships)
|
229
230
|
##=> true
|
@@ -242,39 +243,39 @@ instance_methods = @user.methods.grep(/class_/)
|
|
242
243
|
instance_methods.all? { |m| m.to_s.include?('class_') }
|
243
244
|
#=> true
|
244
245
|
|
245
|
-
##
|
246
|
-
|
247
|
-
|
246
|
+
## Context-based methods use snake_case class names
|
247
|
+
context_methods = @membership.methods.grep(/api_test_user/)
|
248
|
+
context_methods.length > 0
|
248
249
|
#=> true
|
249
250
|
|
250
251
|
# =============================================
|
251
252
|
# 6. Metadata Storage Tests
|
252
253
|
# =============================================
|
253
254
|
|
254
|
-
##
|
255
|
-
|
256
|
-
|
257
|
-
#=>
|
255
|
+
## class_participates_in stores correct target_class (now a Class object)
|
256
|
+
participation_meta = ApiTestUser.participation_relationships.find { |r| r.collection_name == :all_users }
|
257
|
+
participation_meta.target_class
|
258
|
+
#=> ApiTestUser
|
258
259
|
|
259
|
-
##
|
260
|
-
|
261
|
-
|
262
|
-
#=>
|
260
|
+
## class_participates_in stores correct target_class via familia_name
|
261
|
+
participation_meta = ApiTestUser.participation_relationships.find { |r| r.collection_name == :all_users }
|
262
|
+
participation_meta.target_class.familia_name
|
263
|
+
#=> 'ApiTestUser'
|
263
264
|
|
264
|
-
##
|
265
|
-
indexing_meta = ApiTestUser.indexing_relationships.find { |r| r
|
266
|
-
indexing_meta
|
267
|
-
#=>
|
265
|
+
## unique_index stores correct target_class
|
266
|
+
indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
|
267
|
+
indexing_meta.target_class
|
268
|
+
#=> ApiTestUser
|
268
269
|
|
269
|
-
##
|
270
|
-
indexing_meta = ApiTestUser.indexing_relationships.find { |r| r
|
271
|
-
indexing_meta
|
272
|
-
#=>
|
270
|
+
## unique_index stores correct target_class via familia_name
|
271
|
+
indexing_meta = ApiTestUser.indexing_relationships.find { |r| r.index_name == :email_lookup }
|
272
|
+
indexing_meta.target_class.familia_name
|
273
|
+
#=> 'ApiTestUser'
|
273
274
|
|
274
|
-
##
|
275
|
-
membership_meta = ApiTestMembership.indexing_relationships.find { |r| r
|
276
|
-
membership_meta
|
277
|
-
#=>
|
275
|
+
## multi_index with within: stores correct metadata
|
276
|
+
membership_meta = ApiTestMembership.indexing_relationships.find { |r| r.index_name == :user_memberships }
|
277
|
+
membership_meta.target_class
|
278
|
+
#=> ApiTestUser
|
278
279
|
|
279
280
|
# =============================================
|
280
281
|
# 7. Functional Integration Tests
|
@@ -288,7 +289,7 @@ ApiTestUser.all_users.member?(@user.identifier) && ApiTestUser.email_lookup.get(
|
|
288
289
|
## Parent-based relationships work with tracking
|
289
290
|
@project.save
|
290
291
|
# Note: Skipping complex parent relationship test
|
291
|
-
@membership.respond_to?(:
|
292
|
+
@membership.respond_to?(:add_to_api_test_project_memberships)
|
292
293
|
#=> true
|
293
294
|
|
294
295
|
## Score-based tracking maintains proper ordering
|
@@ -24,13 +24,10 @@ class EdgeTestDomain < Familia::Horreum
|
|
24
24
|
field :created_at
|
25
25
|
field :score_value
|
26
26
|
|
27
|
-
# Test different score calculation methods - simplified
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
member_of EdgeTestCustomer, :domains, type: :sorted_set
|
32
|
-
member_of EdgeTestCustomer, :simple_domains, type: :set
|
33
|
-
member_of EdgeTestCustomer, :domain_list, type: :list
|
27
|
+
# Test different score calculation methods and collection types - simplified
|
28
|
+
participates_in EdgeTestCustomer, :domains, score: :created_at, type: :sorted_set
|
29
|
+
participates_in EdgeTestCustomer, :simple_domains, type: :set
|
30
|
+
participates_in EdgeTestCustomer, :domain_list, type: :list
|
34
31
|
|
35
32
|
def calculated_score
|
36
33
|
(score_value || 0) * 2
|
@@ -58,19 +55,19 @@ end
|
|
58
55
|
# Score encoding edge cases
|
59
56
|
|
60
57
|
## Score encoding handles maximum metadata value
|
61
|
-
max_score = @domain1.encode_score(
|
58
|
+
max_score = @domain1.encode_score(Familia.now, 255)
|
62
59
|
decoded = @domain1.decode_score(max_score)
|
63
60
|
decoded[:permissions]
|
64
61
|
#=> 255
|
65
62
|
|
66
63
|
## Score encoding handles zero metadata
|
67
|
-
zero_score = @domain1.encode_score(
|
64
|
+
zero_score = @domain1.encode_score(Familia.now, 0)
|
68
65
|
decoded_zero = @domain1.decode_score(zero_score)
|
69
66
|
decoded_zero[:permissions]
|
70
67
|
#=> 0
|
71
68
|
|
72
69
|
## Permission encoding handles unknown permission levels
|
73
|
-
unknown_perm_score = @domain1.permission_encode(
|
70
|
+
unknown_perm_score = @domain1.permission_encode(Familia.now, :unknown_permission)
|
74
71
|
decoded_unknown = @domain1.permission_decode(unknown_perm_score)
|
75
72
|
decoded_unknown[:permission_list]
|
76
73
|
#=> []
|
@@ -90,7 +87,7 @@ decoded_large[:permissions]
|
|
90
87
|
#=> 123
|
91
88
|
|
92
89
|
## Permission encoding maps correctly
|
93
|
-
read_score = @domain1.permission_encode(
|
90
|
+
read_score = @domain1.permission_encode(Familia.now, :read)
|
94
91
|
decoded_read = @domain1.permission_decode(read_score)
|
95
92
|
decoded_read[:permission_list].include?(:read)
|
96
93
|
#=> true
|
@@ -102,7 +99,7 @@ decoded_epoch[:permissions]
|
|
102
99
|
#=> 42
|
103
100
|
|
104
101
|
## Boundary score values work correctly
|
105
|
-
boundary_score = @domain1.encode_score(
|
102
|
+
boundary_score = @domain1.encode_score(Familia.now, 255)
|
106
103
|
decoded_boundary = @domain1.decode_score(boundary_score)
|
107
104
|
decoded_boundary[:permissions] <= 255
|
108
105
|
#=> true
|
@@ -112,22 +109,22 @@ decoded_boundary[:permissions] <= 255
|
|
112
109
|
## Method score calculation works with saved objects
|
113
110
|
@customer1.save
|
114
111
|
@domain1.save
|
115
|
-
@domain1.
|
116
|
-
method_score = @domain1.
|
112
|
+
@domain1.add_to_edge_test_customer_domains(@customer1)
|
113
|
+
method_score = @domain1.score_in_edge_test_customer_domains(@customer1)
|
117
114
|
method_score.is_a?(Float) && method_score > 0
|
118
115
|
#=> true
|
119
116
|
|
120
117
|
## Sorted set membership works
|
121
|
-
@domain1.
|
118
|
+
@domain1.in_edge_test_customer_domains?(@customer1)
|
122
119
|
#=> true
|
123
120
|
|
124
121
|
## Score methods respond correctly
|
125
|
-
@domain1.respond_to?(:
|
122
|
+
@domain1.respond_to?(:score_in_edge_test_customer_domains)
|
126
123
|
#=> true
|
127
124
|
|
128
125
|
## Basic relationship cleanup works
|
129
|
-
@domain1.
|
130
|
-
@domain1.
|
126
|
+
@domain1.remove_from_edge_test_customer_domains(@customer1)
|
127
|
+
@domain1.in_edge_test_customer_domains?(@customer1)
|
131
128
|
#=> false
|
132
129
|
|
133
130
|
# Clean up test data
|
@@ -44,7 +44,7 @@ save_time = Benchmark.realtime do
|
|
44
44
|
@domains.each do |domain|
|
45
45
|
domain.save
|
46
46
|
# Use numeric scores for sorted sets
|
47
|
-
MinimalDomain.all_domains.add(domain.
|
47
|
+
MinimalDomain.all_domains.add(domain.identifier, domain.priority_score.to_f)
|
48
48
|
MinimalDomain.active_domains.add(domain.identifier)
|
49
49
|
MinimalDomain.domain_history.push(domain.identifier)
|
50
50
|
MinimalDomain.domain_lookup[domain.display_domain] = domain.identifier
|
@@ -105,7 +105,7 @@ end
|
|
105
105
|
large_time = Benchmark.realtime do
|
106
106
|
@large_domains.each do |domain|
|
107
107
|
domain.save
|
108
|
-
MinimalDomain.all_domains.add(domain.
|
108
|
+
MinimalDomain.all_domains.add(domain.identifier, domain.priority_score.to_f)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -30,7 +30,7 @@ class SimplePerfDomain < Familia::Horreum
|
|
30
30
|
class_hashkey :domain_lookup
|
31
31
|
|
32
32
|
# Define tracking relationships for testing
|
33
|
-
|
33
|
+
participates_in SimplePerfCustomer, :simple_domains, score: :created_at
|
34
34
|
end
|
35
35
|
|
36
36
|
# =============================================
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
SimplePerfDomain.new(
|
47
47
|
domain_id: "simple_perf_domain_#{i}",
|
48
48
|
display_domain: "simple#{i}.example.com",
|
49
|
-
created_at:
|
49
|
+
created_at: Familia.now.to_i + i,
|
50
50
|
priority_score: rand(100)
|
51
51
|
)
|
52
52
|
end
|
@@ -58,15 +58,15 @@ save_time = Benchmark.realtime do
|
|
58
58
|
# Manually add to collections for testing (ensure numeric score)
|
59
59
|
# Convert created_at to float, handling nil case
|
60
60
|
score = if domain.created_at.nil?
|
61
|
-
|
61
|
+
Familia.now
|
62
62
|
elsif domain.created_at.is_a?(Time)
|
63
63
|
domain.created_at.to_f
|
64
64
|
else
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
domain.created_at.to_f
|
66
|
+
end
|
67
|
+
SimplePerfDomain.all_domains.add(domain.identifier, score)
|
68
|
+
SimplePerfDomain.domain_lookup[domain.display_domain] = domain.identifier
|
69
|
+
end
|
70
70
|
end
|
71
71
|
|
72
72
|
# Should complete in reasonable time
|
@@ -39,7 +39,7 @@ class PerfDomain < Familia::Horreum
|
|
39
39
|
class_hashkey :id_lookup
|
40
40
|
|
41
41
|
# Define tracking relationships for testing
|
42
|
-
|
42
|
+
participates_in PerfCustomer, :domains, score: :created_at
|
43
43
|
end
|
44
44
|
|
45
45
|
# Integration test with other features
|
@@ -73,7 +73,7 @@ end
|
|
73
73
|
# =============================================
|
74
74
|
|
75
75
|
## Create test objects for performance testing
|
76
|
-
@customer = PerfCustomer.new(custid: 'perf_customer', name: 'Performance Test', created_at:
|
76
|
+
@customer = PerfCustomer.new(custid: 'perf_customer', name: 'Performance Test', created_at: Familia.now.to_i)
|
77
77
|
@customer.save
|
78
78
|
|
79
79
|
# Create multiple domains for bulk operations
|
@@ -81,7 +81,7 @@ end
|
|
81
81
|
PerfDomain.new(
|
82
82
|
domain_id: "perf_domain_#{i}",
|
83
83
|
display_domain: "perf#{i}.example.com",
|
84
|
-
created_at:
|
84
|
+
created_at: Familia.now.to_i + i,
|
85
85
|
priority_score: rand(100),
|
86
86
|
customer_id: @customer.custid
|
87
87
|
)
|
@@ -93,8 +93,8 @@ save_time = Benchmark.realtime do
|
|
93
93
|
domain.save
|
94
94
|
# Manually populate collections for testing
|
95
95
|
score = domain.created_at.is_a?(Time) ? domain.created_at.to_f : domain.created_at.to_f
|
96
|
-
PerfDomain.all_domains.add(
|
97
|
-
PerfDomain.priority_queue.add(domain.
|
96
|
+
PerfDomain.all_domains.add(domain.identifier, score)
|
97
|
+
PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
|
98
98
|
PerfDomain.active_domains.add(domain.identifier)
|
99
99
|
PerfDomain.domain_history.push(domain.identifier)
|
100
100
|
PerfDomain.domain_lookup[domain.display_domain] = domain.identifier
|
@@ -153,7 +153,7 @@ instances = 100.times.map { |i| PerfDomain.new(domain_id: "mem_test_#{i}") }
|
|
153
153
|
after_instances = ObjectSpace.count_objects[:T_OBJECT]
|
154
154
|
|
155
155
|
# Should not have created excessive objects (relationship metadata is shared)
|
156
|
-
(after_instances - before_instances) <
|
156
|
+
(after_instances - before_instances) < 250 # Allow for reasonable, 2.5x overhead
|
157
157
|
#=> true
|
158
158
|
|
159
159
|
## Class-level relationship metadata should be constant size
|
@@ -203,19 +203,19 @@ operation_threads = 3.times.map do |i|
|
|
203
203
|
domain = PerfDomain.new(
|
204
204
|
domain_id: "thread_test_#{i}",
|
205
205
|
display_domain: "thread#{i}.test.com",
|
206
|
-
created_at:
|
206
|
+
created_at: Familia.now.to_i + i,
|
207
207
|
priority_score: i * 10
|
208
208
|
)
|
209
209
|
domain.save
|
210
210
|
|
211
211
|
# Add to collections manually
|
212
|
-
PerfDomain.all_domains.add(domain.
|
213
|
-
PerfDomain.priority_queue.add(domain.
|
212
|
+
PerfDomain.all_domains.add(domain.identifier, domain.created_at.to_f)
|
213
|
+
PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
|
214
214
|
domain.save
|
215
215
|
|
216
216
|
# Manually add to collections for thread test
|
217
|
-
PerfDomain.all_domains.add(domain.
|
218
|
-
PerfDomain.priority_queue.add(domain.
|
217
|
+
PerfDomain.all_domains.add(domain.identifier, domain.created_at.to_f)
|
218
|
+
PerfDomain.priority_queue.add(domain.identifier, domain.priority_score)
|
219
219
|
|
220
220
|
# Verify the domain was added to collections
|
221
221
|
in_all = PerfDomain.all_domains.member?(domain.identifier)
|
@@ -244,7 +244,7 @@ thread_results.all? { |result| result == [true, true] }
|
|
244
244
|
# =============================================
|
245
245
|
|
246
246
|
## Integration with safe_dump (if available)
|
247
|
-
integration_model = IntegrationTestModel.new(id: 'integration_test', data: 'test_data', created_at:
|
247
|
+
integration_model = IntegrationTestModel.new(id: 'integration_test', data: 'test_data', created_at: Familia.now.to_i)
|
248
248
|
|
249
249
|
if integration_model.respond_to?(:safe_dump)
|
250
250
|
integration_model.save
|
@@ -260,7 +260,7 @@ end
|
|
260
260
|
#=:> String
|
261
261
|
|
262
262
|
## Integration with expiration (if available)
|
263
|
-
integration_model2 = IntegrationTestModel.new(id: 'integration_test2', data: 'test_data2', created_at:
|
263
|
+
integration_model2 = IntegrationTestModel.new(id: 'integration_test2', data: 'test_data2', created_at: Familia.now.to_i)
|
264
264
|
if integration_model2.respond_to?(:default_expiration)
|
265
265
|
# Test that expiration works with relationship collections
|
266
266
|
if integration_model2.class.respond_to?(:all_items)
|
@@ -315,15 +315,15 @@ stress_time < 0.1
|
|
315
315
|
test_domain = PerfDomain.new(
|
316
316
|
domain_id: 'atomic_test',
|
317
317
|
display_domain: 'atomic.test.com',
|
318
|
-
created_at:
|
318
|
+
created_at: Familia.now.to_i,
|
319
319
|
priority_score: 50
|
320
320
|
)
|
321
321
|
|
322
322
|
# Save should succeed and add to all collections
|
323
323
|
test_domain.save
|
324
324
|
# Manually add to collections
|
325
|
-
PerfDomain.all_domains.add(test_domain.
|
326
|
-
PerfDomain.priority_queue.add(test_domain.
|
325
|
+
PerfDomain.all_domains.add(test_domain.identifier, test_domain.created_at.to_f)
|
326
|
+
PerfDomain.priority_queue.add(test_domain.identifier, test_domain.priority_score)
|
327
327
|
PerfDomain.active_domains.add(test_domain.identifier)
|
328
328
|
PerfDomain.domain_history.push(test_domain.identifier)
|
329
329
|
|
@@ -337,14 +337,14 @@ all_collections_have_domain
|
|
337
337
|
#=> true
|
338
338
|
|
339
339
|
## Partial failures should not leave inconsistent state
|
340
|
-
# This would require more complex testing infrastructure to simulate Redis failures
|
340
|
+
# This would require more complex testing infrastructure to simulate Valkey/Redis failures
|
341
341
|
|
342
|
-
## Recovery from Redis connection issues
|
342
|
+
## Recovery from Valkey/Redis connection issues
|
343
343
|
begin
|
344
|
-
# Test graceful handling of Redis errors during relationship operations
|
344
|
+
# Test graceful handling of Valkey/Redis errors during relationship operations
|
345
345
|
dead_domain = PerfDomain.new(domain_id: 'dead_test')
|
346
346
|
|
347
|
-
# This test would require mocking Redis to fail, which is complex in this context
|
347
|
+
# This test would require mocking Valkey/Redis to fail, which is complex in this context
|
348
348
|
# For now, just verify that normal operations work
|
349
349
|
dead_domain.save
|
350
350
|
dead_domain.destroy!
|