familia 1.2.1 → 2.0.0.pre2

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +68 -0
  3. data/.github/workflows/docs.yml +64 -0
  4. data/.gitignore +4 -0
  5. data/.pre-commit-config.yaml +3 -1
  6. data/.rubocop.yml +16 -9
  7. data/.rubocop_todo.yml +177 -31
  8. data/.yardopts +9 -0
  9. data/CLAUDE.md +141 -0
  10. data/Gemfile +16 -2
  11. data/Gemfile.lock +97 -36
  12. data/README.md +39 -23
  13. data/bin/irb +3 -0
  14. data/docs/connection_pooling.md +192 -0
  15. data/familia.gemspec +10 -6
  16. data/lib/familia/base.rb +19 -9
  17. data/lib/familia/connection.rb +232 -65
  18. data/lib/familia/core_ext.rb +1 -1
  19. data/lib/familia/datatype/commands.rb +59 -0
  20. data/lib/familia/{redistype → datatype}/serialization.rb +9 -13
  21. data/lib/familia/{redistype → datatype}/types/hashkey.rb +25 -25
  22. data/lib/familia/{redistype → datatype}/types/list.rb +13 -13
  23. data/lib/familia/{redistype → datatype}/types/sorted_set.rb +20 -20
  24. data/lib/familia/{redistype → datatype}/types/string.rb +22 -21
  25. data/lib/familia/{redistype → datatype}/types/unsorted_set.rb +11 -11
  26. data/lib/familia/datatype.rb +243 -0
  27. data/lib/familia/errors.rb +5 -2
  28. data/lib/familia/features/expiration.rb +33 -34
  29. data/lib/familia/features/quantization.rb +9 -3
  30. data/lib/familia/features/safe_dump.rb +2 -3
  31. data/lib/familia/features.rb +2 -2
  32. data/lib/familia/horreum/class_methods.rb +97 -110
  33. data/lib/familia/horreum/commands.rb +46 -51
  34. data/lib/familia/horreum/connection.rb +82 -0
  35. data/lib/familia/horreum/{relations_management.rb → related_fields_management.rb} +37 -35
  36. data/lib/familia/horreum/serialization.rb +61 -198
  37. data/lib/familia/horreum/settings.rb +6 -17
  38. data/lib/familia/horreum/utils.rb +11 -10
  39. data/lib/familia/horreum.rb +69 -60
  40. data/lib/familia/logging.rb +12 -12
  41. data/lib/familia/multi_result.rb +72 -0
  42. data/lib/familia/refinements.rb +7 -44
  43. data/lib/familia/settings.rb +11 -11
  44. data/lib/familia/utils.rb +123 -90
  45. data/lib/familia/version.rb +4 -21
  46. data/lib/familia.rb +18 -13
  47. data/lib/middleware/database_middleware.rb +150 -0
  48. data/try/configuration/scenarios_try.rb +65 -0
  49. data/try/core/connection_try.rb +58 -0
  50. data/try/core/errors_try.rb +93 -0
  51. data/try/core/extensions_try.rb +26 -0
  52. data/try/{10_familia_try.rb → core/familia_extended_try.rb} +11 -10
  53. data/try/{00_familia_try.rb → core/familia_try.rb} +7 -5
  54. data/try/core/middleware_try.rb +68 -0
  55. data/try/core/refinements_try.rb +39 -0
  56. data/try/core/settings_try.rb +76 -0
  57. data/try/core/tools_try.rb +54 -0
  58. data/try/core/utils_try.rb +189 -0
  59. data/try/{26_redis_bool_try.rb → datatypes/boolean_try.rb} +4 -2
  60. data/try/datatypes/datatype_base_try.rb +69 -0
  61. data/try/{25_redis_type_hash_try.rb → datatypes/hash_try.rb} +5 -3
  62. data/try/{23_redis_type_list_try.rb → datatypes/list_try.rb} +5 -3
  63. data/try/{22_redis_type_set_try.rb → datatypes/set_try.rb} +5 -3
  64. data/try/{21_redis_type_zset_try.rb → datatypes/sorted_set_try.rb} +6 -4
  65. data/try/{24_redis_type_string_try.rb → datatypes/string_try.rb} +8 -8
  66. data/try/edge_cases/empty_identifiers_try.rb +48 -0
  67. data/try/{92_symbolize_try.rb → edge_cases/hash_symbolization_try.rb} +12 -7
  68. data/try/edge_cases/json_serialization_try.rb +85 -0
  69. data/try/edge_cases/race_conditions_try.rb +60 -0
  70. data/try/edge_cases/reserved_keywords_try.rb +59 -0
  71. data/try/{93_string_coercion_try.rb → edge_cases/string_coercion_try.rb} +60 -59
  72. data/try/edge_cases/ttl_side_effects_try.rb +51 -0
  73. data/try/features/expiration_try.rb +86 -0
  74. data/try/features/quantization_try.rb +90 -0
  75. data/try/{35_feature_safedump_try.rb → features/safe_dump_advanced_try.rb} +7 -6
  76. data/try/features/safe_dump_try.rb +137 -0
  77. data/try/{test_helpers.rb → helpers/test_helpers.rb} +25 -60
  78. data/try/{27_redis_horreum_try.rb → horreum/base_try.rb} +39 -14
  79. data/try/horreum/class_methods_try.rb +41 -0
  80. data/try/horreum/commands_try.rb +49 -0
  81. data/try/{29_redis_horreum_initialization_try.rb → horreum/initialization_try.rb} +9 -7
  82. data/try/horreum/relations_try.rb +146 -0
  83. data/try/{28_redis_horreum_serialization_try.rb → horreum/serialization_try.rb} +13 -11
  84. data/try/horreum/settings_try.rb +43 -0
  85. data/try/integration/cross_component_try.rb +46 -0
  86. data/try/{41_customer_safedump_try.rb → models/customer_safe_dump_try.rb} +9 -7
  87. data/try/{40_customer_try.rb → models/customer_try.rb} +21 -18
  88. data/try/models/datatype_base_try.rb +100 -0
  89. data/try/{30_familia_object_try.rb → models/familia_object_try.rb} +18 -16
  90. data/try/performance/benchmarks_try.rb +55 -0
  91. data/try/pooling/README.md +20 -0
  92. data/try/pooling/configurable_stress_test_try.rb +435 -0
  93. data/try/pooling/connection_pool_test_try.rb +273 -0
  94. data/try/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  95. data/try/pooling/lib/connection_pool_metrics.rb +372 -0
  96. data/try/pooling/lib/connection_pool_stress_test.rb +959 -0
  97. data/try/pooling/lib/connection_pool_threading_models.rb +421 -0
  98. data/try/pooling/lib/visualize_stress_results.rb +434 -0
  99. data/try/pooling/pool_siege_try.rb +509 -0
  100. data/try/pooling/run_stress_tests_try.rb +482 -0
  101. data/try/prototypes/atomic_saves_v1_context_proxy.rb +121 -0
  102. data/try/prototypes/atomic_saves_v2_connection_switching.rb +161 -0
  103. data/try/prototypes/atomic_saves_v3_connection_pool.rb +189 -0
  104. data/try/prototypes/atomic_saves_v4.rb +105 -0
  105. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +124 -0
  106. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  107. metadata +143 -46
  108. data/.github/workflows/ruby.yml +0 -71
  109. data/VERSION.yml +0 -4
  110. data/lib/familia/redistype/commands.rb +0 -59
  111. data/lib/familia/redistype.rb +0 -228
  112. data/lib/familia/tools.rb +0 -68
  113. data/lib/redis_middleware.rb +0 -109
  114. data/try/20_redis_type_try.rb +0 -70
  115. data/try/91_json_bug_try.rb +0 -86
