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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +12 -5
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +3 -9
  5. data/lib/familia/core_ext.rb +2 -2
  6. data/lib/familia/features/expiration.rb +0 -1
  7. data/lib/familia/features/relatable_objects.rb +127 -0
  8. data/lib/familia/features.rb +7 -3
  9. data/lib/familia/horreum/class_methods.rb +32 -4
  10. data/lib/familia/secure_identifier.rb +129 -0
  11. data/lib/familia/utils.rb +7 -96
  12. data/lib/familia/version.rb +1 -1
  13. data/lib/familia.rb +2 -0
  14. data/try/configuration/scenarios_try.rb +43 -31
  15. data/try/core/errors_try.rb +10 -10
  16. data/try/core/extensions_try.rb +56 -23
  17. data/try/core/familia_extended_try.rb +3 -3
  18. data/try/core/familia_try.rb +0 -4
  19. data/try/core/middleware_try.rb +34 -40
  20. data/try/core/secure_identifier_try.rb +104 -0
  21. data/try/core/tools_try.rb +52 -36
  22. data/try/core/utils_try.rb +0 -98
  23. data/try/datatypes/boolean_try.rb +6 -7
  24. data/try/datatypes/datatype_base_try.rb +2 -2
  25. data/try/datatypes/hash_try.rb +0 -1
  26. data/try/datatypes/list_try.rb +0 -1
  27. data/try/datatypes/set_try.rb +0 -2
  28. data/try/datatypes/sorted_set_try.rb +1 -2
  29. data/try/datatypes/string_try.rb +1 -2
  30. data/try/edge_cases/empty_identifiers_try.rb +42 -35
  31. data/try/edge_cases/hash_symbolization_try.rb +5 -5
  32. data/try/edge_cases/json_serialization_try.rb +12 -13
  33. data/try/edge_cases/race_conditions_try.rb +46 -49
  34. data/try/edge_cases/reserved_keywords_try.rb +103 -49
  35. data/try/edge_cases/string_coercion_try.rb +2 -2
  36. data/try/edge_cases/ttl_side_effects_try.rb +44 -25
  37. data/try/features/expiration_try.rb +2 -2
  38. data/try/features/quantization_try.rb +2 -2
  39. data/try/features/relatable_objects_try.rb +221 -0
  40. data/try/features/safe_dump_advanced_try.rb +13 -14
  41. data/try/features/safe_dump_try.rb +8 -8
  42. data/try/helpers/test_helpers.rb +10 -12
  43. data/try/horreum/base_try.rb +9 -9
  44. data/try/horreum/class_methods_try.rb +27 -30
  45. data/try/horreum/commands_try.rb +69 -33
  46. data/try/horreum/initialization_try.rb +4 -4
  47. data/try/horreum/relations_try.rb +13 -14
  48. data/try/horreum/serialization_try.rb +3 -3
  49. data/try/horreum/settings_try.rb +25 -31
  50. data/try/integration/cross_component_try.rb +45 -35
  51. data/try/models/customer_safe_dump_try.rb +4 -4
  52. data/try/models/customer_try.rb +21 -24
  53. data/try/models/datatype_base_try.rb +0 -1
  54. data/try/models/familia_object_try.rb +3 -4
  55. data/try/performance/benchmarks_try.rb +47 -38
  56. data/try/prototypes/atomic_saves_v4.rb +3 -3
  57. metadata +15 -12
  58. data/try/core/refinements_try.rb +0 -39
  59. /data/try/{pooling/connection_pool_test_try.rb → core/pools_try.rb} +0 -0
  60. /data/try/{pooling → prototypes/pooling}/README.md +0 -0
  61. /data/try/{pooling/configurable_stress_test_try.rb → prototypes/pooling/configurable_stress_test.rb} +0 -0
  62. /data/try/{pooling → prototypes/pooling}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  63. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_metrics.rb +0 -0
  64. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_stress_test.rb +0 -0
  65. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_threading_models.rb +0 -0
  66. /data/try/{pooling → prototypes/pooling}/lib/visualize_stress_results.rb +0 -0
  67. /data/try/{pooling/pool_siege_try.rb → prototypes/pooling/pool_siege.rb} +0 -0
  68. /data/try/{pooling/run_stress_tests_try.rb → prototypes/pooling/run_stress_tests.rb} +0 -0
