familia 2.0.0.pre2 → 2.0.0.pre4
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/CLAUDE.md +12 -5
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -9
- data/lib/familia/core_ext.rb +2 -2
- data/lib/familia/features/expiration.rb +0 -1
- data/lib/familia/features/relatable_objects.rb +127 -0
- data/lib/familia/features.rb +7 -3
- data/lib/familia/horreum/class_methods.rb +32 -4
- data/lib/familia/secure_identifier.rb +129 -0
- data/lib/familia/utils.rb +7 -96
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +2 -0
- data/try/configuration/scenarios_try.rb +43 -31
- data/try/core/errors_try.rb +10 -10
- data/try/core/extensions_try.rb +56 -23
- data/try/core/familia_extended_try.rb +3 -3
- data/try/core/familia_try.rb +0 -4
- data/try/core/middleware_try.rb +34 -40
- data/try/core/secure_identifier_try.rb +104 -0
- data/try/core/tools_try.rb +52 -36
- data/try/core/utils_try.rb +0 -98
- data/try/datatypes/boolean_try.rb +6 -7
- data/try/datatypes/datatype_base_try.rb +2 -2
- data/try/datatypes/hash_try.rb +0 -1
- data/try/datatypes/list_try.rb +0 -1
- data/try/datatypes/set_try.rb +0 -2
- data/try/datatypes/sorted_set_try.rb +1 -2
- data/try/datatypes/string_try.rb +1 -2
- data/try/edge_cases/empty_identifiers_try.rb +42 -35
- data/try/edge_cases/hash_symbolization_try.rb +5 -5
- data/try/edge_cases/json_serialization_try.rb +12 -13
- data/try/edge_cases/race_conditions_try.rb +46 -49
- data/try/edge_cases/reserved_keywords_try.rb +103 -49
- data/try/edge_cases/string_coercion_try.rb +2 -2
- data/try/edge_cases/ttl_side_effects_try.rb +44 -25
- data/try/features/expiration_try.rb +2 -2
- data/try/features/quantization_try.rb +2 -2
- data/try/features/relatable_objects_try.rb +221 -0
- data/try/features/safe_dump_advanced_try.rb +13 -14
- data/try/features/safe_dump_try.rb +8 -8
- data/try/helpers/test_helpers.rb +10 -12
- data/try/horreum/base_try.rb +9 -9
- data/try/horreum/class_methods_try.rb +27 -30
- data/try/horreum/commands_try.rb +69 -33
- data/try/horreum/initialization_try.rb +4 -4
- data/try/horreum/relations_try.rb +13 -14
- data/try/horreum/serialization_try.rb +3 -3
- data/try/horreum/settings_try.rb +25 -31
- data/try/integration/cross_component_try.rb +45 -35
- data/try/models/customer_safe_dump_try.rb +4 -4
- data/try/models/customer_try.rb +21 -24
- data/try/models/datatype_base_try.rb +0 -1
- data/try/models/familia_object_try.rb +3 -4
- data/try/performance/benchmarks_try.rb +47 -38
- data/try/prototypes/atomic_saves_v4.rb +3 -3
- metadata +15 -12
- data/try/core/refinements_try.rb +0 -39
- /data/try/{pooling/connection_pool_test_try.rb → core/pools_try.rb} +0 -0
- /data/try/{pooling → prototypes/pooling}/README.md +0 -0
- /data/try/{pooling/configurable_stress_test_try.rb → prototypes/pooling/configurable_stress_test.rb} +0 -0
- /data/try/{pooling → prototypes/pooling}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{pooling → prototypes/pooling}/lib/connection_pool_metrics.rb +0 -0
- /data/try/{pooling → prototypes/pooling}/lib/connection_pool_stress_test.rb +0 -0
- /data/try/{pooling → prototypes/pooling}/lib/connection_pool_threading_models.rb +0 -0
- /data/try/{pooling → prototypes/pooling}/lib/visualize_stress_results.rb +0 -0
- /data/try/{pooling/pool_siege_try.rb → prototypes/pooling/pool_siege.rb} +0 -0
- /data/try/{pooling/run_stress_tests_try.rb → prototypes/pooling/run_stress_tests.rb} +0 -0
data/try/helpers/test_helpers.rb
CHANGED
@@ -18,7 +18,7 @@ class Bone < Familia::Horreum
|
|
18
18
|
set :tags
|
19
19
|
zset :metrics
|
20
20
|
hashkey :props
|
21
|
-
string :value, :
|
21
|
+
string :value, default: 'GREAT!'
|
22
22
|
end
|
23
23
|
|
24
24
|
class Blone < Familia::Horreum
|
@@ -27,7 +27,7 @@ class Blone < Familia::Horreum
|
|
27
27
|
set :tags
|
28
28
|
zset :metrics
|
29
29
|
hashkey :props
|
30
|
-
string :value, :
|
30
|
+
string :value, default: 'GREAT!'
|
31
31
|
end
|
32
32
|
|
33
33
|
class Customer < Familia::Horreum
|
@@ -35,8 +35,8 @@ class Customer < Familia::Horreum
|
|
35
35
|
default_expiration 5.years
|
36
36
|
|
37
37
|
feature :safe_dump
|
38
|
-
#feature :expiration
|
39
|
-
#feature :api_version
|
38
|
+
# feature :expiration
|
39
|
+
# feature :api_version
|
40
40
|
|
41
41
|
# NOTE: The SafeDump mixin caches the safe_dump_field_map so updating this list
|
42
42
|
# with hot reloading in dev mode will not work. You will need to restart the
|
@@ -51,10 +51,10 @@ class Customer < Familia::Horreum
|
|
51
51
|
# NOTE: The secrets_created incrementer is null until the first secret
|
52
52
|
# is created. See CreateSecret for where the incrementer is called.
|
53
53
|
#
|
54
|
-
{secrets_created: ->(cust) { cust.secrets_created.value || 0 } },
|
54
|
+
{ secrets_created: ->(cust) { cust.secrets_created.value || 0 } },
|
55
55
|
|
56
56
|
# We use the hash syntax here since `:active?` is not a valid symbol.
|
57
|
-
{active: ->(cust) { cust.active? } }
|
57
|
+
{ active: ->(cust) { cust.active? } }
|
58
58
|
]
|
59
59
|
|
60
60
|
class_sorted_set :values, key: 'onetime:customer'
|
@@ -95,7 +95,7 @@ class Customer < Familia::Horreum
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
@c = Customer.new
|
98
|
-
@c.custid =
|
98
|
+
@c.custid = 'd@example.com'
|
99
99
|
|
100
100
|
class Session < Familia::Horreum
|
101
101
|
logical_database 14 # don't use Onetime's default DB
|
@@ -113,14 +113,13 @@ class Session < Familia::Horreum
|
|
113
113
|
field :updated
|
114
114
|
|
115
115
|
def save
|
116
|
-
self.sessid ||= Familia.generate_id
|
116
|
+
self.sessid ||= Familia.generate_id # Only generates when persisting
|
117
117
|
super
|
118
118
|
end
|
119
119
|
end
|
120
120
|
@s = Session.new
|
121
121
|
|
122
122
|
class CustomDomain < Familia::Horreum
|
123
|
-
|
124
123
|
feature :expiration
|
125
124
|
|
126
125
|
class_sorted_set :values
|
@@ -146,11 +145,10 @@ class CustomDomain < Familia::Horreum
|
|
146
145
|
end
|
147
146
|
|
148
147
|
@d = CustomDomain.new
|
149
|
-
@d.display_domain =
|
148
|
+
@d.display_domain = 'example.com'
|
150
149
|
@d.custid = @c.custid
|
151
150
|
|
152
151
|
class Limiter < Familia::Horreum
|
153
|
-
|
154
152
|
feature :expiration
|
155
153
|
feature :quantization
|
156
154
|
|
@@ -158,7 +156,7 @@ class Limiter < Familia::Horreum
|
|
158
156
|
default_expiration 30.minutes
|
159
157
|
field :name
|
160
158
|
|
161
|
-
string :counter, :
|
159
|
+
string :counter, default_expiration: 1.hour, quantize: [10.minutes, '%H:%M', 1_302_468_980]
|
162
160
|
|
163
161
|
def identifier
|
164
162
|
@name
|
data/try/horreum/base_try.rb
CHANGED
@@ -13,31 +13,31 @@ Familia.debug = false
|
|
13
13
|
## TODO: Revisit these @identifier testcases b/c I think we don't want to be setting
|
14
14
|
## the @identifier instance var anymore since identifer_field should only take field
|
15
15
|
## names now (and might be removed altogether).
|
16
|
-
@hashkey[
|
16
|
+
@hashkey['test1'] = @customer.identifier
|
17
17
|
#=> @identifier
|
18
18
|
|
19
19
|
## Customer passed as value is returned as string identifier
|
20
|
-
@hashkey[
|
20
|
+
@hashkey['test1']
|
21
21
|
#=> @identifier
|
22
22
|
|
23
23
|
## Trying to store a customer to a hash key implicitly converts it to string identifier
|
24
24
|
## we can't tell from the return value this way either. Here the store method return value
|
25
25
|
## is either 1 (if the key is new) or 0 (if the key already exists).
|
26
|
-
@hashkey.store
|
26
|
+
@hashkey.store 'test2', @customer
|
27
27
|
#=> 1
|
28
28
|
|
29
29
|
## Trying to store a customer to a hash key implicitly converts it to string identifier
|
30
30
|
## but we can't tell that from the return value. Here the hash syntax return value
|
31
31
|
## is the value that is being assigned.
|
32
|
-
@hashkey[
|
32
|
+
@hashkey['test2'] = @customer
|
33
33
|
#=> @customer
|
34
34
|
|
35
35
|
## Trying store again with the same key returns 0
|
36
|
-
@hashkey.store
|
36
|
+
@hashkey.store 'test2', @customer
|
37
37
|
#=> 0
|
38
38
|
|
39
39
|
## Customer passed as value is returned as string identifier
|
40
|
-
@hashkey[
|
40
|
+
@hashkey['test2']
|
41
41
|
#=> @identifier
|
42
42
|
|
43
43
|
## Remove the key
|
@@ -80,12 +80,12 @@ Familia.trace :LOAD, @customer.dbclient, @customer.uri, caller if Familia.debug?
|
|
80
80
|
class NoIdentifierClass < Familia::Horreum
|
81
81
|
field :name
|
82
82
|
end
|
83
|
-
@no_id = NoIdentifierClass.new name:
|
83
|
+
@no_id = NoIdentifierClass.new name: 'test'
|
84
84
|
@no_id.identifier
|
85
85
|
#=> nil
|
86
86
|
|
87
87
|
## We can call #identifier directly if we want to "lasy load" the unique identifier
|
88
|
-
@cd = CustomDomain.new display_domain:
|
88
|
+
@cd = CustomDomain.new display_domain: 'www.example.com', custid: 'domain-test@example.com'
|
89
89
|
@cd.identifier
|
90
90
|
#=:> String
|
91
91
|
#=/=> _.empty?
|
@@ -111,7 +111,7 @@ end
|
|
111
111
|
|
112
112
|
## Array-based identifiers are no longer supported and raise clear errors at class definition time
|
113
113
|
class ArrayIdentifierTest < Familia::Horreum
|
114
|
-
identifier_field [
|
114
|
+
identifier_field %i[token name] # This should raise an error immediately
|
115
115
|
field :token
|
116
116
|
field :name
|
117
117
|
end
|
@@ -1,41 +1,38 @@
|
|
1
|
-
|
1
|
+
# try/horreum/class_methods_try.rb
|
2
2
|
|
3
3
|
# Test Horreum class methods
|
4
|
-
group "Horreum Class Methods"
|
5
4
|
|
6
|
-
|
7
|
-
@user_class = Class.new(Familia::Horreum) do
|
8
|
-
identifier :email
|
9
|
-
field :name
|
10
|
-
field :age
|
11
|
-
end
|
12
|
-
end
|
5
|
+
require_relative '../helpers/test_helpers'
|
13
6
|
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
TestUser = Class.new(Familia::Horreum) do
|
8
|
+
identifier_field :email
|
9
|
+
field :email
|
10
|
+
field :name
|
11
|
+
field :age
|
12
|
+
end
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
module AnotherModuleName
|
15
|
+
AnotherTestUser = Class.new(Familia::Horreum) do
|
16
|
+
end
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
## create factory method with existence checking
|
20
|
+
TestUser
|
21
|
+
#==> _.respond_to?(:create)
|
22
|
+
#==> _.respond_to?(:exists?)
|
26
23
|
|
27
|
-
|
24
|
+
## multiget method is available
|
25
|
+
TestUser
|
26
|
+
#==> _.respond_to?(:multiget)
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
28
|
+
## find_keys method is available
|
29
|
+
TestUser
|
30
|
+
#==> _.respond_to?(:find_keys)
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
## config name turns a top-level class into a symbol
|
33
|
+
TestUser.config_name.to_sym
|
34
|
+
#=> :test_user
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
36
|
+
## config name turns the fully qualified class into a symbol, but just the right most class
|
37
|
+
AnotherModuleName::AnotherTestUser.config_name.to_sym
|
38
|
+
#=> :another_test_user
|
data/try/horreum/commands_try.rb
CHANGED
@@ -1,49 +1,85 @@
|
|
1
|
-
require_relative '../helpers/test_helpers'
|
2
|
-
|
3
1
|
# Test Horreum Redis commands
|
4
|
-
group "Horreum Commands"
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require_relative '../helpers/test_helpers'
|
4
|
+
|
5
|
+
## hget/hset operations
|
6
|
+
begin
|
7
|
+
user_class = Class.new(Familia::Horreum) do
|
8
|
+
identifier_field :email
|
9
|
+
field :email
|
9
10
|
field :name
|
10
11
|
field :score
|
11
12
|
end
|
12
|
-
@user = @user_class.new(email: "test@example.com", name: "Test")
|
13
|
-
@user.save
|
14
|
-
end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
@user.hget("name") == "Updated"
|
19
|
-
end
|
14
|
+
user = user_class.new(email: "test@example.com", name: "Test")
|
15
|
+
user.save
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
result = user.respond_to?(:hset) && user.respond_to?(:hget)
|
18
|
+
user.delete!
|
19
|
+
result
|
20
|
+
rescue StandardError => e
|
21
|
+
user&.delete! rescue nil
|
22
|
+
false
|
26
23
|
end
|
24
|
+
#=> false
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
## increment/decrement operations not available
|
27
|
+
begin
|
28
|
+
user_class = Class.new(Familia::Horreum) do
|
29
|
+
identifier_field :email
|
30
|
+
field :email
|
31
|
+
field :name
|
32
|
+
field :score
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
+
user = user_class.new(email: "test@example.com", name: "Test")
|
36
|
+
user.save
|
37
|
+
|
38
|
+
result = user.respond_to?(:incr) && user.respond_to?(:decr)
|
39
|
+
user.delete!
|
40
|
+
result
|
41
|
+
rescue StandardError => e
|
42
|
+
user&.delete! rescue nil
|
43
|
+
false
|
35
44
|
end
|
45
|
+
#=> false
|
46
|
+
|
47
|
+
## field existence and key operations not available
|
48
|
+
begin
|
49
|
+
user_class = Class.new(Familia::Horreum) do
|
50
|
+
identifier_field :email
|
51
|
+
field :email
|
52
|
+
field :name
|
53
|
+
end
|
36
54
|
|
37
|
-
|
38
|
-
|
39
|
-
values = @user.hvals
|
40
|
-
all_data = @user.hgetall
|
55
|
+
user = user_class.new(email: "test@example.com", name: "Test")
|
56
|
+
user.save
|
41
57
|
|
42
|
-
|
43
|
-
|
44
|
-
|
58
|
+
result = user.respond_to?(:key?)
|
59
|
+
user.delete!
|
60
|
+
result
|
61
|
+
rescue StandardError => e
|
62
|
+
user&.delete! rescue nil
|
63
|
+
false
|
45
64
|
end
|
65
|
+
#=> false
|
66
|
+
|
67
|
+
## bulk field operations availability
|
68
|
+
begin
|
69
|
+
user_class = Class.new(Familia::Horreum) do
|
70
|
+
identifier_field :email
|
71
|
+
field :email
|
72
|
+
field :name
|
73
|
+
end
|
74
|
+
|
75
|
+
user = user_class.new(email: "test@example.com", name: "Test")
|
76
|
+
user.save
|
46
77
|
|
47
|
-
|
48
|
-
|
78
|
+
result = user.respond_to?(:hkeys) && user.respond_to?(:hvals) && user.respond_to?(:hgetall)
|
79
|
+
user.delete!
|
80
|
+
result
|
81
|
+
rescue StandardError => e
|
82
|
+
user&.delete! rescue nil
|
83
|
+
false
|
49
84
|
end
|
85
|
+
#=> false
|
@@ -21,7 +21,7 @@ Familia.debug = false
|
|
21
21
|
#=> ["bob@test.com", "Bob Jones", "bob@example.com"]
|
22
22
|
|
23
23
|
## Legacy hash support (single hash argument)
|
24
|
-
@customer4 = Customer.new({custid: 'legacy@test.com', name: 'Legacy User', role: 'admin'})
|
24
|
+
@customer4 = Customer.new({ custid: 'legacy@test.com', name: 'Legacy User', role: 'admin' })
|
25
25
|
[@customer4.custid, @customer4.name, @customer4.role]
|
26
26
|
#=> ["legacy@test.com", "Legacy User", "admin"]
|
27
27
|
|
@@ -54,7 +54,7 @@ Familia.debug = false
|
|
54
54
|
#=> "Jane Smith"
|
55
55
|
|
56
56
|
## to_a works correctly with keyword-initialized objects
|
57
|
-
@customer2.to_a[4]
|
57
|
+
@customer2.to_a[4] # name field should be the fifth field defined in the class
|
58
58
|
#=> "Jane Smith"
|
59
59
|
|
60
60
|
## Session has limited fields (only sessid defined)
|
@@ -68,7 +68,7 @@ Familia.debug = false
|
|
68
68
|
#=> "keyword-sess"
|
69
69
|
|
70
70
|
## Session with legacy hash
|
71
|
-
@session3 = Session.new({sessid: 'hash-sess'})
|
71
|
+
@session3 = Session.new({ sessid: 'hash-sess' })
|
72
72
|
@session3.sessid
|
73
73
|
#=> "hash-sess"
|
74
74
|
|
@@ -106,7 +106,7 @@ Familia.debug = false
|
|
106
106
|
|
107
107
|
## "Cleaning up" test objects that were never saved returns false.
|
108
108
|
@customer1.save
|
109
|
-
|
109
|
+
[
|
110
110
|
@customer1, @customer2, @customer3, @customer4, @customer6, @customer7,
|
111
111
|
@session1, @session2, @session3,
|
112
112
|
@domain1, @domain2,
|
@@ -30,12 +30,12 @@ class RelationsTestProduct < Familia::Horreum
|
|
30
30
|
end
|
31
31
|
|
32
32
|
@test_user = RelationsTestUser.new
|
33
|
-
@test_user.userid =
|
34
|
-
@test_user.name =
|
33
|
+
@test_user.userid = 'user123'
|
34
|
+
@test_user.name = 'Test User'
|
35
35
|
|
36
36
|
@test_product = RelationsTestProduct.new
|
37
|
-
@test_product.productid =
|
38
|
-
@test_product.title =
|
37
|
+
@test_product.productid = 'prod456'
|
38
|
+
@test_product.title = 'Test Product'
|
39
39
|
|
40
40
|
## Class knows about Database type relationships
|
41
41
|
RelationsTestUser.has_relations?
|
@@ -69,27 +69,27 @@ prefs = @test_user.preferences
|
|
69
69
|
|
70
70
|
## Can work with List Database type
|
71
71
|
@test_user.sessions.clear
|
72
|
-
@test_user.sessions.push(
|
72
|
+
@test_user.sessions.push('session1', 'session2')
|
73
73
|
@test_user.sessions.size
|
74
74
|
#=> 2
|
75
75
|
|
76
76
|
## Can work with Set Database type
|
77
77
|
@test_user.tags.clear
|
78
|
-
@test_user.tags.add(
|
78
|
+
@test_user.tags.add('ruby', 'valkey', 'web')
|
79
79
|
@test_user.tags.size
|
80
80
|
#=> 3
|
81
81
|
|
82
82
|
## Can work with SortedSet Database type
|
83
83
|
@test_user.scores.clear
|
84
|
-
@test_user.scores.add(100,
|
85
|
-
@test_user.scores.add(200,
|
84
|
+
@test_user.scores.add(100, 'level1')
|
85
|
+
@test_user.scores.add(200, 'level2')
|
86
86
|
@test_user.scores.size
|
87
87
|
#=> 2
|
88
88
|
|
89
89
|
## Can work with HashKey Database type
|
90
90
|
@test_user.preferences.clear
|
91
|
-
@test_user.preferences.put(
|
92
|
-
@test_user.preferences.put(
|
91
|
+
@test_user.preferences.put('theme', 'dark')
|
92
|
+
@test_user.preferences.put('lang', 'en')
|
93
93
|
@test_user.preferences.size
|
94
94
|
#=> 2
|
95
95
|
|
@@ -118,7 +118,7 @@ prefs = @test_user.preferences
|
|
118
118
|
#=> :tags
|
119
119
|
|
120
120
|
## Can check if Database types exist
|
121
|
-
@test_user.scores.add(50,
|
121
|
+
@test_user.scores.add(50, 'test')
|
122
122
|
before_exists = @test_user.scores.exists?
|
123
123
|
@test_user.scores.clear
|
124
124
|
after_exists = @test_user.scores.exists?
|
@@ -126,13 +126,13 @@ after_exists = @test_user.scores.exists?
|
|
126
126
|
#=> [true, false]
|
127
127
|
|
128
128
|
## Can destroy individual Database types
|
129
|
-
@test_user.preferences.put(
|
129
|
+
@test_user.preferences.put('temp', 'value')
|
130
130
|
@test_user.preferences.clear
|
131
131
|
@test_user.preferences.exists?
|
132
132
|
#=> false
|
133
133
|
|
134
134
|
## Parent object destruction does not clean up relations
|
135
|
-
@test_user.sessions.add(
|
135
|
+
@test_user.sessions.add('cleanup_test')
|
136
136
|
@test_user.destroy!
|
137
137
|
@test_user.sessions.exists?
|
138
138
|
#=> true
|
@@ -142,5 +142,4 @@ after_exists = @test_user.scores.exists?
|
|
142
142
|
@test_user.sessions.clear
|
143
143
|
#=> true
|
144
144
|
|
145
|
-
|
146
145
|
@test_product.destroy!
|
@@ -76,7 +76,7 @@ Familia.debug = false
|
|
76
76
|
#=> ["Bob Jones", "jane@example.com"]
|
77
77
|
|
78
78
|
## serialize_value handles strings
|
79
|
-
@customer.serialize_value(
|
79
|
+
@customer.serialize_value('test string')
|
80
80
|
#=> "test string"
|
81
81
|
|
82
82
|
## serialize_value handles numbers
|
@@ -84,7 +84,7 @@ Familia.debug = false
|
|
84
84
|
#=> "42"
|
85
85
|
|
86
86
|
## serialize_value handles hashes as JSON
|
87
|
-
@customer.serialize_value({key: 'value', num: 123})
|
87
|
+
@customer.serialize_value({ key: 'value', num: 123 })
|
88
88
|
#=> "{\"key\":\"value\",\"num\":123}"
|
89
89
|
|
90
90
|
## serialize_value handles arrays as JSON
|
@@ -117,7 +117,7 @@ result.size
|
|
117
117
|
#=> "temp_value"
|
118
118
|
|
119
119
|
## Empty batch_update still works
|
120
|
-
result = @customer.batch_update
|
120
|
+
result = @customer.batch_update
|
121
121
|
result.successful?
|
122
122
|
#=> true
|
123
123
|
|
data/try/horreum/settings_try.rb
CHANGED
@@ -1,43 +1,37 @@
|
|
1
|
-
require_relative '../helpers/test_helpers'
|
2
|
-
|
3
1
|
# Test Horreum settings
|
4
|
-
group "Horreum Settings"
|
5
|
-
|
6
|
-
setup do
|
7
|
-
@user_class = Class.new(Familia::Horreum) do
|
8
|
-
identifier :email
|
9
|
-
field :name
|
10
|
-
end
|
11
|
-
end
|
12
2
|
|
13
|
-
|
14
|
-
@user_class.db 5
|
15
|
-
user = @user_class.new(email: "test@example.com")
|
3
|
+
require_relative '../helpers/test_helpers'
|
16
4
|
|
17
|
-
|
5
|
+
## database selection inheritance
|
6
|
+
user_class = Class.new(Familia::Horreum) do
|
7
|
+
identifier_field :email
|
8
|
+
field :email
|
9
|
+
field :name
|
10
|
+
logical_database 5
|
18
11
|
end
|
19
12
|
|
20
|
-
|
21
|
-
|
22
|
-
@user_class.load_method :from_json
|
23
|
-
user = @user_class.new(email: "test@example.com")
|
13
|
+
user_class.new(email: "test@example.com")
|
14
|
+
#==> _.logical_database == 5
|
24
15
|
|
25
|
-
|
26
|
-
|
16
|
+
## custom serialization methods
|
17
|
+
user_class = Class.new(Familia::Horreum) do
|
18
|
+
identifier_field :email
|
19
|
+
field :email
|
20
|
+
field :name
|
27
21
|
end
|
28
22
|
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
user_class.new(email: "test@example.com")
|
24
|
+
#==> _.respond_to?(:dump_method)
|
25
|
+
#==> _.respond_to?(:load_method)
|
32
26
|
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
## redisuri generation with suffix
|
28
|
+
user_class = Class.new(Familia::Horreum) do
|
29
|
+
identifier_field :email
|
30
|
+
field :email
|
36
31
|
end
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
uri = user.redisuri("suffix")
|
33
|
+
user = user_class.new(email: "test@example.com")
|
34
|
+
uri = user.redisuri("suffix")
|
41
35
|
|
42
|
-
|
43
|
-
|
36
|
+
uri.include?("suffix")
|
37
|
+
#=!> StandardError
|
@@ -1,46 +1,56 @@
|
|
1
|
+
# Test cross-component integration scenarios
|
2
|
+
|
1
3
|
require_relative '../helpers/test_helpers'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
ttl 3600
|
13
|
-
end
|
5
|
+
TestUser = Class.new(Familia::Horreum) do
|
6
|
+
identifier_field :email
|
7
|
+
field :email
|
8
|
+
field :name
|
9
|
+
feature :expiration
|
10
|
+
feature :safe_dump
|
11
|
+
set :tags
|
12
|
+
|
13
|
+
default_expiration 3600
|
14
14
|
end
|
15
15
|
|
16
|
-
try "Horreum with multiple features integration" do
|
17
|
-
user = @user_class.new(email: "test@example.com", name: "Integration Test")
|
18
|
-
user.save
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
## Horreum with multiple features integration
|
18
|
+
user = TestUser.new(email: "test@example.com", name: "Integration Test")
|
19
|
+
user.save
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
# Test expiration feature
|
22
|
+
user.expire(1800)
|
23
|
+
ttl_works = user.current_expiration > 0
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
25
|
+
# Test safe_dump feature
|
26
|
+
safe_data = user.safe_dump
|
27
|
+
safe_dump_works = safe_data.is_a?(Hash)
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
result = ttl_works && safe_dump_works
|
30
|
+
user.delete!
|
31
|
+
result
|
32
|
+
#=> true
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
tags << "ruby" << "redis"
|
34
|
+
## Cannot generate a prefix for an anonymous class
|
35
|
+
user_class = Class.new(Familia::Horreum) do
|
41
36
|
|
42
|
-
# Expiration should cascade
|
43
|
-
tags.exists? && user.exists?
|
44
|
-
ensure
|
45
|
-
user&.delete!
|
46
37
|
end
|
38
|
+
user_class.prefix
|
39
|
+
#=!> Familia::Problem
|
40
|
+
|
41
|
+
## RedisType relations with Horreum expiration
|
42
|
+
user_class = TestUser
|
43
|
+
|
44
|
+
user = user_class.new(email: "test@example.com")
|
45
|
+
user.save
|
46
|
+
user.expire(1800)
|
47
|
+
|
48
|
+
# Create related RedisType
|
49
|
+
tags = user.tags
|
50
|
+
tags << "ruby" << "redis"
|
51
|
+
|
52
|
+
# Check if both exist
|
53
|
+
result = tags.exists? && user.exists?
|
54
|
+
user.delete!
|
55
|
+
result
|
56
|
+
#=> true
|
@@ -6,11 +6,11 @@ require_relative '../helpers/test_helpers'
|
|
6
6
|
# Setup
|
7
7
|
@now = Time.now.to_i
|
8
8
|
@customer = Customer.new
|
9
|
-
@customer.custid =
|
10
|
-
@customer.email =
|
11
|
-
@customer.role =
|
9
|
+
@customer.custid = 'test+customer_safedump@example.com'
|
10
|
+
@customer.email = 'test+customer_safedump@example.com'
|
11
|
+
@customer.role = 'user'
|
12
12
|
# No longer need to set key field - identifier computed from custid
|
13
|
-
@customer.planid =
|
13
|
+
@customer.planid = 'basic'
|
14
14
|
@customer.created = @now
|
15
15
|
@customer.updated = @now
|
16
16
|
@customer.verified = true
|