@@ -1,19 +1,18 @@
1
- # rubocop:disable all
2
- # frozen_string_literal: true
1
+ # lib/familia/features/expiration.rb
3
2
 
4
3
 
5
4
  module Familia::Features
6
5
 
7
6
  module Expiration
8
- @ttl = nil
7
+ @default_expiration = nil
9
8
 
10
9
  module ClassMethods
11
10
 
12
- attr_writer :ttl
11
+ attr_writer :default_expiration
13
12
 
14
- def ttl(v = nil)
15
- @ttl = v.to_f unless v.nil?
16
- @ttl || parent&.ttl || Familia.ttl
13
+ def default_expiration(v = nil)
14
+ @default_expiration = v.to_f unless v.nil?
15
+ @default_expiration || parent&.default_expiration || Familia.default_expiration
17
16
  end
18
17
 
19
18
  end
@@ -22,51 +21,51 @@ module Familia::Features
22
21
  Familia.ld "[#{base}] Loaded #{self}"
23
22
  base.extend ClassMethods
24
23
 
25
- # Optionally define ttl in the class to make
24
+ # Optionally define default_expiration in the class to make
26
25
  # sure we always have an array to work with.
27
- unless base.instance_variable_defined?(:@ttl)
28
- base.instance_variable_set(:@ttl, @ttl) # set above
26
+ unless base.instance_variable_defined?(:@default_expiration)
27
+ base.instance_variable_set(:@default_expiration, @default_expiration) # set above
29
28
  end