@@ -4,15 +4,14 @@
4
4
  require_relative '../../lib/familia'
5
5
  require_relative '../helpers/test_helpers'
6
6
 
7
-
8
7
  # Setup
9
8
  @now = Time.now.to_f
10
9
  @customer = Customer.new
11
- @customer.custid = "test@example.com"
12
- @customer.email = "test@example.com"
13
- @customer.role = "user"
10
+ @customer.custid = 'test@example.com'
11
+ @customer.email = 'test@example.com'
12
+ @customer.role = 'user'
14
13
  # No longer need to set key field - identifier computed from custid
15
- @customer.planid = "basic"
14
+ @customer.planid = 'basic'
16
15
  @customer.created = Time.now.to_i
17
16
  @customer.updated = Time.now.to_i
18
17
 
@@ -21,7 +20,7 @@ require_relative '../helpers/test_helpers'
21
20
  #=> true
22
21
 
23
22
  ## Customer can be retrieved by identifier
24
- retrieved_customer = Customer.find_by_id("test@example.com")
23
+ retrieved_customer = Customer.find_by_id('test@example.com')
25
24
  retrieved_customer.custid
26
25
  #=> "test@example.com"
27
26
 
@@ -30,12 +29,12 @@ retrieved_customer.custid
30
29
  #=> "test@example.com"
31
30
 
32
31
  ## Customer role can be set and retrieved
33
- @customer.role = "admin"
32
+ @customer.role = 'admin'
34
33
  @customer.role
35
34
  #=> "admin"
36
35
 
37
36
  ## Customer can update fields
38
- @customer.planid = "premium"
37
+ @customer.planid = 'premium'
39
38
  @customer.save
40
39
  ident = @customer.identifier
41
40
  Customer.find_by_id(ident).planid
@@ -48,27 +47,26 @@ Customer.find_by_id(ident).planid
48
47
  #=> '1'
49
48
 
50
49
  ## Customer can add custom domain via add method
51
- @customer.custom_domains.add(@now, "example.org")
52
- @customer.custom_domains.members.include?("example.org")
50
+ @customer.custom_domains.add(@now, 'example.org')
51
+ @customer.custom_domains.members.include?('example.org')
53
52
  #=> true
54
53
 
55
54
  ## Customer can retrieve custom domain score via score method
56
- @customer.custom_domains.score("example.org")
55
+ @customer.custom_domains.score('example.org')
57
56
  #=> @now
58
57
 
59
58
  ## Customer can add custom domain via []= method
60
- @customer.custom_domains["example2.org"] = @now
61
- @customer.custom_domains.members.include?("example2.org")
59
+ @customer.custom_domains['example2.org'] = @now
60
+ @customer.custom_domains.members.include?('example2.org')
62
61
  #=> true
63
62
 
64
63
  ## Customer can retrieve custom domain score via []
65
- @customer.custom_domains["example.org"]
64
+ @customer.custom_domains['example.org']
66
65
  #=> @now
67
66
 
68
-
69
67
  ## Customer can store timeline
70
- @customer.timeline["last_login"] = @now
71
- @customer.timeline["last_login"].to_i.positive?
68
+ @customer.timeline['last_login'] = @now
69
+ @customer.timeline['last_login'].to_i.positive?
72
70
  #=> true
73
71
 
74
72
  ## Customer can be added to class-level sorted set
@@ -82,19 +80,19 @@ Customer.instances.member?(@customer)
82
80
  #=> false
83
81
 
84
82
  ## Customer can add a session
85
- @customer.sessions << "session123"
86
- @customer.sessions.members.include?("session123")
83
+ @customer.sessions << 'session123'
84
+ @customer.sessions.members.include?('session123')
87
85
  #=> true
88
86
 
89
87
  ## Customer can set and get password reset information
