familia 2.0.0.pre.pre → 2.0.0.pre3
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 +4 -3
- data/Gemfile.lock +24 -11
- data/bin/irb +1 -1
- data/docs/connection_pooling.md +98 -223
- data/familia.gemspec +1 -1
- data/lib/familia/connection.rb +3 -3
- 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 +18 -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 +3 -1
- data/try/configuration/scenarios_try.rb +43 -31
- data/try/core/connection_try.rb +1 -1
- 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 +2 -6
- data/try/core/middleware_try.rb +34 -40
- data/try/{pooling/connection_pool_test_try.rb → core/pools_try.rb} +2 -2
- 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 +34 -28
- 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 +22 -25
- data/try/models/datatype_base_try.rb +2 -4
- 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 +18 -15
- data/try/core/refinements_try.rb +0 -39
- /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
@@ -29,8 +29,8 @@ end
|
|
29
29
|
|
30
30
|
# Setup test object
|
31
31
|
@test_obj = ExpiringTest.new
|
32
|
-
@test_obj.id =
|
33
|
-
@test_obj.data =
|
32
|
+
@test_obj.id = 'expire_test_1'
|
33
|
+
@test_obj.data = 'test data'
|
34
34
|
|
35
35
|
## Class has default_expiration method from feature
|
36
36
|
ExpiringTest.respond_to?(:default_expiration)
|
@@ -67,7 +67,7 @@ obj_stamp.class
|
|
67
67
|
## Different quantum values produce different buckets
|
68
68
|
test_time = Time.utc(2023, 6, 15, 14, 30, 0) # use a fixed time, mid-day (avoid ToD boundary)
|
69
69
|
@hour_stamp = QuantizedTest.qstamp(3600, time: test_time)
|
70
|
-
@day_stamp = QuantizedTest.qstamp(
|
70
|
+
@day_stamp = QuantizedTest.qstamp(86_400, time: test_time)
|
71
71
|
#=> 1686787200
|
72
72
|
#=<> @hour_stamp
|
73
73
|
#==> @hour_stamp == 1686837600
|
@@ -86,5 +86,5 @@ custom_stamp
|
|
86
86
|
#=> "2023061514"
|
87
87
|
|
88
88
|
# Cleanup
|
89
|
-
@test_obj.id =
|
89
|
+
@test_obj.id = 'quantized_test_obj' # Set identifier before cleanup
|
90
90
|
@test_obj.destroy! if @test_obj
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# try/features/relatable_objects_try.rb
|
2
|
+
|
3
|
+
# Test RelatableObject feature functionality
|
4
|
+
|
5
|
+
require_relative '../../lib/familia'
|
6
|
+
require_relative '../helpers/test_helpers'
|
7
|
+
|
8
|
+
Familia.debug = false
|
9
|
+
|
10
|
+
class RelatableTest < Familia::Horreum
|
11
|
+
feature :relatable_object
|
12
|
+
identifier_field :id
|
13
|
+
field :id
|
14
|
+
field :name
|
15
|
+
end
|
16
|
+
|
17
|
+
class RelatedTest < Familia::Horreum
|
18
|
+
feature :relatable_object
|
19
|
+
identifier_field :id
|
20
|
+
field :id
|
21
|
+
field :name
|
22
|
+
end
|
23
|
+
|
24
|
+
class NonRelatableTest < Familia::Horreum
|
25
|
+
identifier_field :id
|
26
|
+
field :id
|
27
|
+
field :name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Setup test objects
|
31
|
+
@relatable_obj = RelatableTest.new
|
32
|
+
@relatable_obj.id = 'test_rel_1'
|
33
|
+
@relatable_obj.name = 'Test Relatable 1'
|
34
|
+
|
35
|
+
@related_obj = RelatedTest.new
|
36
|
+
@related_obj.id = 'test_rel_2'
|
37
|
+
@related_obj.name = 'Test Related 2'
|
38
|
+
|
39
|
+
@non_relatable = NonRelatableTest.new
|
40
|
+
@non_relatable.id = 'test_non_rel'
|
41
|
+
@non_relatable.name = 'Non Relatable'
|
42
|
+
|
43
|
+
## Class has RelatableObject methods mixed in
|
44
|
+
RelatableTest.respond_to?(:relatable_objids)
|
45
|
+
#=> true
|
46
|
+
|
47
|
+
## Class has owners class method
|
48
|
+
RelatableTest.respond_to?(:owners)
|
49
|
+
#=> true
|
50
|
+
|
51
|
+
## Class has relatable? method
|
52
|
+
RelatableTest.respond_to?(:relatable?)
|
53
|
+
#=> true
|
54
|
+
|
55
|
+
## Class has generate_objid method
|
56
|
+
RelatableTest.respond_to?(:generate_objid)
|
57
|
+
#=> true
|
58
|
+
|
59
|
+
## Class has generate_extid method
|
60
|
+
RelatableTest.respond_to?(:generate_extid)
|
61
|
+
#=> true
|
62
|
+
|
63
|
+
## Class has find_by_objid method
|
64
|
+
RelatableTest.respond_to?(:find_by_objid)
|
65
|
+
#=> true
|
66
|
+
|
67
|
+
## Object has objid method
|
68
|
+
@relatable_obj.respond_to?(:objid)
|
69
|
+
#=> true
|
70
|
+
|
71
|
+
## Object has extid method
|
72
|
+
@relatable_obj.respond_to?(:extid)
|
73
|
+
#=> true
|
74
|
+
|
75
|
+
## Object has api_version field
|
76
|
+
@relatable_obj.respond_to?(:api_version)
|
77
|
+
#=> true
|
78
|
+
|
79
|
+
## Object has owner? method
|
80
|
+
@relatable_obj.respond_to?(:owner?)
|
81
|
+
#=> true
|
82
|
+
|
83
|
+
## Object has owned? method
|
84
|
+
@relatable_obj.respond_to?(:owned?)
|
85
|
+
#=> true
|
86
|
+
|
87
|
+
## Object has relatable_objid alias
|
88
|
+
@relatable_obj.respond_to?(:relatable_objid)
|
89
|
+
#=> true
|
90
|
+
|
91
|
+
## Object has external_identifier alias
|
92
|
+
@relatable_obj.respond_to?(:external_identifier)
|
93
|
+
#=> true
|
94
|
+
|
95
|
+
## objid is lazily generated on first access
|
96
|
+
@relatable_obj.objid
|
97
|
+
#=:> String
|
98
|
+
|
99
|
+
## objid is a UUID v7 format
|
100
|
+
objid = @relatable_obj.objid
|
101
|
+
objid.match?(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
|
102
|
+
#=> true
|
103
|
+
|
104
|
+
## objid is cached after first generation
|
105
|
+
objid1 = @relatable_obj.objid
|
106
|
+
objid2 = @related_obj.objid
|
107
|
+
[objid1, objid2]
|
108
|
+
#=/=> _[0].eql?(_[1])
|
109
|
+
|
110
|
+
## extid is lazily generated on first access
|
111
|
+
@relatable_obj.extid
|
112
|
+
#=:> String
|
113
|
+
|
114
|
+
## extid starts with 'ext_' prefix (from our mock)
|
115
|
+
@relatable_obj.extid.start_with?('ext_')
|
116
|
+
#=> true
|
117
|
+
|
118
|
+
## extid is cached after first generation
|
119
|
+
extid1 = @relatable_obj.extid
|
120
|
+
extid2 = @related_obj.extid
|
121
|
+
[extid1, extid2]
|
122
|
+
#=/=> _[0].eql?(_[1])
|
123
|
+
|
124
|
+
## api_version defaults to 'v2'
|
125
|
+
@relatable_obj.api_version
|
126
|
+
#=> 'v2'
|
127
|
+
|
128
|
+
## relatable_objid is alias for objid
|
129
|
+
[@relatable_obj.relatable_objid, @relatable_obj.objid]
|
130
|
+
#==> _[0].eql?(_[1])
|
131
|
+
|
132
|
+
## external_identifier is alias for extid
|
133
|
+
[@relatable_obj.external_identifier, @relatable_obj.extid]
|
134
|
+
#==> _[0].eql?(_[1])
|
135
|
+
|
136
|
+
## relatable? prevents self-ownership (same class)
|
137
|
+
RelatableTest.relatable?(@relatable_obj)
|
138
|
+
#=!> V2::Features::RelatableObjectError
|
139
|
+
|
140
|
+
## relatable? returns true for different relatable classes
|
141
|
+
RelatableTest.relatable?(@related_obj)
|
142
|
+
#=> true
|
143
|
+
|
144
|
+
## relatable? raises error for non-relatable objects
|
145
|
+
RelatableTest.relatable?(@non_relatable)
|
146
|
+
#=!> V2::Features::RelatableObjectError
|
147
|
+
|
148
|
+
|
149
|
+
## relatable? with block executes block for relatable objects
|
150
|
+
result = nil
|
151
|
+
RelatableTest.relatable?(@related_obj) do
|
152
|
+
result = "executed"
|
153
|
+
end
|
154
|
+
result
|
155
|
+
#=> "executed"
|
156
|
+
|
157
|
+
## owned? returns false when no owner is set
|
158
|
+
@relatable_obj.owned?
|
159
|
+
#=> false
|
160
|
+
|
161
|
+
## owner? returns false when objects are not related
|
162
|
+
@relatable_obj.owner?(@related_obj)
|
163
|
+
#=> false
|
164
|
+
|
165
|
+
## generate_objid creates UUID v7
|
166
|
+
generated_id = RelatableTest.generate_objid
|
167
|
+
generated_id.match?(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
|
168
|
+
#=> true
|
169
|
+
|
170
|
+
## generate_extid creates external ID
|
171
|
+
RelatableTest.generate_extid
|
172
|
+
#==> _.start_with?('ext_')
|
173
|
+
#==> _.size == 54
|
174
|
+
|
175
|
+
## find_by_objid returns nil for empty objid
|
176
|
+
result = RelatableTest.find_by_objid('')
|
177
|
+
result.nil?
|
178
|
+
#=> true
|
179
|
+
|
180
|
+
## find_by_objid returns nil for nil objid
|
181
|
+
result = RelatableTest.find_by_objid(nil)
|
182
|
+
result.nil?
|
183
|
+
#=> true
|
184
|
+
|
185
|
+
## Class has relatable_objids sorted set
|
186
|
+
objids_set = RelatableTest.relatable_objids
|
187
|
+
objids_set.class.name
|
188
|
+
#=> "Familia::SortedSet"
|
189
|
+
|
190
|
+
## Class has owners hash key
|
191
|
+
owners_hash = RelatableTest.owners
|
192
|
+
owners_hash.class.name
|
193
|
+
#=> "Familia::HashKey"
|
194
|
+
|
195
|
+
## Objects can be persisted and retrieved
|
196
|
+
@relatable_obj.save
|
197
|
+
retrieved = RelatableTest.find(@relatable_obj.id)
|
198
|
+
retrieved.id == @relatable_obj.id
|
199
|
+
#=> true
|
200
|
+
|
201
|
+
## API version is preserved when persisting
|
202
|
+
retrieved = RelatableTest.find(@relatable_obj.id)
|
203
|
+
retrieved.api_version
|
204
|
+
#=> 'v2'
|
205
|
+
|
206
|
+
## Objid is preserved when persisting
|
207
|
+
original_objid = @relatable_obj.objid
|
208
|
+
retrieved = RelatableTest.find(@relatable_obj.id)
|
209
|
+
retrieved.objid == original_objid
|
210
|
+
#=> true
|
211
|
+
|
212
|
+
## Extid is preserved when persisting
|
213
|
+
original_extid = @relatable_obj.extid
|
214
|
+
retrieved = RelatableTest.find(@relatable_obj.id)
|
215
|
+
retrieved.extid == original_extid
|
216
|
+
#=> true
|
217
|
+
|
218
|
+
# Cleanup
|
219
|
+
@relatable_obj.destroy! if @relatable_obj
|
220
|
+
@related_obj.destroy! if @related_obj
|
221
|
+
@non_relatable.destroy! if @non_relatable
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# try/features/safe_dump_extended_try.rb
|
2
2
|
|
3
|
-
|
4
3
|
# These tryouts test the safe dumping functionality.
|
5
4
|
|
6
5
|
require_relative '../../lib/familia'
|
@@ -16,8 +15,8 @@ Customer.safe_dump_fields
|
|
16
15
|
|
17
16
|
## Implementing models like Customer can safely dump their fields
|
18
17
|
@cust = Customer.new
|
19
|
-
@cust.custid =
|
20
|
-
@cust.role =
|
18
|
+
@cust.custid = 'test@example.com'
|
19
|
+
@cust.role = 'user'
|
21
20
|
@cust.verified = true
|
22
21
|
@cust.created = Time.now.to_i
|
23
22
|
@cust.updated = Time.now.to_i
|
@@ -28,11 +27,11 @@ Customer.safe_dump_fields
|
|
28
27
|
## Implementing models like Customer do have other fields
|
29
28
|
## that are by default considered not safe to dump.
|
30
29
|
@cust1 = Customer.new
|
31
|
-
@cust1.email =
|
30
|
+
@cust1.email = 'test@example.com'
|
32
31
|
|
33
|
-
@all_non_safe_fields = @cust1.instance_variables.map
|
32
|
+
@all_non_safe_fields = @cust1.instance_variables.map do |el|
|
34
33
|
el.to_s[1..-1].to_sym # slice off the leading @
|
35
|
-
|
34
|
+
end.sort
|
36
35
|
|
37
36
|
(@all_non_safe_fields - Customer.safe_dump_fields).sort
|
38
37
|
#=> [:custom_domains, :email, :password_reset, :sessions, :stripe_customer, :timeline]
|
@@ -40,16 +39,16 @@ Customer.safe_dump_fields
|
|
40
39
|
## Implementing models like Customer can rest assured knowing
|
41
40
|
## any other field not in the safe list will not be dumped.
|
42
41
|
@cust2 = Customer.new
|
43
|
-
@cust2.email =
|
44
|
-
@cust2.custid =
|
42
|
+
@cust2.email = 'test@example.com'
|
43
|
+
@cust2.custid = 'test@example.com'
|
45
44
|
@all_safe_fields = @cust2.safe_dump.keys.sort
|
46
|
-
@all_non_safe_fields = @cust2.instance_variables.map
|
45
|
+
@all_non_safe_fields = @cust2.instance_variables.map do |el|
|
47
46
|
el.to_s[1..-1].to_sym # slice off the leading @
|
48
|
-
|
47
|
+
end.sort
|
49
48
|
# Check if any of the non-safe fields are in the safe dump (tryouts bug
|
50
49
|
# if this comment is placed right before the last line.)
|
51
|
-
p [1, all_non_safe_fields: @all_non_safe_fields]
|
52
|
-
(@all_non_safe_fields & @all_safe_fields) - [
|
50
|
+
p [1, { all_non_safe_fields: @all_non_safe_fields }]
|
51
|
+
(@all_non_safe_fields & @all_safe_fields) - %i[custid role verified updated created secrets_created]
|
53
52
|
#=> []
|
54
53
|
|
55
54
|
## Bone does not have safe_dump feature enabled
|
@@ -57,7 +56,7 @@ Bone.respond_to?(:safe_dump_fields)
|
|
57
56
|
#=> false
|
58
57
|
|
59
58
|
## Bone instances do not have safe_dump method
|
60
|
-
@bone = Bone.new(token:
|
59
|
+
@bone = Bone.new(token: 'boneid1', name: 'Rex')
|
61
60
|
@bone.respond_to?(:safe_dump)
|
62
61
|
#=> false
|
63
62
|
|
@@ -70,7 +69,7 @@ Blone.safe_dump_fields
|
|
70
69
|
#=> []
|
71
70
|
|
72
71
|
## Blone instances have safe_dump method
|
73
|
-
@blone = Blone.new(name:
|
72
|
+
@blone = Blone.new(name: 'Fido', age: 5)
|
74
73
|
@blone.respond_to?(:safe_dump)
|
75
74
|
#=> true
|
76
75
|
|
@@ -19,8 +19,8 @@ class SafeDumpTest < Familia::Horreum
|
|
19
19
|
@safe_dump_fields = [
|
20
20
|
:id,
|
21
21
|
:name,
|
22
|
-
{ :
|
23
|
-
{ :
|
22
|
+
{ display_name: ->(obj) { "#{obj.name} (#{obj.id})" } },
|
23
|
+
{ has_email: ->(obj) { !obj.email.nil? && !obj.email.empty? } }
|
24
24
|
]
|
25
25
|
|
26
26
|
def active?
|
@@ -30,10 +30,10 @@ end
|
|
30
30
|
|
31
31
|
# Setup test object
|
32
32
|
@test_obj = SafeDumpTest.new
|
33
|
-
@test_obj.id =
|
34
|
-
@test_obj.name =
|
35
|
-
@test_obj.email =
|
36
|
-
@test_obj.secret_data =
|
33
|
+
@test_obj.id = 'safe_test_1'
|
34
|
+
@test_obj.name = 'Test User'
|
35
|
+
@test_obj.email = 'test@example.com'
|
36
|
+
@test_obj.secret_data = 'sensitive_info'
|
37
37
|
|
38
38
|
## Class has SafeDump methods
|
39
39
|
SafeDumpTest.respond_to?(:safe_dump_fields)
|
@@ -99,7 +99,7 @@ dump[:has_email]
|
|
99
99
|
#=> false
|
100
100
|
|
101
101
|
## Safe dump works with empty values
|
102
|
-
@test_obj.email =
|
102
|
+
@test_obj.email = ''
|
103
103
|
dump = @test_obj.safe_dump
|
104
104
|
dump[:has_email]
|
105
105
|
#=> false
|
@@ -128,7 +128,7 @@ EmptySafeDump.safe_dump_fields
|
|
128
128
|
|
129
129
|
## Empty safe_dump returns empty hash
|
130
130
|
@empty_obj = EmptySafeDump.new
|
131
|
-
@empty_obj.id =
|
131
|
+
@empty_obj.id = 'empty_test'
|
132
132
|
@empty_obj.safe_dump
|
133
133
|
#=> {}
|
134
134
|
|
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,47 @@
|
|
1
|
-
require_relative '../helpers/test_helpers'
|
2
|
-
|
3
1
|
# Test Horreum class methods
|
4
|
-
group "Horreum Class Methods"
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require_relative '../helpers/test_helpers'
|
4
|
+
|
5
|
+
## create factory method with existence checking
|
6
|
+
begin
|
7
|
+
user_class = Class.new(Familia::Horreum) do
|
8
|
+
identifier_field :email
|
9
|
+
field :email
|
9
10
|
field :name
|
10
11
|
field :age
|
11
12
|
end
|
12
|
-
end
|
13
|
-
|
14
|
-
try "create factory method with existence checking" do
|
15
|
-
user = @user_class.create(email: "test@example.com", name: "Test")
|
16
|
-
exists = @user_class.exists?("test@example.com")
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
result = user_class.respond_to?(:create) && user_class.respond_to?(:exists?)
|
15
|
+
result
|
16
|
+
rescue StandardError => e
|
17
|
+
false
|
21
18
|
end
|
19
|
+
#=> true
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
## multiget method is available
|
22
|
+
begin
|
23
|
+
user_class = Class.new(Familia::Horreum) do
|
24
|
+
identifier_field :email
|
25
|
+
field :email
|
26
|
+
field :name
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
user_class.respond_to?(:multiget)
|
30
|
+
rescue StandardError => e
|
31
|
+
false
|
32
32
|
end
|
33
|
+
#=> true
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
## find_keys method is available
|
36
|
+
begin
|
37
|
+
user_class = Class.new(Familia::Horreum) do
|
38
|
+
identifier_field :email
|
39
|
+
field :email
|
40
|
+
field :name
|
41
|
+
end
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
user_class.respond_to?(:find_keys)
|
44
|
+
rescue StandardError => e
|
45
|
+
false
|
41
46
|
end
|
47
|
+
#=> true
|