30
29
  end
31
30
 
32
- def ttl=(v)
33
- @ttl = v.to_f
31
+ def default_expiration=(v)
32
+ @default_expiration = v.to_f
34
33
  end
35
34
 
36
- def ttl
37
- @ttl || self.class.ttl
35
+ def default_expiration
36
+ @default_expiration || self.class.default_expiration
38
37
  end
39
38
 
40
- # Sets an expiration time for the Redis data associated with this object.
39
+ # Sets an expiration time for the Database data associated with this object.
41
40
  #
42
41
  # This method allows setting a Time To Live (TTL) for the data in Redis,
43
42
  # after which it will be automatically removed.
44
43
  #
45
- # @param ttl [Integer, nil] The Time To Live in seconds. If nil, the default
44
+ # @param default_expiration [Integer, nil] The Time To Live in seconds. If nil, the default
46
45
  # TTL will be used.
47
46
  #
48
47
  # @return [Boolean] Returns true if the expiration was set successfully,
49
48
  # false otherwise.
50
49
  #
51
50
  # @example Setting an expiration of one day
52
- # object.update_expiration(ttl: 86400)
51
+ # object.update_expiration(default_expiration: 86400)
53
52
  #
54
- # @note If TTL is set to zero, the expiration will be removed, making the
53
+ # @note If Default expiration is set to zero, the expiration will be removed, making the
55
54
  # data persist indefinitely.
56
55
  #
57
- # @raise [Familia::Problem] Raises an error if the TTL is not a non-negative
56
+ # @raise [Familia::Problem] Raises an error if the default expiration is not a non-negative
58
57
  # integer.
59
58
  #
60
- def update_expiration(ttl: nil)
61
- ttl ||= self.ttl
59
+ def update_expiration(default_expiration: nil)
60
+ default_expiration ||= self.default_expiration
62
61
 
63
62
  if self.class.has_relations?
64
- Familia.ld "[update_expiration] #{self.class} has relations: #{self.class.redis_types.keys}"
65
- self.class.redis_types.each do |name, definition|
66
- next if definition.opts[:ttl].nil?
63
+ Familia.ld "[update_expiration] #{self.class} has relations: #{self.class.related_fields.keys}"
64
+ self.class.related_fields.each do |name, definition|
65
+ next if definition.opts[:default_expiration].nil?
67
66
  obj = send(name)
68
- Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.rediskey}) to #{ttl}"
69
- obj.update_expiration(ttl: ttl)
67
+ Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.dbkey}) to #{default_expiration}"
68
+ obj.update_expiration(default_expiration: default_expiration)
70
69
  end
71
70
  end
72
71
 
@@ -75,23 +74,23 @@ module Familia::Features
75
74
  # retention issues (e.g. not removed in a timely fashion).
76
75
  #
77
76
  # For the same reason, we don't want to default to 0 bc there's not a
78
- # good reason for the ttl to not be set in the first place. If the
79
- # class doesn't have a ttl, the default comes from Familia.ttl (which
77
+ # good reason for the default_expiration to not be set in the first place. If the
78
+ # class doesn't have a default_expiration, the default comes from Familia.default_expiration (which
80
79
  # is 0).
81
- unless ttl.is_a?(Numeric)
82
- raise Familia::Problem, "TTL must be a number (#{ttl.class} in #{self.class})"
80
+ unless default_expiration.is_a?(Numeric)
81
+ raise Familia::Problem, "Default expiration must be a number (#{default_expiration.class} in #{self.class})"
83
82
  end
84
83
 
85
- if ttl.zero?
86
- return Familia.ld "[update_expiration] No expiration for #{self.class} (#{rediskey})"
84
+ if default_expiration.zero?
85
+ return Familia.ld "[update_expiration] No expiration for #{self.class} (#{dbkey})"
87
86
  end