90
- @customer.password_reset["token"] = "reset123"
91
- @customer.password_reset["token"]
88
+ @customer.password_reset['token'] = 'reset123'
89
+ @customer.password_reset['token']
92
90
  #=> "reset123"
93
91
 
94
92
  ## Customer can be destroyed
95
93
  ret = @customer.destroy!
96
- cust = Customer.find_by_id("test@example.com")
97
- exists = Customer.exists?("test@example.com")
94
+ cust = Customer.find_by_id('test@example.com')
95
+ exists = Customer.exists?('test@example.com')
98
96
  [ret, cust.nil?, exists]
99
97
  #=> [true, true, false]
100
98
 
@@ -138,6 +136,5 @@ Customer.instances.logical_database
138
136
  Customer.instances.uri.to_s
139
137
  #=> 'redis://127.0.0.1/15'
140
138
 
141
-
142
139
  # Teardown
143
140
  Customer.instances.delete!
@@ -55,7 +55,6 @@ stripe_customer.class.name
55
55
  #==> _.respond_to?(:exists?)
56
56
  #=/=> _.respond_to?(:destroy!)
57
57
 
58
-
59
58
  ## Can check if DataType exists in Redis
60
59
  timeline = @sample_obj.timeline
61
60
  exists_before = timeline.exists?
@@ -28,7 +28,7 @@ Session.suffix
28
28
  #=> 'session:atoken:object'
29
29
 
30
30
  ## Familia#save
31
- @cust = Customer.new :delano, "Delano Mandelbaum"
31
+ @cust = Customer.new :delano, 'Delano Mandelbaum'
32
32
  @cust.save
33
33
  #=> true
34
34
 
@@ -74,7 +74,7 @@ Customer.customers.delete!
74
74
  #=> true
75
75
 
76
76
  ## Familia class replace 1 of 4
77
- Customer.message.value = "msg1"
77
+ Customer.message.value = 'msg1'
78
78
  #=> "msg1"
79
79
 
80
80
  ## Familia class replace 2 of 4
@@ -82,13 +82,12 @@ Customer.message.value
82
82
  #=> "msg1"
83
83
 
84
84
  ## Familia class replace 3 of 4
85
- Customer.message = "msg2"
85
+ Customer.message = 'msg2'
86
86
  #=> "msg2"
87
87
 
88
88
  ## Familia class replace 4 of 4
89
89
  Customer.message.value
90
90
  #=> "msg2"
91
91
 
92
-
93
92
  # Teardown
94
93
  Customer.values.delete!
@@ -1,55 +1,64 @@
1
+ # Performance benchmarks separate from stress tests
2
+
1
3
  require_relative '../helpers/test_helpers'
2
4
  require 'benchmark'
3
5
 
4
- # Performance benchmarks separate from stress tests
5
- group "Performance Benchmarks"
6
-
7
- setup do
8
- @user_class = Class.new(Familia::Horreum) do
9
- identifier :email
10
- field :name
11
- field :data
12
- end
6
+ ## serialization performance comparison
7
+ user_class = Class.new(Familia::Horreum) do
8
+ identifier_field :email
9
+ field :name
10
+ field :data
13
11
  end
14
12
 
15
- try "serialization performance comparison" do
16
- large_data = { items: (1..1000).to_a, metadata: "x" * 1000 }
13
+ large_data = { items: (1..1000).to_a, metadata: "x" * 1000 }
17
14
 
18
- json_time = Benchmark.realtime do
19
- 100.times { JSON.dump(large_data) }
20
- end
21
-
22
- familia_time = Benchmark.realtime do
23
- 100.times { Familia.distinguisher(large_data) }
24
- end
15
+ json_time = Benchmark.realtime do
16
+ 100.times { JSON.dump(large_data) }
17
+ end
25
18
 
26
- json_time > 0 && familia_time > 0
19
+ familia_time = Benchmark.realtime do
20
+ 100.times { Familia.distinguisher(large_data) }
27
21
  end
28
22
 
