familia 2.0.0.pre14 → 2.0.0.pre16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/code-quality.yml +138 -0
- data/.github/workflows/code-smellage.yml +145 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +1 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +48 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +66 -6
- data/CLAUDE.md +1 -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 +41 -41
- 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 +4 -4
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre13.md +1 -1
- 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 +623 -19
- 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 +6 -6
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +49 -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/commands.rb +53 -51
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +13 -10
- 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 +26 -15
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +75 -47
- 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/{autoloader.rb → features/autoloader.rb} +49 -23
- 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 +68 -66
- data/lib/familia/features/expiration/extensions.rb +61 -0
- data/lib/familia/features/expiration.rb +35 -87
- data/lib/familia/features/external_identifier.rb +11 -12
- data/lib/familia/features/object_identifier.rb +58 -20
- data/lib/familia/features/quantization.rb +17 -22
- 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 +301 -0
- data/lib/familia/features/relationships/indexing.rb +176 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +160 -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 +69 -271
- data/lib/familia/features/safe_dump.rb +127 -132
- 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 +5 -5
- data/lib/familia/features.rb +21 -21
- data/lib/familia/field_type.rb +24 -4
- data/lib/familia/horreum/core/connection.rb +229 -26
- data/lib/familia/horreum/core/database_commands.rb +27 -17
- data/lib/familia/horreum/core/serialization.rb +40 -20
- data/lib/familia/horreum/core/utils.rb +2 -1
- data/lib/familia/horreum/shared/settings.rb +2 -1
- data/lib/familia/horreum/subclass/definition.rb +33 -45
- data/lib/familia/horreum/subclass/management.rb +72 -24
- data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
- data/lib/familia/horreum.rb +196 -114
- 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 -15
- 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 +1 -1
- 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 +129 -11
- data/try/core/connection_try.rb +7 -7
- 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 +10 -10
- data/try/core/errors_try.rb +8 -11
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- 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 +1 -1
- 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/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/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 +433 -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 +1 -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 +3 -3
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +1 -1
- data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -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/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +1 -1
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +8 -10
- 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
- metadata +77 -45
- 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 -228
- 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/autoloadable.rb +0 -113
- 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/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/autoloadable/autoloadable_try.rb +0 -61
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
- 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
data/try/core/pools_try.rb
CHANGED
@@ -51,10 +51,28 @@ class PoolTestSession < Familia::Horreum
|
|
51
51
|
|
52
52
|
def init
|
53
53
|
@session_id ||= SecureRandom.hex(8)
|
54
|
-
@created_at ||=
|
54
|
+
@created_at ||= Familia.now.to_i
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
class PoolTestAccountDB1 < Familia::Horreum
|
59
|
+
logical_database 1
|
60
|
+
identifier_field :account_id
|
61
|
+
field :account_id
|
62
|
+
field :balance, on_conflict: :skip
|
63
|
+
field :holder_name
|
64
|
+
|
65
|
+
def init
|
66
|
+
@account_id ||= SecureRandom.hex(6)
|
67
|
+
@balance = @balance.to_f if @balance
|
68
|
+
end
|
69
|
+
|
70
|
+
def balance
|
71
|
+
@balance&.to_f
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
58
76
|
## Clean up before tests
|
59
77
|
PoolTestAccount.dbclient.flushdb
|
60
78
|
#=> "OK"
|
@@ -79,23 +97,6 @@ Familia.connection_provider.is_a?(Proc)
|
|
79
97
|
#=> true
|
80
98
|
|
81
99
|
## Test 5: Account in DB 1 via class configuration
|
82
|
-
class PoolTestAccountDB1 < Familia::Horreum
|
83
|
-
self.logical_database = 1
|
84
|
-
identifier_field :account_id
|
85
|
-
field :account_id
|
86
|
-
field :balance, on_conflict: :skip
|
87
|
-
field :holder_name
|
88
|
-
|
89
|
-
def init
|
90
|
-
@account_id ||= SecureRandom.hex(6)
|
91
|
-
@balance = @balance.to_f if @balance
|
92
|
-
end
|
93
|
-
|
94
|
-
def balance
|
95
|
-
@balance&.to_f
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
100
|
@account_db1 = PoolTestAccountDB1.new(balance: 750, holder_name: "Charlie")
|
100
101
|
@account_db1.save
|
101
102
|
#=> true
|
@@ -140,8 +141,8 @@ threads.each(&:join)
|
|
140
141
|
# Test that transaction connection is available
|
141
142
|
conn.ping
|
142
143
|
end
|
143
|
-
# Transaction returns
|
144
|
-
@transfer_result.first
|
144
|
+
# Transaction returns MultiResult with success status and results
|
145
|
+
@transfer_result.results.first
|
145
146
|
#=> "PONG"
|
146
147
|
|
147
148
|
## Test 12: Transaction block executes properly
|
@@ -149,19 +150,19 @@ end
|
|
149
150
|
[@account_a.balance, @account_b.balance]
|
150
151
|
#=> [1000.0, 500.0]
|
151
152
|
|
152
|
-
## Test 13:
|
153
|
-
@connection_test_result = Familia.
|
153
|
+
## Test 13: with_dbclient method
|
154
|
+
@connection_test_result = Familia.with_dbclient do |conn|
|
154
155
|
conn.set("test_key_#{SecureRandom.hex(4)}", "test_value")
|
155
156
|
end
|
156
157
|
@connection_test_result
|
157
158
|
#=> "OK"
|
158
159
|
|
159
160
|
## Test 14: Pipeline operations with connection pool
|
160
|
-
@pipeline_results = Familia.
|
161
|
+
@pipeline_results = Familia.pipelined do |conn|
|
161
162
|
conn.ping
|
162
163
|
end
|
163
164
|
# Pipeline executes successfully
|
164
|
-
@pipeline_results.first
|
165
|
+
@pipeline_results.results.first
|
165
166
|
#=> "PONG"
|
166
167
|
|
167
168
|
## Test 15: Multi/EXEC operations with connection pool
|
@@ -169,7 +170,7 @@ end
|
|
169
170
|
conn.ping
|
170
171
|
end
|
171
172
|
# Multi/EXEC executes successfully
|
172
|
-
@multi_results.first
|
173
|
+
@multi_results.results.first
|
173
174
|
#=> "PONG"
|
174
175
|
|
175
176
|
## Test 16: Error handling in transactions
|
@@ -208,7 +209,7 @@ timeout_mutex = Mutex.new
|
|
208
209
|
3.times do |i|
|
209
210
|
timeout_threads << Thread.new do
|
210
211
|
begin
|
211
|
-
result = Familia.
|
212
|
+
result = Familia.with_dbclient do |conn|
|
212
213
|
sleep(0.1) # Brief hold
|
213
214
|
conn.ping
|
214
215
|
end
|
@@ -270,4 +271,19 @@ Familia.connection_provider = original_provider
|
|
270
271
|
@captured_uris.any? { |uri| uri.include?('redis://') }
|
271
272
|
#=> true
|
272
273
|
|
274
|
+
## Check PoolTestAccountDB1 config name
|
275
|
+
PoolTestAccountDB1.config_name
|
276
|
+
#=> 'pool_test_account_db1'
|
277
|
+
|
278
|
+
|
273
279
|
puts "Connection pool tests completed successfully!"
|
280
|
+
|
281
|
+
# Teardown
|
282
|
+
Familia.connection_provider = nil
|
283
|
+
Fiber[:familia_connection] = nil
|
284
|
+
Fiber[:familia_connection_handler_class] = nil
|
285
|
+
Fiber[:familia_transaction] = nil
|
286
|
+
Fiber[:familia_pipeline] = nil
|
287
|
+
Fiber[:familia_key_cache] = nil
|
288
|
+
Fiber[:familia_request_cache] = nil
|
289
|
+
Fiber[:familia_request_cache_enabled] = nil
|
@@ -24,6 +24,20 @@ hex_id = Familia.generate_id(16)
|
|
24
24
|
[hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
|
25
25
|
#=> [String, true, true]
|
26
26
|
|
27
|
+
## Familia.generate_lite_id
|
28
|
+
Familia.respond_to?(:generate_lite_id)
|
29
|
+
#=> true
|
30
|
+
|
31
|
+
## Can generate a default base-36 lite ID
|
32
|
+
lite_id = Familia.generate_lite_id
|
33
|
+
[lite_id.class, lite_id.length > 10, lite_id.match?(/^[a-z0-9]+$/)]
|
34
|
+
#=> [String, true, true]
|
35
|
+
|
36
|
+
## Can generate a lite ID with a custom base (hex)
|
37
|
+
hex_lite_id = Familia.generate_lite_id(16)
|
38
|
+
[hex_lite_id.class, hex_lite_id.length == 32, hex_lite_id.match?(/^[a-f0-9]+$/)]
|
39
|
+
#=> [String, true, true]
|
40
|
+
|
27
41
|
## Familia.generate_trace_id
|
28
42
|
Familia.respond_to?(:generate_trace_id)
|
29
43
|
#=> true
|
@@ -35,39 +49,29 @@ trace_id = Familia.generate_trace_id
|
|
35
49
|
|
36
50
|
## Can generate a trace ID with a custom base (hex)
|
37
51
|
hex_trace_id = Familia.generate_trace_id(16)
|
38
|
-
[hex_trace_id.class, hex_trace_id.length == 16]
|
39
|
-
#=> [String, true]
|
40
|
-
|
41
|
-
## Familia.generate_hex_id
|
42
|
-
Familia.respond_to?(:generate_hex_id)
|
43
|
-
#=> true
|
44
|
-
|
45
|
-
## Can generate a 256-bit hex ID
|
46
|
-
hex_id = Familia.generate_hex_id
|
47
|
-
[hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
|
52
|
+
[hex_trace_id.class, hex_trace_id.length == 16, hex_trace_id.match?(/^[a-f0-9]+$/)]
|
48
53
|
#=> [String, true, true]
|
49
54
|
|
50
|
-
##
|
51
|
-
Familia.
|
52
|
-
#=>
|
55
|
+
## Generated lite IDs are unique
|
56
|
+
[Familia.generate_lite_id == Familia.generate_lite_id]
|
57
|
+
#=> [false]
|
53
58
|
|
54
|
-
##
|
55
|
-
|
56
|
-
|
57
|
-
#=> [String, true, true]
|
59
|
+
## Generated trace IDs are unique
|
60
|
+
[Familia.generate_trace_id == Familia.generate_trace_id]
|
61
|
+
#=> [false]
|
58
62
|
|
59
63
|
## Familia.shorten_to_trace_id
|
60
64
|
Familia.respond_to?(:shorten_to_trace_id)
|
61
65
|
#=> true
|
62
66
|
|
63
67
|
## Can shorten hex ID to trace ID (64 bits)
|
64
|
-
hex_id = Familia.
|
68
|
+
hex_id = Familia.generate_id(16)
|
65
69
|
trace_id = Familia.shorten_to_trace_id(hex_id)
|
66
70
|
[trace_id.class, trace_id.length < hex_id.length]
|
67
71
|
#=> [String, true]
|
68
72
|
|
69
73
|
## Can shorten hex ID to trace ID with custom base (hex)
|
70
|
-
hex_id = Familia.
|
74
|
+
hex_id = Familia.generate_id(16)
|
71
75
|
hex_trace_id = Familia.shorten_to_trace_id(hex_id, base: 16)
|
72
76
|
[hex_trace_id.class, hex_trace_id.length == 16]
|
73
77
|
#=> [String, true]
|
@@ -77,25 +81,25 @@ Familia.respond_to?(:truncate_hex)
|
|
77
81
|
#=> true
|
78
82
|
|
79
83
|
## Can truncate hex ID to 128 bits by default
|
80
|
-
hex_id = Familia.
|
84
|
+
hex_id = Familia.generate_id(16)
|
81
85
|
truncated_id = Familia.truncate_hex(hex_id)
|
82
86
|
[truncated_id.class, truncated_id.length < hex_id.length]
|
83
87
|
#=> [String, true]
|
84
88
|
|
85
89
|
## Can truncate hex ID to a custom bit length (64 bits)
|
86
|
-
hex_id = Familia.
|
90
|
+
hex_id = Familia.generate_id(16)
|
87
91
|
truncated_64 = Familia.truncate_hex(hex_id, bits: 64)
|
88
92
|
[truncated_64.class, truncated_64.length < hex_id.length]
|
89
93
|
#=> [String, true]
|
90
94
|
|
91
95
|
## Can truncate with a custom base (hex)
|
92
|
-
hex_id = Familia.
|
96
|
+
hex_id = Familia.generate_id(16)
|
93
97
|
hex_truncated = Familia.truncate_hex(hex_id, bits: 128, base: 16)
|
94
98
|
[hex_truncated.class, hex_truncated.length == 32]
|
95
99
|
#=> [String, true]
|
96
100
|
|
97
101
|
## Truncated IDs are deterministic
|
98
|
-
hex_id = Familia.
|
102
|
+
hex_id = Familia.generate_id(16)
|
99
103
|
id1 = Familia.truncate_hex(hex_id)
|
100
104
|
id2 = Familia.truncate_hex(hex_id)
|
101
105
|
id1 == id2
|
@@ -118,7 +122,7 @@ end
|
|
118
122
|
#=> "Input bits (12) cannot be less than desired output bits (64)."
|
119
123
|
|
120
124
|
## Shortened IDs are deterministic
|
121
|
-
hex_id = Familia.
|
125
|
+
hex_id = Familia.generate_id(16)
|
122
126
|
id1 = Familia.shorten_to_trace_id(hex_id)
|
123
127
|
id2 = Familia.shorten_to_trace_id(hex_id)
|
124
128
|
id1 == id2
|
data/try/core/time_utils_try.rb
CHANGED
@@ -52,51 +52,51 @@ result.round(0)
|
|
52
52
|
#=> 31556952.0
|
53
53
|
|
54
54
|
## Numeric#age_in - calculate age in months from timestamp (approximately 1 month ago)
|
55
|
-
timestamp =
|
55
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
|
56
56
|
result = RefinedContext.eval_in_refined_context("#{timestamp}.age_in(:months)")
|
57
57
|
(result - 1.0).abs < 0.01
|
58
58
|
#=> true
|
59
59
|
|
60
60
|
## Numeric#age_in - calculate age in years from timestamp (approximately 1 year ago)
|
61
|
-
timestamp =
|
61
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
|
62
62
|
result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.age_in(:years)")
|
63
63
|
(result - 1.0).abs < 0.01
|
64
64
|
#=> true
|
65
65
|
|
66
66
|
## Numeric#months_old - convenience method for age_in(:months)
|
67
|
-
timestamp =
|
67
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
|
68
68
|
result = RefinedContext.eval_in_refined_context("#{timestamp}.months_old")
|
69
69
|
(result - 1.0).abs < 0.01
|
70
70
|
#=> true
|
71
71
|
|
72
72
|
## Numeric#years_old - convenience method for age_in(:years)
|
73
|
-
timestamp =
|
73
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
|
74
74
|
result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.years_old")
|
75
75
|
(result - 1.0).abs < 0.01
|
76
76
|
#=> true
|
77
77
|
|
78
78
|
## Numeric#months_old - should NOT return seconds (the original bug)
|
79
|
-
timestamp =
|
79
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH
|
80
80
|
result = RefinedContext.eval_in_refined_context("#{timestamp}.months_old")
|
81
81
|
result.between?(0.9, 1.1) # Should be ~1 month, not millions of seconds
|
82
82
|
#=> true
|
83
83
|
|
84
84
|
## Numeric#years_old - should NOT return seconds (the original bug)
|
85
|
-
timestamp =
|
85
|
+
timestamp = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR
|
86
86
|
result = RefinedContext.instance_eval_in_refined_context("#{timestamp}.years_old")
|
87
87
|
result.between?(0.9, 1.1) # Should be ~1 year, not millions of seconds
|
88
88
|
#=> true
|
89
89
|
|
90
90
|
## age_in with from_time parameter - months
|
91
|
-
past_time =
|
92
|
-
from_time =
|
91
|
+
past_time = Familia.now - (2 * Familia::Refinements::TimeLiterals::PER_MONTH) # 2 months ago
|
92
|
+
from_time = Familia.now - Familia::Refinements::TimeLiterals::PER_MONTH # 1 month ago
|
93
93
|
result = RefinedContext.eval_in_refined_context("#{past_time.to_f}.age_in(:months, #{from_time.to_f})")
|
94
94
|
(result - 1.0).abs < 0.01
|
95
95
|
#=> true
|
96
96
|
|
97
97
|
## age_in with from_time parameter - years
|
98
|
-
past_time =
|
99
|
-
from_time =
|
98
|
+
past_time = Familia.now - (2 * Familia::Refinements::TimeLiterals::PER_YEAR) # 2 years ago
|
99
|
+
from_time = Familia.now - Familia::Refinements::TimeLiterals::PER_YEAR # 1 year ago
|
100
100
|
result = RefinedContext.instance_eval_in_refined_context("#{past_time.to_f}.age_in(:years, #{from_time.to_f})")
|
101
101
|
(result - 1.0).abs < 0.01
|
102
102
|
#=> true
|
data/try/core/tools_try.rb
CHANGED
data/try/core/utils_try.rb
CHANGED
@@ -72,10 +72,10 @@ sym_result = Familia.distinguisher(:symbol)
|
|
72
72
|
## distinguisher raises error for high-risk types with strict mode
|
73
73
|
begin
|
74
74
|
Familia.distinguisher(true, strict_values: true)
|
75
|
-
rescue Familia::
|
75
|
+
rescue Familia::NotDistinguishableError => e
|
76
76
|
e.class
|
77
77
|
end
|
78
|
-
#=> Familia::
|
78
|
+
#=> Familia::NotDistinguishableError
|
79
79
|
|
80
80
|
## distinguisher allows high-risk types with non-strict mode
|
81
81
|
result = Familia.distinguisher(false, strict_values: false)
|
@@ -17,10 +17,10 @@ Familia.debug = false
|
|
17
17
|
## Trying to store a boolean value to a hash key raises an exception
|
18
18
|
begin
|
19
19
|
@hashkey['test'] = true
|
20
|
-
rescue Familia::
|
20
|
+
rescue Familia::NotDistinguishableError => e
|
21
21
|
e.message
|
22
22
|
end
|
23
|
-
#=> "
|
23
|
+
#=> "Cannot represent true<TrueClass> as a string"
|
24
24
|
|
25
25
|
## Boolean values are returned as strings
|
26
26
|
@hashkey['test']
|
@@ -29,10 +29,10 @@ end
|
|
29
29
|
## Trying to store a nil value to a hash key raises an exception
|
30
30
|
begin
|
31
31
|
@hashkey['test'] = nil
|
32
|
-
rescue Familia::
|
32
|
+
rescue Familia::NotDistinguishableError => e
|
33
33
|
e.message
|
34
34
|
end
|
35
|
-
#=> "
|
35
|
+
#=> "Cannot represent <NilClass> as a string"
|
36
36
|
|
37
37
|
## The exceptions prevented the hash from being updated
|
38
38
|
@hashkey['test']
|
@@ -30,8 +30,6 @@ RefinedContext.eval_in_refined_context("@limiter1.counter.qstamp(10.minutes, '%H
|
|
30
30
|
|
31
31
|
## Limiter#qstamp as a number
|
32
32
|
@limiter2 = Limiter.new :requests
|
33
|
-
p [@limiter1.default_expiration, @limiter2.default_expiration]
|
34
|
-
p [@limiter1.counter.parent.default_expiration, @limiter2.counter.parent.default_expiration]
|
35
33
|
RefinedContext.instance_variable_set(:@limiter2, @limiter2)
|
36
34
|
RefinedContext.eval_in_refined_context("@limiter2.counter.qstamp(10.minutes, pattern: nil, time: 1_302_468_980)")
|
37
35
|
#=> 1302468600
|
data/try/data_types/list_try.rb
CHANGED
@@ -4,37 +4,37 @@ require_relative '../helpers/test_helpers'
|
|
4
4
|
|
5
5
|
@a = Bone.new 'atoken'
|
6
6
|
|
7
|
-
## Familia::
|
7
|
+
## Familia::ListKey#push
|
8
8
|
ret = @a.owners.push :value1
|
9
9
|
ret.class
|
10
|
-
#=> Familia::
|
10
|
+
#=> Familia::ListKey
|
11
11
|
|
12
|
-
## Familia::
|
12
|
+
## Familia::ListKey#<<
|
13
13
|
ret = @a.owners << :value2 << :value3 << :value4
|
14
14
|
ret.class
|
15
|
-
#=> Familia::
|
15
|
+
#=> Familia::ListKey
|
16
16
|
|
17
|
-
## Familia::
|
17
|
+
## Familia::ListKey#pop
|
18
18
|
@a.owners.pop
|
19
19
|
#=> 'value4'
|
20
20
|
|
21
|
-
## Familia::
|
21
|
+
## Familia::ListKey#first
|
22
22
|
@a.owners.first
|
23
23
|
#=> 'value1'
|
24
24
|
|
25
|
-
## Familia::
|
25
|
+
## Familia::ListKey#last
|
26
26
|
@a.owners.last
|
27
27
|
#=> 'value3'
|
28
28
|
|
29
|
-
## Familia::
|
29
|
+
## Familia::ListKey#to_a
|
30
30
|
@a.owners.to_a
|
31
31
|
#=> ['value1','value2','value3']
|
32
32
|
|
33
|
-
## Familia::
|
33
|
+
## Familia::ListKey#delete
|
34
34
|
@a.owners.remove 'value3'
|
35
35
|
#=> 1
|
36
36
|
|
37
|
-
## Familia::
|
37
|
+
## Familia::ListKey#size
|
38
38
|
@a.owners.size
|
39
39
|
#=> 2
|
40
40
|
|
@@ -6,11 +6,11 @@ require_relative '../helpers/test_helpers'
|
|
6
6
|
|
7
7
|
## Familia::SortedSet#add
|
8
8
|
@a = Bone.new 'atoken'
|
9
|
-
@a.metrics.add
|
10
|
-
@a.metrics.add
|
11
|
-
@a.metrics.add
|
12
|
-
@a.metrics.add
|
13
|
-
@a.metrics.add
|
9
|
+
@a.metrics.add :metric2, 2
|
10
|
+
@a.metrics.add :metric4, 4
|
11
|
+
@a.metrics.add :metric0, 0
|
12
|
+
@a.metrics.add :metric1, 1
|
13
|
+
@a.metrics.add :metric3, 3
|
14
14
|
#=> true
|
15
15
|
|
16
16
|
## Familia::SortedSet#members
|
@@ -8,24 +8,24 @@ require_relative '../helpers/test_helpers'
|
|
8
8
|
@a.dbkey
|
9
9
|
#=> 'bone:atoken2:object'
|
10
10
|
|
11
|
-
## Familia::
|
11
|
+
## Familia::StringKey#value should give default value
|
12
12
|
@a.value.value
|
13
13
|
#=> 'GREAT!'
|
14
14
|
|
15
|
-
## Familia::
|
15
|
+
## Familia::StringKey#value=
|
16
16
|
@a.value.value = 'DECENT!'
|
17
17
|
#=> 'DECENT!'
|
18
18
|
|
19
|
-
## Familia::
|
19
|
+
## Familia::StringKey#to_s
|
20
20
|
@a.value.to_s
|
21
21
|
#=> 'DECENT!'
|
22
22
|
|
23
|
-
## Familia::
|
23
|
+
## Familia::StringKey#destroy!
|
24
24
|
@a.value.delete!
|
25
25
|
#=> true
|
26
26
|
|
27
|
-
## Familia::
|
28
|
-
@ret = Familia::
|
27
|
+
## Familia::StringKey.new
|
28
|
+
@ret = Familia::StringKey.new 'arbitrary:key'
|
29
29
|
@ret.dbkey
|
30
30
|
#=> 'arbitrary:key'
|
31
31
|
|
@@ -37,27 +37,27 @@ require_relative '../helpers/test_helpers'
|
|
37
37
|
@ret.value
|
38
38
|
#=> '1000'
|
39
39
|
|
40
|
-
## Familia::
|
40
|
+
## Familia::StringKey#increment
|
41
41
|
@ret.increment
|
42
42
|
#=> 1001
|
43
43
|
|
44
|
-
## Familia::
|
44
|
+
## Familia::StringKey#incrementby
|
45
45
|
@ret.incrementby 99
|
46
46
|
#=> 1100
|
47
47
|
|
48
|
-
## Familia::
|
48
|
+
## Familia::StringKey#decrement
|
49
49
|
@ret.decrement
|
50
50
|
#=> 1099
|
51
51
|
|
52
|
-
## Familia::
|
52
|
+
## Familia::StringKey#decrementby
|
53
53
|
@ret.decrementby 49
|
54
54
|
#=> 1050
|
55
55
|
|
56
|
-
## Familia::
|
56
|
+
## Familia::StringKey#append
|
57
57
|
@ret.append 'bytes'
|
58
58
|
#=> 9
|
59
59
|
|
60
|
-
## Familia::
|
60
|
+
## Familia::StringKey#value after append
|
61
61
|
@ret.value
|
62
62
|
#=> '1050bytes'
|
63
63
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# try/data_types/unsortedset_try.rb
|
2
|
+
|
3
|
+
require_relative '../helpers/test_helpers'
|
4
|
+
|
5
|
+
@a = Bone.new 'atoken'
|
6
|
+
|
7
|
+
## Familia::UnsortedSet#add
|
8
|
+
ret = @a.tags.add :a
|
9
|
+
ret.class
|
10
|
+
#=> Familia::UnsortedSet
|
11
|
+
|
12
|
+
## Familia::UnsortedSet#<<
|
13
|
+
ret = @a.tags << :a << :b << :c
|
14
|
+
ret.class
|
15
|
+
#=> Familia::UnsortedSet
|
16
|
+
|
17
|
+
## Familia::UnsortedSet#members
|
18
|
+
@a.tags.members.sort
|
19
|
+
#=> ['a', 'b', 'c']
|
20
|
+
|
21
|
+
## Familia::UnsortedSet#member? knows when a value exists
|
22
|
+
@a.tags.member? :a
|
23
|
+
#=> true
|
24
|
+
|
25
|
+
## Familia::UnsortedSet#member? knows when a value doesn't exist
|
26
|
+
@a.tags.member? :x
|
27
|
+
#=> false
|
28
|
+
|
29
|
+
## Familia::UnsortedSet#member? knows when a value doesn't exist
|
30
|
+
@a.tags.size
|
31
|
+
#=> 3
|
32
|
+
|
33
|
+
@a.tags.delete!
|
@@ -14,9 +14,9 @@ Familia.config.encryption_keys = test_keys
|
|
14
14
|
Familia.config.current_key_version = :v1
|
15
15
|
|
16
16
|
# Clear any existing cache
|
17
|
-
|
17
|
+
Fiber[:familia_key_cache] = nil
|
18
18
|
puts "1. Initial Cache State:"
|
19
|
-
puts " Cache: #{
|
19
|
+
puts " Cache: #{Fiber[:familia_key_cache].inspect}"
|
20
20
|
puts
|
21
21
|
|
22
22
|
# Define test model with multiple encrypted fields
|
@@ -32,14 +32,14 @@ end
|
|
32
32
|
|
33
33
|
puts "2. Model Creation:"
|
34
34
|
user = CacheTraceModel.new(user_id: 'cache-user-1')
|
35
|
-
puts " After model creation: #{
|
35
|
+
puts " After model creation: #{Fiber[:familia_key_cache].inspect}"
|
36
36
|
puts
|
37
37
|
|
38
38
|
puts "3. Setting Encrypted Fields:"
|
39
39
|
['field_a', 'field_b', 'field_c'].each_with_index do |field, i|
|
40
40
|
puts " Setting #{field}..."
|
41
41
|
user.public_send("#{field}=", "test-value-#{i+1}")
|
42
|
-
cache =
|
42
|
+
cache = Fiber[:familia_key_cache]
|
43
43
|
if cache
|
44
44
|
puts " Cache size: #{cache.size}"
|
45
45
|
puts " Cache keys: #{cache.keys.inspect}"
|
@@ -54,7 +54,7 @@ puts "4. Reading Encrypted Fields:"
|
|
54
54
|
puts " Reading #{field}..."
|
55
55
|
value = user.public_send(field)
|
56
56
|
puts " Retrieved: #{value}"
|
57
|
-
cache =
|
57
|
+
cache = Fiber[:familia_key_cache]
|
58
58
|
if cache
|
59
59
|
puts " Cache size: #{cache.size}"
|
60
60
|
puts " Cache keys: #{cache.keys.inspect}"
|
@@ -65,7 +65,7 @@ end
|
|
65
65
|
puts
|
66
66
|
|
67
67
|
puts "5. Final Cache Analysis:"
|
68
|
-
cache =
|
68
|
+
cache = Fiber[:familia_key_cache]
|
69
69
|
if cache && !cache.empty?
|
70
70
|
puts " Cache size: #{cache.size} entries"
|
71
71
|
cache.each_with_index do |(key, value), i|
|
@@ -81,7 +81,7 @@ puts "6. Multiple Models Cache Test:"
|
|
81
81
|
user2 = CacheTraceModel.new(user_id: 'cache-user-2')
|
82
82
|
user2.field_a = 'different-value'
|
83
83
|
puts " After second model field set:"
|
84
|
-
cache =
|
84
|
+
cache = Fiber[:familia_key_cache]
|
85
85
|
if cache
|
86
86
|
puts " Cache size: #{cache.size}"
|
87
87
|
puts " Unique contexts: #{cache.keys.map { |k| k.split(':').last }.uniq.size}"
|
@@ -3,7 +3,7 @@
|
|
3
3
|
$LOAD_PATH.unshift(File.expand_path('lib', __dir__))
|
4
4
|
ENV['TEST'] = 'true' # Mark as test environment
|
5
5
|
require 'familia'
|
6
|
-
require_relative '
|
6
|
+
require_relative '../helpers/test_helpers'
|
7
7
|
|
8
8
|
puts "Testing ConcealedString internal call chain..."
|
9
9
|
|
@@ -44,7 +44,7 @@ puts "cipher_b: #{cipher_b.class} - #{cipher_b.encrypted_value}"
|
|
44
44
|
# Now try cross-context access
|
45
45
|
puts "\n=== Cross-context test ==="
|
46
46
|
model_a.instance_variable_set(:@api_key, cipher_b)
|
47
|
-
puts "
|
47
|
+
puts "UnsortedSet cipher_b into model_a"
|
48
48
|
|
49
49
|
begin
|
50
50
|
result = model_a.api_key
|
@@ -45,7 +45,7 @@ puts "cipher_b encrypted: #{cipher_b.encrypted_value}"
|
|
45
45
|
# Now try cross-context access
|
46
46
|
puts "\n=== Cross-context test ==="
|
47
47
|
model_a.instance_variable_set(:@api_key, cipher_b)
|
48
|
-
puts "
|
48
|
+
puts "UnsortedSet cipher_b into model_a"
|
49
49
|
|
50
50
|
begin
|
51
51
|
result = model_a.api_key
|