88
87
 
89
- Familia.ld "[update_expiration] Expires #{rediskey} in #{ttl} seconds"
88
+ Familia.ld "[update_expiration] Expires #{dbkey} in #{default_expiration} seconds"
90
89
 
91
90
  # Redis' EXPIRE command returns 1 if the timeout was set, 0 if key does
92
91
  # not exist or the timeout could not be set. Via redis-rb here, it's
93
92
  # a bool.
94
- expire(ttl)
93
+ expire(default_expiration)
95
94
  end
96
95
 
97
96
  extend ClassMethods
@@ -1,4 +1,4 @@
1
- # rubocop:disable all
1
+ # lib/familia/features/quantization.rb
2
2
 
3
3
  module Familia::Features
4
4
 
@@ -28,7 +28,13 @@ module Familia::Features
28
28
  if quantum.is_a?(Array)
29
29
  quantum, pattern = quantum
30
30
  end
31
- quantum ||= @opts[:quantize] || ttl || 10.minutes
31
+
32
+ # Previously we erronously included `@opts.fetch(:quantize, nil)` in
33
+ # the list of default values here, but @opts is for horreum instances
34
+ # not at the class level. This method `qstamp` is part of the initial
35
+ # definition for whatever horreum subclass we're in right now. That's
36
+ # why default_expiration works (e.g. `class Plop; feature :quantization; default_expiration 90; end`).
37
+ quantum ||= default_expiration || 10.minutes
32
38
 
33
39
  # Validate quantum
34
40
  unless quantum.is_a?(Numeric) && quantum.positive?
@@ -46,7 +52,7 @@ module Familia::Features
46
52
  end
47
53
 
48
54
  def qstamp(quantum = nil, pattern: nil, time: nil)
49
- self.class.qstamp(quantum || self.class.ttl, pattern: pattern, time: time)
55
+ self.class.qstamp(quantum || self.class.default_expiration, pattern: pattern, time: time)
50
56
  end
51
57
 
52
58
  extend ClassMethods
@@ -1,5 +1,4 @@
1
- # rubocop:disable all
2
- # frozen_string_literal: true
1
+ # lib/familia/features/safe_dump.rb
3
2
 
4
3
 
5
4
  module Familia::Features
@@ -87,7 +86,7 @@ module Familia::Features
87
86
  field_name = el