29
- try "bulk operations vs individual saves" do
30
- users = 100.times.map { |i|
31
- @user_class.new(email: "user#{i}@example.com", name: "User #{i}")
32
- }
23
+ json_time > 0 && familia_time > 0
24
+ #=!> StandardError
33
25
 
34
- individual_time = Benchmark.realtime do
35
- users.each(&:save)
36
- end
26
+ ## bulk operations vs individual saves
27
+ user_class = Class.new(Familia::Horreum) do
28
+ identifier_field :email
29
+ field :name
30
+ field :data
31
+ end
37
32
 
38
- # Cleanup for next test
39
- users.each(&:delete!)
33
+ users = 100.times.map { |i|
34
+ user_class.new(email: "user#{i}@example.com", name: "User #{i}")
35
+ }
40
36
 
41
- individual_time > 0
37
+ individual_time = Benchmark.realtime do
38
+ users.each(&:save)
42
39
  end
43
40
 
44
- try "Redis type access performance" do
45
- user = @user_class.new(email: "perf@example.com")
46
- user.save
41
+ # Cleanup for next test
42
+ users.each(&:delete!)
47
43
 
48
- access_time = Benchmark.realtime do
49
- 1000.times { user.set(:tags) }
50
- end
44
+ individual_time > 0
45
+ #=!> StandardError
51
46
 
52
- access_time > 0
53
- ensure
54
- user&.delete!
47
+ ## Redis type access performance
48
+ user_class = Class.new(Familia::Horreum) do
49
+ identifier_field :email
50
+ field :name
51
+ field :data
55
52
  end
53
+
54
+ user = user_class.new(email: "perf@example.com")
55
+ user.save
56
+
57
+ access_time = Benchmark.realtime do
58
+ 1000.times { user.set(:tags) }
59
+ end
60
+
61
+ result = access_time > 0
62
+ user.delete!
63
+ result
64
+ #=!> StandardError
@@ -1,7 +1,7 @@
1
1
  # try/prototypes/atomic_saves_v4.rb
2
2
 
3
3
  class BankAccount < Familia::Horreum
4
- class_sorted_set :relatable_object_ids
4
+ class_sorted_set :relatable_objids
5
5
 
6
6
  identifier_field :account_number
7
7
  field :account_number
@@ -89,7 +89,7 @@ class BankAccount < Familia::Horreum
89
89
  # does a class level method like `add` get its db connection from then?
90
90
  #
91
91
  # This is PURPOSE2, a major reason for implementing transactions. We want
92
- # to prevent our relatable_object_ids index from being updated if the
92
+ # to prevent our relatable_objids index from being updated if the
93
93
  # account save fails.
94
94
  add accnt
95
95
 
@@ -99,7 +99,7 @@ class BankAccount < Familia::Horreum
99
99
  end
100
100
 
101
101
  def add(accnt)
102
- relatable_object_ids.add Time.now.to_f, accnt.identifier
102
+ relatable_objids.add Time.now.to_f, accnt.identifier
103
103
  end
104
104
  end
105
105
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: familia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre2
4
+ version: 2.0.0.pre4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -151,6 +151,7 @@ files:
151
151
  - lib/familia/features.rb
152
152
  - lib/familia/features/expiration.rb
153
153
  - lib/familia/features/quantization.rb
154
+ - lib/familia/features/relatable_objects.rb
154
155
  - lib/familia/features/safe_dump.rb
155
156
  - lib/familia/horreum.rb
156
157
  - lib/familia/horreum/class_methods.rb
@@ -163,6 +164,7 @@ files:
163
164
  - lib/familia/logging.rb
164
165
  - lib/familia/multi_result.rb
165
166
  - lib/familia/refinements.rb
167
+ - lib/familia/secure_identifier.rb
166
168
  - lib/familia/settings.rb
167
169
  - lib/familia/utils.rb
168
170
  - lib/familia/version.rb
@@ -174,7 +176,8 @@ files:
174
176
  - try/core/familia_extended_try.rb
175
177
  - try/core/familia_try.rb
176
178
  - try/core/middleware_try.rb
177
- - try/core/refinements_try.rb
179
+ - try/core/pools_try.rb
180
+ - try/core/secure_identifier_try.rb
178
181
  - try/core/settings_try.rb
179
182
  - try/core/tools_try.rb
180
183
  - try/core/utils_try.rb
@@ -194,6 +197,7 @@ files:
194
197
  - try/edge_cases/ttl_side_effects_try.rb
195
198
  - try/features/expiration_try.rb
196
199
  - try/features/quantization_try.rb
200
+ - try/features/relatable_objects_try.rb
197
201
  - try/features/safe_dump_advanced_try.rb
198
202
  - try/features/safe_dump_try.rb
199
203
  - try/helpers/test_helpers.rb
@@ -210,22 +214,21 @@ files:
210
214
  - try/models/datatype_base_try.rb
211
215
  - try/models/familia_object_try.rb
212
216
  - try/performance/benchmarks_try.rb
213
- - try/pooling/README.md
214
- - try/pooling/configurable_stress_test_try.rb
215
- - try/pooling/connection_pool_test_try.rb
216
- - try/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb
217
- - try/pooling/lib/connection_pool_metrics.rb
218
- - try/pooling/lib/connection_pool_stress_test.rb
219
- - try/pooling/lib/connection_pool_threading_models.rb
220
- - try/pooling/lib/visualize_stress_results.rb
221
- - try/pooling/pool_siege_try.rb
222
- - try/pooling/run_stress_tests_try.rb
223
217
  - try/prototypes/atomic_saves_v1_context_proxy.rb
224
218
  - try/prototypes/atomic_saves_v2_connection_switching.rb
225
219
  - try/prototypes/atomic_saves_v3_connection_pool.rb
226
220
  - try/prototypes/atomic_saves_v4.rb
227
221
  - try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb
228
222
  - try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb
223
+ - try/prototypes/pooling/README.md
224
+ - try/prototypes/pooling/configurable_stress_test.rb
225
+ - try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb
226
+ - try/prototypes/pooling/lib/connection_pool_metrics.rb
227
+ - try/prototypes/pooling/lib/connection_pool_stress_test.rb
228
+ - try/prototypes/pooling/lib/connection_pool_threading_models.rb
229
+ - try/prototypes/pooling/lib/visualize_stress_results.rb
230
+ - try/prototypes/pooling/pool_siege.rb
231
+ - try/prototypes/pooling/run_stress_tests.rb
229
232
  homepage: https://github.com/delano/familia
230
233
  licenses:
231
234
  - MIT
@@ -1,39 +0,0 @@
1
- require_relative '../helpers/test_helpers'
2
-
3
- # Test Familia refinements
4
- group "Familia Refinements"
5
-
6
- using Familia::FlexibleHashAccess
7
-
8
- try "FlexibleHashAccess allows string/symbol key interchange" do
9
- hash = { name: "test", "email" => "test@example.com" }
10
-
11
- hash[:name] == "test" &&
12
- hash["name"] == "test" &&
13
- hash[:email] == "test@example.com" &&
14
- hash["email"] == "test@example.com"
15
- end
16
-
17
- try "LoggerTraceRefinement adds trace level when FAMILIA_TRACE enabled" do
18
- old_env = ENV['FAMILIA_TRACE']
19
- ENV['FAMILIA_TRACE'] = '1'
20
-
21
- logger = Logger.new(STDOUT)
22
- logger.respond_to?(:trace)
23
- ensure
24
- ENV['FAMILIA_TRACE'] = old_env
25
- end
26
-
27
- try "FAMILIA_TRACE environment control" do
28
- old_env = ENV['FAMILIA_TRACE']
29
-
30
- ENV['FAMILIA_TRACE'] = '1'
31
- trace_enabled = ENV['FAMILIA_TRACE']
32
-
33
- ENV['FAMILIA_TRACE'] = nil
34
- trace_disabled = ENV['FAMILIA_TRACE']
35
-
36
- trace_enabled == '1' && trace_disabled.nil?
37
- ensure
38
- ENV['FAMILIA_TRACE'] = old_env
39
- end
File without changes