88
87
  callable = lambda { |obj|
89
88
  if obj.respond_to?(:[]) && obj[field_name]
90
- obj[field_name] # Familia::RedisType classes
89
+ obj[field_name] # Familia::DataType classes
91
90
  elsif obj.respond_to?(field_name)
92
91
  obj.send(field_name) # Onetime::Models::RedisHash classes via method_missing 😩
93
92
  end
@@ -1,4 +1,4 @@
1
- # rubocop:disable all
1
+ # lib/familia/features.rb
2
2
 
3
3
  module Familia
4
4
 
@@ -28,7 +28,7 @@ module Familia
28
28
  # Extend the Familia::Base subclass (e.g. Customer) with the feature module
29
29
  include klass
30
30
 
31
- # NOTE: We may also want to extend Familia::RedisType here so that we can
31
+ # NOTE: We may also want to extend Familia::DataType here so that we can
32
32
  # call safe_dump on relations fields (e.g. list, set, zset, hashkey). Or
33
33
  # maybe that only makes sense for hashk/object relations.
34
34
  #
@@ -1,62 +1,61 @@
1
- # frozen_string_literal: true
1
+ # lib/familia/horreum/class_methods.rb
2
2
 
3
- require_relative 'relations_management'
3
+ require_relative 'related_fields_management'
4
4
 
5
5
  module Familia
6
6
  class Horreum
7
7
  # Class-level instance variables
8
8
  # These are set up as nil initially and populated later
9
- @redis = nil
10
- @identifier = nil
11
- @ttl = nil
12
- @db = nil
9
+ @dbclient = nil # TODO
10
+ @identifier_field = nil
11
+ @default_expiration = nil
12
+ @logical_database = nil
13
13
  @uri = nil
14
14
  @suffix = nil
15
15
  @prefix = nil
16
16
  @fields = nil # []
17
- @class_redis_types = nil # {}
18
- @redis_types = nil # {}
17
+ @class_related_fields = nil # {}
18
+ @related_fields = nil # {}
19
19
  @dump_method = nil
20
20
  @load_method = nil
21
21
 
22
22
  # ClassMethods: Provides class-level functionality for Horreum
23
23
  #
24
24
  # This module is extended into classes that include Familia::Horreum,
25
- # providing methods for Redis operations and object management.
25
+ # providing methods for Database operations and object management.
26
26
  #
27
27
  # Key features:
28
- # * Includes RelationsManagement for Redis-type field handling
29
- # * Defines methods for managing fields, identifiers, and Redis keys
30
- # * Provides utility methods for working with Redis objects
28
+ # * Includes RelatedFieldsManagement for DataType field handling
29
+ # * Defines methods for managing fields, identifiers, and dbkeys
30
+ # * Provides utility methods for working with Database objects
31
31
  #
32
32
  module ClassMethods
33
33
  include Familia::Settings
34
- include Familia::Horreum::RelationsManagement
35
-
36
- # Returns the Redis connection for the class.
37
- #
38
- # This method retrieves the Redis connection instance for the class. If no
39
- # connection is set, it initializes a new connection using the provided URI
40
- # or database configuration.
41
- #
42
- # @return [Redis] the Redis connection instance.
43
- #
44
- def redis
45
- @redis || Familia.redis(uri || db)
46
- end
47
-
48
- # Sets or retrieves the unique identifier for the class.
49
- #
50
- # This method defines or returns the unique identifier used to generate the
51
- # Redis key for the object. If a value is provided, it sets the identifier;
52
- # otherwise, it returns the current identifier.
53
- #
54
- # @param [Object] val the value to set as the identifier (optional).
55
- # @return [Object] the current identifier.
56
- #
57
- def identifier(val = nil)
58
- @identifier = val if val
59
- @identifier
34
+ include Familia::Horreum::RelatedFieldsManagement
35
+
36
+ # Sets or retrieves the unique identifier field for the class.
37
+ #
38
+ # This method defines or returns the field or method that contains the unique
39
+ # identifier used to generate the dbkey for the object. If a value is provided,
40
+ # it sets the identifier field; otherwise, it returns the current identifier field.
41
+ #
42
+ # @param [Object] val the field name or method to set as the identifier field (optional).
43
+ # @return [Object] the current identifier field.
44
+ #
45
+ def identifier_field(val = nil)
46
+ if val
47
+ # Validate identifier field definition at class definition time
48
+ case val
49
+ when Symbol, String, Proc
50
+ @identifier_field = val
51
+ else
52
+ raise Problem, <<~ERROR
53
+ Invalid identifier field definition: #{val.inspect}.
54
+ Use a field name (Symbol/String) or Proc.
55
+ ERROR
56
+ end
57
+ end
58
+ @identifier_field
60
59
  end
61
60
 
62
61
  # Defines a field for the class and creates accessor methods.
@@ -84,12 +83,12 @@ module Familia
84
83
  # The dynamically defined method performs the following:
85
84
  # - Acts as both a reader and a writer method.
86
85
  # - When called without arguments, retrieves the current value from Redis.
87
- # - When called with an argument, persists the value to Redis immediately.
86
+ # - When called with an argument, persists the value to Database immediately.
88
87
  # - Checks if the correct number of arguments is provided (zero or one).
89
- # - Converts the provided value to a format suitable for Redis storage.
88
+ # - Converts the provided value to a format suitable for Database storage.
90
89
  # - Uses the existing accessor method to set the attribute value when
91
90
  # writing.
92
- # - Persists the value to Redis immediately using the hset command when
91
+ # - Persists the value to Database immediately using the hset command when
93
92
  # writing.
94
93
  # - Includes custom error handling to raise an ArgumentError if the wrong
95
94
  # number of arguments is given.
@@ -145,21 +144,21 @@ module Familia
145
144
  val = args.first
146
145
 
147
146
  # If no value is provided to this fast attribute method, make a call
148
- # to redis to return the current stored value of the hash field.
147
+ # to the db to return the current stored value of the hash field.
149
148
  return hget name if val.nil?
150
149
 
151
150
  begin
152
151
  # Trace the operation if debugging is enabled.
153
- Familia.trace :FAST_WRITER, redis, "#{name}: #{val.inspect}", caller(1..1) if Familia.debug?
152
+ Familia.trace :FAST_WRITER, dbclient, "#{name}: #{val.inspect}", caller(1..1) if Familia.debug?
154
153
 
155
- # Convert the provided value to a format suitable for Redis storage.
154
+ # Convert the provided value to a format suitable for Database storage.
156
155
  prepared = serialize_value(val)
157
156
  Familia.ld "[.fast_attribute!] #{name} val: #{val.class} prepared: #{prepared.class}"
158
157
 
159
158
  # Use the existing accessor method to set the attribute value.
160
159
  send :"#{name}=", val
161
160
 
162
- # Persist the value to Redis immediately using the hset command.
161
+ # Persist the value to Database immediately using the hset command.
163
162
  hset name, prepared
164
163
  rescue Familia::Problem => e
165
164
  # Raise a custom error message if an exception occurs during the execution of the method.
@@ -175,36 +174,24 @@ module Familia
175
174
  @fields
176
175
  end
177
176
 
178
- def class_redis_types
179
- @class_redis_types ||= {}
180
- @class_redis_types
177
+ def class_related_fields
178
+ @class_related_fields ||= {}
179
+ @class_related_fields
181
180
  end
182
181
 
183
- def class_redis_types?(name)
184
- class_redis_types.key? name.to_s.to_sym
185
- end
186
-
187
- def redis_object?(name)
188
- redis_types.key? name.to_s.to_sym
189
- end
190
-
191
- def redis_types
192
- @redis_types ||= {}
193
- @redis_types
182
+ def related_fields
183
+ @related_fields ||= {}
184
+ @related_fields
194
185
  end
195
186
 
196
187
  def has_relations?
197
188
  @has_relations ||= false
198
189
  end
199
190
 
200
- def db(v = nil)
201
- @db = v unless v.nil?
202
- @db || parent&.db
203
- end
204
-
205
- def uri(v = nil)
206
- @uri = v unless v.nil?
207
- @uri || parent&.uri
191
+ def logical_database(v = nil)
192
+ Familia.trace :DB, Familia.dbclient, "#{@logical_database} #{v}", caller(1..1) if Familia.debug?
193
+ @logical_database = v unless v.nil?
194
+ @logical_database || parent&.logical_database
208
195
  end
209
196
 
210
197
  def all(suffix = nil)
@@ -217,11 +204,11 @@ module Familia
217
204
  matching_keys_count(filter) > 0
218
205
  end
219
206
 
220
- # Returns the number of Redis keys matching the given filter pattern
221
- # @param filter [String] Redis key pattern to match (default: '*')
207
+ # Returns the number of dbkeys matching the given filter pattern
208
+ # @param filter [String] dbkey pattern to match (default: '*')
222
209
  # @return [Integer] Number of matching keys
223
210
  def matching_keys_count(filter = '*')
224
- redis.keys(rediskey(filter)).compact.size
211
+ dbclient.keys(dbkey(filter)).compact.size
225
212
  end
226
213
  alias size matching_keys_count # For backwards compatibility
227
214
 
@@ -273,7 +260,7 @@ module Familia
273
260
  #
274
261
  def create *args, **kwargs
275
262
  fobj = new(*args, **kwargs)
276
- raise Familia::Problem, "#{self} already exists: #{fobj.rediskey}" if fobj.exists?
263
+ raise Familia::Problem, "#{self} already exists: #{fobj.dbkey}" if fobj.exists?
277
264
 
278
265
  fobj.save
279
266
  fobj
@@ -285,17 +272,17 @@ module Familia
285
272
  end
286
273
 
287
274
  def rawmultiget(*ids)
288
- ids.collect! { |objid| rediskey(objid) }
275
+ ids.collect! { |objid| dbkey(objid) }
289
276
  return [] if ids.compact.empty?
290
277
 
291
- Familia.trace :MULTIGET, redis, "#{ids.size}: #{ids}", caller(1..1) if Familia.debug?
292
- redis.mget(*ids)
278
+ Familia.trace :MULTIGET, dbclient, "#{ids.size}: #{ids}", caller(1..1) if Familia.debug?
279
+ dbclient.mget(*ids)
293
280
  end
294
281
 
295
- # Retrieves and instantiates an object from Redis using the full object
282
+ # Retrieves and instantiates an object from Database using the full object
296
283
  # key.
297
284
  #
298
- # @param objkey [String] The full Redis key for the object.
285
+ # @param objkey [String] The full dbkey for the object.
299
286
  # @return [Object, nil] An instance of the class if the key exists, nil
300
287
  # otherwise.
301
288
  # @raise [ArgumentError] If the provided key is empty.
@@ -325,10 +312,10 @@ module Familia
325
312
 
326
313
  # We use a lower-level method here b/c we're working with the
327
314
  # full key and not just the identifier.
328
- does_exist = redis.exists(objkey).positive?
315
+ does_exist = dbclient.exists(objkey).positive?
329
316
 
330
317
  Familia.ld "[.find_by_key] #{self} from key #{objkey} (exists: #{does_exist})"
331
- Familia.trace :FROM_KEY, redis, objkey, caller(1..1) if Familia.debug?
318
+ Familia.trace :FROM_KEY, dbclient, objkey, caller(1..1) if Familia.debug?
332
319
 
333
320
  # This is the reason for calling exists first. We want to definitively
334
321
  # and without any ambiguity know if the object exists in Redis. If it
@@ -337,22 +324,22 @@ module Familia
337
324
  # the constructor, which will then be annoying to debug.
338
325
  return unless does_exist
339
326
 
340
- obj = redis.hgetall(objkey) # horreum objects are persisted as redis hashes
341
- Familia.trace :FROM_KEY2, redis, "#{objkey}: #{obj.inspect}", caller(1..1) if Familia.debug?
327
+ obj = dbclient.hgetall(objkey) # horreum objects are persisted as database hashes
328
+ Familia.trace :FROM_KEY2, dbclient, "#{objkey}: #{obj.inspect}", caller(1..1) if Familia.debug?
342
329
 
343
330
  new(**obj)
344
331
  end
345
- alias from_rediskey find_by_key # deprecated
332
+ alias from_dbkey find_by_key # deprecated
346
333
 
347
- # Retrieves and instantiates an object from Redis using its identifier.
334
+ # Retrieves and instantiates an object from Database using its identifier.
348
335
  #
349
336
  # @param identifier [String, Integer] The unique identifier for the
350
337
  # object.
351
- # @param suffix [Symbol] The suffix to use in the Redis key (default:
338
+ # @param suffix [Symbol] The suffix to use in the dbkey (default:
352
339
  # :object).
353
340
  # @return [Object, nil] An instance of the class if found, nil otherwise.
354
341
  #
355
- # This method constructs the full Redis key using the provided identifier
342
+ # This method constructs the full dbkey using the provided identifier
356
343
  # and suffix, then delegates to `find_by_key` for the actual retrieval and
357
344
  # instantiation.
358
345
  #
@@ -367,10 +354,10 @@ module Familia
367
354
  suffix ||= self.suffix
368
355
  return nil if identifier.to_s.empty?
369
356
 
370
- objkey = rediskey(identifier, suffix)
357
+ objkey = dbkey(identifier, suffix)
371
358
 
372
359
  Familia.ld "[.find_by_id] #{self} from key #{objkey})"
373
- Familia.trace :FIND_BY_ID, Familia.redis(uri), objkey, caller(1..1).first if Familia.debug?
360
+ Familia.trace :FIND_BY_ID, Familia.dbclient(uri), objkey, caller(1..1).first if Familia.debug?
374
361
  find_by_key objkey
375
362
  end
376
363
  alias find find_by_id
@@ -380,10 +367,10 @@ module Familia
380
367
  # Checks if an object with the given identifier exists in Redis.
381
368
  #
382
369
  # @param identifier [String, Integer] The unique identifier for the object.
383
- # @param suffix [Symbol, nil] The suffix to use in the Redis key (default: class suffix).
370
+ # @param suffix [Symbol, nil] The suffix to use in the dbkey (default: class suffix).
384
371
  # @return [Boolean] true if the object exists, false otherwise.
385
372
  #
386
- # This method constructs the full Redis key using the provided identifier and suffix,
373
+ # This method constructs the full dbkey using the provided identifier and suffix,
387
374
  # then checks if the key exists in Redis.
388
375
  #
389
376
  # @example
@@ -393,24 +380,24 @@ module Familia
393
380
  suffix ||= self.suffix
394
381
  return false if identifier.to_s.empty?
395
382
 
396
- objkey = rediskey identifier, suffix
383
+ objkey = dbkey identifier, suffix
397
384
 
398
- ret = redis.exists objkey
399
- Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
385
+ ret = dbclient.exists objkey
386
+ Familia.trace :EXISTS, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
400
387
 
401
- ret.positive? # differs from redis API but I think it's okay bc `exists?` is a predicate method.
388
+ ret.positive? # differs from Valkey API but I think it's okay bc `exists?` is a predicate method.
402
389
  end
403
390
 
404
- # Destroys an object in Redis with the given identifier.
391
+ # Destroys an object in Database with the given identifier.
405
392
  #
406
393
  # @param identifier [String, Integer] The unique identifier for the object to destroy.
407
- # @param suffix [Symbol, nil] The suffix to use in the Redis key (default: class suffix).
394
+ # @param suffix [Symbol, nil] The suffix to use in the dbkey (default: class suffix).
408
395
  # @return [Boolean] true if the object was successfully destroyed, false otherwise.
409
396
  #
410
397
  # This method is part of Familia's high-level object lifecycle management. While `delete!`
411
- # operates directly on Redis keys, `destroy!` operates at the object level and is used for
398
+ # operates directly on dbkeys, `destroy!` operates at the object level and is used for
412
399
  # ORM-style operations. Use `destroy!` when removing complete objects from the system, and
413
- # `delete!` when working directly with Redis keys.
400
+ # `delete!` when working directly with dbkeys.
414
401
  #
415
402
  # @example
416
403
  # User.destroy!(123) # Removes user:123:object from Redis
@@ -419,27 +406,27 @@ module Familia
419
406
  suffix ||= self.suffix
420
407
  return false if identifier.to_s.empty?
421
408
 
422
- objkey = rediskey identifier, suffix
409
+ objkey = dbkey identifier, suffix
423
410
 
424
- ret = redis.del objkey
425
- Familia.trace :DESTROY!, redis, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
411
+ ret = dbclient.del objkey
412
+ Familia.trace :DESTROY!, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
426
413
  ret.positive?
427
414
  end
428
415
 
429
- # Finds all keys in Redis matching the given suffix pattern.
416
+ # Finds all keys in Database matching the given suffix pattern.
430
417
  #
431
418
  # @param suffix [String] The suffix pattern to match (default: '*').
432
- # @return [Array<String>] An array of matching Redis keys.
419
+ # @return [Array<String>] An array of matching dbkeys.
433
420
  #
434
- # This method searches for all Redis keys that match the given suffix pattern.
435
- # It uses the class's rediskey method to construct the search pattern.
421
+ # This method searches for all dbkeys that match the given suffix pattern.
422
+ # It uses the class's dbkey method to construct the search pattern.
436
423
  #
437
424
  # @example
438
425
  # User.find # Returns all keys matching user:*:object
439
426
  # User.find('active') # Returns all keys matching user:*:active
440
427
  #
441
428
  def find_keys(suffix = '*')
442
- redis.keys(rediskey('*', suffix)) || []
429
+ dbclient.keys(dbkey('*', suffix)) || []
443
430
  end
444
431
 
445
432
  # +identifier+ can be a value or an Array of values used to create the index.
@@ -449,15 +436,15 @@ module Familia
449
436
  # +suffix+ If a nil value is explicitly passed in, it won't appear in the redis
450
437
  # key that's returned. If no suffix is passed in, the class' suffix is used
451
438
  # as the default (via the class method self.suffix). It's an important
452
- # distinction b/c passing in an explicitly nil is how RedisType objects
439
+ # distinction b/c passing in an explicitly nil is how DataType objects
453
440
  # at the class level are created without the global default 'object'
454
- # suffix. See RedisType#rediskey "parent_class?" for more details.
455
- def rediskey(identifier, suffix = self.suffix)
456
- # Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})"
441
+ # suffix. See DataType#dbkey "parent_class?" for more details.
442
+ def dbkey(identifier, suffix = self.suffix)
443
+ # Familia.ld "[.dbkey] #{identifier} for #{self} (suffix:#{suffix})"
457
444
  raise NoIdentifier, self if identifier.to_s.empty?
458
445
 
459
446
  identifier &&= identifier.to_s
460
- Familia.rediskey(prefix, identifier, suffix)
447
+ Familia.dbkey(prefix, identifier, suffix)
461
448
  end
462
449
 
463
450
  def dump_method
@@ -468,6 +455,6 @@ module Familia
468
455
  @load_method || :from_json # Familia.load_method
469
456
  end
470
457
  end
471
- # End of ClassMethods module
458
+
472
459
  end
473
460
  end