familia 1.2.3 → 2.0.0.pre.pre

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 +3 -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 +15 -2
  11. data/Gemfile.lock +61 -61
  12. data/README.md +39 -23
  13. data/bin/irb +3 -0
  14. data/docs/connection_pooling.md +317 -0
  15. data/familia.gemspec +8 -5
  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 -130
  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 +17 -12
  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} +5 -3
  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 -8
  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} +63 -60
  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} +20 -17
  88. data/try/models/datatype_base_try.rb +101 -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 +124 -38
  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,4 +1,4 @@
1
- # rubocop:disable all
1
+ # lib/familia/horreum/serialization.rb
2
2
  #
3
3
  module Familia
4
4
 
@@ -6,9 +6,9 @@ module Familia
6
6
  # Familia::Horreum
7
7
  #
8
8
  class Horreum
9
- # The Sacred Scrolls of Redis Responses
9
+ # The Sacred Scrolls of Database Responses
10
10
  #
11
- # Behold! The mystical runes that Redis whispers back to us:
11
+ # Behold! The mystical runes that Database whispers back to us:
12
12
  #
13
13
  # "OK" - The sweet sound of success, like a tiny "ding!" from the depths of data.
14
14
  # true - The boolean Buddha nods in agreement.
@@ -16,27 +16,27 @@ module Familia
16
16
  # 0 - The silent hero. It tried its best, bless its heart.
17
17
  # nil - The zen master of responses. It's not nothing, it's... enlightenment!
18
18
  #
19
- # These sacred signs are our guide through the Redis wilderness. When we cast
19
+ # These sacred signs are our guide through the Database wilderness. When we cast
20
20
  # our spells (er, commands), we seek these friendly faces in the returned
21
21
  # smoke signals.
22
22
  #
23
- # Should our Redis rituals summon anything else, we pause. We ponder. We
23
+ # Should our Database rituals summon anything else, we pause. We ponder. We
24
24
  # possibly panic. For the unexpected in Redis-land is like finding a penguin
25
25
  # in your pasta - delightfully confusing, but probably not what you ordered.
26
26
  #
27
- # May your Redis returns be ever valid, and your data ever flowing!
27
+ # May your Database returns be ever valid, and your data ever flowing!
28
28
  #
29
- @valid_command_return_values = ["OK", true, 1, 0, nil]
29
+ @valid_command_return_values = ["OK", true, 1, 0, nil].freeze
30
30
 
31
31
  class << self
32
- attr_accessor :valid_command_return_values
32
+ attr_reader :valid_command_return_values
33
33
  end
34
34
 
35
35
  # Serialization: Where Objects Go to Become Strings (and Vice Versa)!
36
36
  #
37
37
  # This module is chock-full of methods that'll make your head spin (in a
38
38
  # good way)! We've got loaders, dumpers, and refreshers galore. It's like
39
- # a laundromat for your data, but instead of quarters, it runs on Redis commands.
39
+ # a laundromat for your data, but instead of quarters, it runs on Database commands.
40
40
  #
41
41
  # A Note on Our Refreshing Refreshers:
42
42
  # In the wild world of Ruby, '!' usually means "Watch out! I'm dangerous!"
@@ -47,7 +47,7 @@ module Familia
47
47
  #
48
48
  # Remember: In Familia, refreshing isn't just a chore, it's a chance to
49
49
  # dance with data! Whether you bang(!) or not, you're still invited to
50
- # the Redis disco.
50
+ # the Database disco.
51
51
  #
52
52
  # (P.S. If you're reading these docs, lol sorry. I asked Claude 3.5 to
53
53
  # write in the style of _why the lucky stiff today and got this uncanny
@@ -56,59 +56,17 @@ module Familia
56
56
  #
57
57
  # (Ahem! What I meant to say was that if you're reading this, congratulations!
58
58
  # You've stumbled upon the secret garden of documentation. Feel free to smell
59
- # the Ruby roses, but watch out for the Redis thorns!)
59
+ # the Ruby roses, but watch out for the Database thorns!)
60
60
  #
61
61
  module Serialization
62
62
 
63
- attr_writer :redis
64
-
65
- # Summon the mystical Redis connection from the depths of instance or class.
66
- #
67
- # This method is like a magical divining rod, always pointing to the nearest
68
- # source of Redis goodness. It first checks if we have a personal Redis
69
- # connection (@redis), and if not, it borrows the class's connection.
70
- #
71
- # @return [Redis] A shimmering Redis connection, ready for your bidding.
72
- #
73
- # @example Finding your Redis way
74
- # puts object.redis
75
- # # => #<Redis client v4.5.1 for redis://localhost:6379/0>
76
- #
77
- def redis
78
- @redis || self.class.redis
79
- end
80
-
81
- # Perform a sacred Redis transaction ritual.
82
- #
83
- # This method creates a protective circle around your Redis operations,
84
- # ensuring they all succeed or fail together. It's like a group hug for your
85
- # data operations, but with more ACID properties.
86
- #
87
- # @yield [conn] A block where you can perform your Redis incantations.
88
- # @yieldparam conn [Redis] A Redis connection in multi mode.
89
- #
90
- # @example Performing a Redis rain dance
91
- # transaction do |conn|
92
- # conn.set("weather", "rainy")
93
- # conn.set("mood", "melancholic")
94
- # end
95
- #
96
- # @note This method temporarily replaces your Redis connection with a multi
97
- # connection. Don't worry, it puts everything back where it found it when it's done.
98
- #
99
- def transaction
100
- redis.multi do |conn|
101
- yield(conn)
102
- end
103
- end
104
-
105
63
  # Save our precious data to Redis, with a sprinkle of timestamp magic!
106
64
  #
107
65
  # This method is like a conscientious historian, not only recording your
108
66
  # object's current state but also meticulously timestamping when it was
109
67
  # created and last updated. It's the record keeper of your data's life story!
110
68
  #
111
- # @return [Boolean] true if the save was successful, false if Redis was grumpy.
69
+ # @return [Boolean] true if the save was successful, false if Database was grumpy.
112
70
  #
113
71
  # @example Preserving your pet rock for posterity
114
72
  # rocky = PetRock.new(name: "Dwayne")
@@ -119,26 +77,23 @@ module Familia
119
77
  # It's like Hansel and Gretel, but for data operations!
120
78
  #
121
79
  def save update_expiration: true
122
- Familia.trace :SAVE, redis, redisuri, caller(1..1) if Familia.debug?
80
+ Familia.trace :SAVE, dbclient, uri, caller(1..1) if Familia.debug?
123
81
 
124
- # Update our object's life story, keeping the mandatory built-in
125
- # key field in sync with the field that is the chosen identifier.
126
- self.key = self.identifier
82
+ # No longer need to sync computed identifier with a cache field
127
83
  self.created ||= Familia.now.to_i if respond_to?(:created)
128
84
  self.updated = Familia.now.to_i if respond_to?(:updated)
129
85
 
130
- # Commit our tale to the Redis chronicles
86
+ # Commit our tale to the Database chronicles
131
87
  #
132
- # e.g. `ret` # => MultiResult.new(true, ["OK", "OK"])
133
88
  ret = commit_fields(update_expiration: update_expiration)
134
89
 
135
- Familia.ld "[save] #{self.class} #{rediskey} #{ret} (update_expiration: #{update_expiration})"
90
+ Familia.ld "[save] #{self.class} #{dbkey} #{ret} (update_expiration: #{update_expiration})"
136
91
 
137
- # Did Redis accept our offering?
138
- ret.successful?
92
+ # Did Database accept our offering?
93
+ !ret.nil?
139
94
  end
140
95
 
141
- # Updates multiple fields atomically in a Redis transaction.
96
+ # Updates multiple fields atomically in a Database transaction.
142
97
  #
143
98
  # @param fields [Hash] Field names and values to update. Special key :update_expiration
144
99
  # controls whether to update key expiration (default: true)
@@ -154,19 +109,19 @@ module Familia
154
109
  update_expiration = kwargs.delete(:update_expiration) { true }
155
110
  fields = kwargs
156
111
 
157
- Familia.trace :BATCH_UPDATE, redis, fields.keys, caller(1..1) if Familia.debug?
112
+ Familia.trace :BATCH_UPDATE, dbclient, fields.keys, caller(1..1) if Familia.debug?
158
113
 
159
114
  command_return_values = transaction do |conn|
160
115
  fields.each do |field, value|
161
116
  prepared_value = serialize_value(value)
162
- conn.hset rediskey, field, prepared_value
117
+ conn.hset dbkey, field, prepared_value
163
118
  # Update instance variable to keep object in sync
164
119
  send("#{field}=", value) if respond_to?("#{field}=")
165
120
  end
166
121
  end
167
122
 
168
123
  # Update expiration if requested and supported
169
- self.update_expiration(ttl: nil) if update_expiration && respond_to?(:update_expiration)
124
+ self.update_expiration(default_expiration: nil) if update_expiration && respond_to?(:update_expiration)
170
125
 
171
126
  # Return same MultiResult format as other methods
172
127
  summary_boolean = command_return_values.all? { |ret| %w[OK 0 1].include?(ret.to_s) }
@@ -204,12 +159,12 @@ module Familia
204
159
  # if applicable, updating the expiration time.
205
160
  #
206
161
  # @param update_expiration [Boolean] Whether to update the expiration time
207
- # of the Redis key. This is true by default, but can be disabled if you
162
+ # of the dbkey. This is true by default, but can be disabled if you
208
163
  # don't want to mess with the cosmic balance of your key's lifespan.
209
164
  #
210
165
  # @return [MultiResult] A mystical object containing:
211
- # - success: A boolean indicating if all Redis commands succeeded
212
- # - results: An array of strings, cryptic messages from the Redis gods
166
+ # - success: A boolean indicating if all Database commands succeeded
167
+ # - results: An array of strings, cryptic messages from the Database gods
213
168
  #
214
169
  # The MultiResult object responds to:
215
170
  # - successful?: Returns the boolean success value
@@ -218,60 +173,43 @@ module Familia
218
173
  # @note Be warned, young programmer! This method dabbles in the arcane
219
174
  # art of transactions. Side effects may include data persistence and a
220
175
  # slight tingling sensation. The method does not raise exceptions for
221
- # unexpected Redis responses, but logs warnings and returns a failure status.
176
+ # unexpected Database responses, but logs warnings and returns a failure status.
222
177
  #
223
- # @example Offering your changes to the Redis deities
178
+ # @example Offering your changes to the Database deities
224
179
  # unicorn.name = "Charlie"
225
180
  # unicorn.horn_length = "magnificent"
226
181
  # result = unicorn.commit_fields
227
182
  # if result.successful?
228
- # puts "The Redis gods are pleased with your offering"
183
+ # puts "The Database gods are pleased with your offering"
229
184
  # p result.results # => ["OK", "OK"]
230
185
  # else
231
- # puts "The Redis gods frown upon your offering"
186
+ # puts "The Database gods frown upon your offering"
232
187
  # p result.results # Examine the unexpected values
233
188
  # end
234
189
  #
235
190
  # @see Familia::Horreum.valid_command_return_values for the list of
236
- # acceptable Redis command return values.
191
+ # acceptable Database command return values.
237
192
  #
238
193
  # @note This method performs logging at various levels:
239
- # - Debug: Logs the object's class, Redis key, and current state before committing
240
- # - Warn: Logs any unexpected return values from Redis commands
194
+ # - Debug: Logs the object's class, dbkey, and current state before committing
195
+ # - Warn: Logs any unexpected return values from Database commands
241
196
  # - Debug: Logs the final result, including success status and all return values
242
197
  #
243
198
  # @note The expiration update is only performed for classes that have
244
199
  # the expiration feature enabled. For others, it's a no-op.
245
200
  #
246
201
  def commit_fields update_expiration: true
247
- Familia.ld "[commit_fields1] #{self.class} #{rediskey} #{to_h} (update_expiration: #{update_expiration})"
248
- command_return_values = transaction do |conn|
249
- conn.hmset rediskey(suffix), self.to_h # using the prepared connection
250
- end
202
+ prepared_value = to_h
203
+ Familia.ld "[commit_fields] Begin #{self.class} #{dbkey} #{prepared_value} (exp: #{update_expiration})"
204
+
205
+ result = self.hmset(prepared_value)
206
+
251
207
  # Only classes that have the expiration ferature enabled will
252
208
  # actually set an expiration time on their keys. Otherwise
253
209
  # this will be a no-op that simply logs the attempt.
254
- self.update_expiration(ttl: nil) if update_expiration
255
-
256
- # The acceptable redis command return values are defined in the
257
- # Horreum class. This is to ensure that all commands return values
258
- # are validated against a consistent set of values.
259
- acceptable_values = Familia::Horreum.valid_command_return_values
260
-
261
- # Check if all return values are valid
262
- summary_boolean = command_return_values.uniq.all? { |value|
263
- acceptable_values.include?(value)
264
- }
265
-
266
- # Log the unexpected
267
- unless summary_boolean
268
- unexpected_values = command_return_values.reject { |value| acceptable_values.include?(value) }
269
- Familia.warn "[commit_fields] Unexpected return values: #{unexpected_values.inspect}"
270
- end
210
+ self.update_expiration(default_expiration: nil) if update_expiration
271
211
 
272
- Familia.ld "[commit_fields2] #{self.class} #{rediskey} #{summary_boolean}: #{command_return_values}"
273
-
274
- MultiResult.new(summary_boolean, command_return_values)
212
+ result
275
213
  end
276
214
 
277
215
  # Dramatically vanquish this object from the face of Redis! (ed: delete it)
@@ -289,9 +227,9 @@ module Familia
289
227
  # # => *poof* Rocky is no more. A moment of silence, please.
290
228
  #
291
229
  # This method is part of Familia's high-level object lifecycle management. While `delete!`
292
- # operates directly on Redis keys, `destroy!` operates at the object level and is used for
230
+ # operates directly on dbkeys, `destroy!` operates at the object level and is used for
293
231
  # ORM-style operations. Use `destroy!` when removing complete objects from the system, and
294
- # `delete!` when working directly with Redis keys.
232
+ # `delete!` when working directly with dbkeys.
295
233
  #
296
234
  # @note If debugging is enabled, this method will leave a trace of its
297
235
  # destructive path, like breadcrumbs for future data archaeologists.
@@ -299,7 +237,7 @@ module Familia
299
237
  # @see #delete! The actual hitman carrying out the deed.
300
238
  #
301
239
  def destroy!
302
- Familia.trace :DESTROY, redis, redisuri, caller(1..1) if Familia.debug?
240
+ Familia.trace :DESTROY, dbclient, uri, caller(1..1) if Familia.debug?
303
241
  delete!
304
242
  end
305
243
 
@@ -323,7 +261,7 @@ module Familia
323
261
  self.class.fields.each { |field| send("#{field}=", nil) }
324
262
  end
325
263
 
326
- # The Great Redis Refresh-o-matic 3000
264
+ # The Great Database Refresh-o-matic 3000
327
265
  #
328
266
  # Imagine your object as a forgetful time traveler. This method is like
329
267
  # zapping it with a memory ray from Redis-topia. ZAP! New memories!
@@ -339,33 +277,33 @@ module Familia
339
277
  # Remember: In the game of Redis-Refresh, you win or you... well, you
340
278
  # always win, but sometimes you forget why you played in the first place.
341
279
  #
342
- # @raise [Familia::KeyNotFoundError] If the Redis key does not exist.
280
+ # @raise [Familia::KeyNotFoundError] If the dbkey does not exist.
343
281
  #
344
282
  # @example
345
283
  # object.refresh!
346
284
  def refresh!
347
- Familia.trace :REFRESH, redis, redisuri, caller(1..1) if Familia.debug?
348
- raise Familia::KeyNotFoundError, rediskey unless redis.exists(rediskey)
285
+ Familia.trace :REFRESH, dbclient, uri, caller(1..1) if Familia.debug?
286
+ raise Familia::KeyNotFoundError, dbkey unless dbclient.exists(dbkey)
349
287
  fields = hgetall
350
- Familia.ld "[refresh!] #{self.class} #{rediskey} #{fields.keys}"
288
+ Familia.ld "[refresh!] #{self.class} #{dbkey} fields:#{fields.keys}"
351
289
  optimistic_refresh(**fields)
352
290
  end
353
291
 
354
292
  # Ah, the magical refresh dance! It's like giving your object a
355
293
  # sip from the fountain of youth.
356
294
  #
357
- # This method twirls your object around, dips it into the Redis pool,
295
+ # This method twirls your object around, dips it into the Database pool,
358
296
  # and brings it back sparkling clean and up-to-date. It's using the
359
- # refresh! spell behind the scenes, so expect some Redis whispering.
297
+ # refresh! spell behind the scenes, so expect some Database whispering.
360
298
  #
361
299
  # @note Caution, young Rubyist! While this method loves to play
362
300
  # chain-tag with other methods, it's still got that refresh! kick.
363
301
  # It'll update your object faster than you can say "matz!"
364
302
  #
365
- # @return [self] Your object, freshly bathed in Redis waters, ready
303
+ # @return [self] Your object, freshly bathed in Database waters, ready
366
304
  # to dance with more methods in a conga line of Ruby joy!
367
305
  #
368
- # @raise [Familia::KeyNotFoundError] If the Redis key does not exist.
306
+ # @raise [Familia::KeyNotFoundError] If the dbkey does not exist.
369
307
  #
370
308
  def refresh
371
309
  refresh!
@@ -385,7 +323,7 @@ module Familia
385
323
  # dragon.to_h
386
324
  # # => {"name"=>"Puff", "breathes"=>"fire", "age"=>1000}
387
325
  #
388
- # @note Watch in awe as each field is lovingly prepared for its Redis adventure!
326
+ # @note Watch in awe as each field is lovingly prepared for its Database adventure!
389
327
  #
390
328
  def to_h
391
329
  self.class.fields.inject({}) do |hsh, field|
@@ -402,7 +340,7 @@ module Familia
402
340
  # Line up all our attributes in a neat little array parade!
403
341
  #
404
342
  # This method marshals all our object's attributes into an orderly procession,
405
- # ready to march into Redis in perfect formation. It's like a little data army,
343
+ # ready to march into Database in perfect formation. It's like a little data army,
406
344
  # but friendlier and less prone to conquering neighboring databases.
407
345
  #
408
346
  # @return [Array] A splendid array of Redis-ready values, in the order of our fields.
@@ -411,7 +349,7 @@ module Familia
411
349
  # unicorn.to_a
412
350
  # # => ["Charlie", "magnificent", 5]
413
351
  #
414
- # @note Each value is carefully disguised in its Redis costume
352
+ # @note Each value is carefully disguised in its Database costume
415
353
  # before joining the parade.
416
354
  #
417
355
  def to_a
@@ -424,21 +362,21 @@ module Familia
424
362
  end
425
363
 
426
364
  # Behold, the grand tale of two serialization sorcerers:
427
- # Familia::Redistype and Familia::Horreum!
365
+ # Familia::DataType and Familia::Horreum!
428
366
  #
429
367
  # These twin wizards, though cut from the same magical cloth,
430
368
  # have their own unique spells for turning Ruby objects into
431
369
  # Redis-friendly potions. Let's peek into their spell books:
432
370
  #
433
371
  # Shared Incantations:
434
- # - Both transform various data creatures for Redis safekeeping
372
+ # - Both transform various data creatures for Database safekeeping
435
373
  # - They tame wild Strings, Symbols, and those slippery Numerics
436
374
  # - Secret rituals (aka custom serialization) are welcome
437
375
  #
438
376
  # Mystical Differences:
439
- # - Redistype reads the future in opts[:class] tea leaves
377
+ # - DataType reads the future in opts[:class] tea leaves
440
378
  # - Horreum prefers to interrogate types more thoroughly
441
- # - Redistype leaves a trail of debug breadcrumbs
379
+ # - DataType leaves a trail of debug breadcrumbs
442
380
  #
443
381
  # But wait! Enter the wise Familia.distinguisher,
444
382
  # a grand unifier of serialization magic!
@@ -447,7 +385,7 @@ module Familia
447
385
  # 1. Juggles a circus of data types from both realms
448
386
  # 2. Offers a 'strict_values' toggle for the type-obsessed
449
387
  # 3. Welcomes custom spells via dump_method
450
- # 4. Sprinkles debug fairy dust à la Redistype
388
+ # 4. Sprinkles debug fairy dust à la DataType
451
389
  #
452
390
  # By channeling the Familia.distinguisher, we've created a
453
391
  # harmonious serialization symphony, flexible enough to dance
@@ -474,14 +412,13 @@ module Familia
474
412
 
475
413
  prepared
476
414
  end
477
- alias to_redis serialize_value
478
415
 
479
- # Converts a Redis string value back to its original Ruby type
416
+ # Converts a Database string value back to its original Ruby type
480
417
  #
481
418
  # This method attempts to deserialize JSON strings back to their original
482
419
  # Hash or Array types. Simple string values are returned as-is.
483
420
  #
484
- # @param val [String] The string value from Redis to deserialize
421
+ # @param val [String] The string value from Database to deserialize
485
422
  # @param symbolize_keys [Boolean] Whether to symbolize hash keys (default: true for compatibility)
486
423
  # @return [Object] The deserialized value (Hash, Array, or original string)
487
424
  #
@@ -500,82 +437,8 @@ module Familia
500
437
 
501
438
  val
502
439
  end
503
- alias from_redis deserialize_value
504
440
 
505
441
  end
506
- # End of Serialization module
507
-
508
- # The magical MultiResult, keeper of Redis's deepest secrets!
509
- #
510
- # This quirky little class wraps up the outcome of a Redis "transaction"
511
- # (or as I like to call it, a "Redis dance party") with a bow made of
512
- # pure Ruby delight. It knows if your commands were successful and
513
- # keeps the results safe in its pocket dimension.
514
- #
515
- # @attr_reader success [Boolean] The golden ticket! True if all your
516
- # Redis wishes came true in the transaction.
517
- # @attr_reader results [Array<String>] A mystical array of return values,
518
- # each one a whisper from the Redis gods.
519
- #
520
- # @example Summoning a MultiResult from the void
521
- # result = MultiResult.new(true, ["OK", "OK"])
522
- #
523
- # @example Divining the success of your Redis ritual
524
- # if result.successful?
525
- # puts "Huzzah! The Redis spirits smile upon you!"
526
- # else
527
- # puts "Alas! The Redis gremlins have conspired against us!"
528
- # end
529
- #
530
- # @example Peering into the raw essence of results
531
- # result.results.each_with_index do |value, index|
532
- # puts "Command #{index + 1} whispered back: #{value}"
533
- # end
534
- #
535
- class MultiResult
536
- # @return [Boolean] true if all commands in the transaction succeeded,
537
- # false otherwise
538
- attr_reader :success
539
-
540
- # @return [Array<String>] The raw return values from the Redis commands
541
- attr_reader :results
542
-
543
- # Creates a new MultiResult instance.
544
- #
545
- # @param success [Boolean] Whether all commands succeeded
546
- # @param results [Array<String>] The raw results from Redis commands
547
- def initialize(success, results)
548
- @success = success
549
- @results = results
550
- end
551
-
552
- # Returns a tuple representing the result of the transaction.
553
- #
554
- # @return [Array] A tuple containing the success status and the raw results.
555
- # The success status is a boolean indicating if all commands succeeded.
556
- # The raw results is an array of return values from the Redis commands.
557
- #
558
- # @example
559
- # [true, ["OK", true, 1]]
560
- #
561
- def tuple
562
- [successful?, results]
563
- end
564
- alias to_a tuple
565
-
566
- def to_h
567
- { success: successful?, results: results }
568
- end
569
-
570
- # Convenient method to check if the commit was successful.
571
- #
572
- # @return [Boolean] true if all commands succeeded, false otherwise
573
- def successful?
574
- @success
575
- end
576
- alias success? successful?
577
- end
578
- # End of MultiResult class
579
442
 
580
443
  include Serialization # these become Horreum instance methods
581
444
  end
@@ -1,10 +1,10 @@
1
- # rubocop:disable all
1
+ # lib/familia/horreum/settings.rb
2
2
  #
3
3
  module Familia
4
4
  # InstanceMethods - Module containing instance-level methods for Familia
5
5
  #
6
6
  # This module is included in classes that include Familia, providing
7
- # instance-level functionality for Redis operations and object management.
7
+ # instance-level functionality for Database operations and object management.
8
8
  #
9
9
  class Horreum
10
10
 
@@ -18,23 +18,12 @@ module Familia
18
18
  @opts
19
19
  end
20
20
 
21
- def redisdetails
22
- {
23
- uri: self.class.uri,
24
- db: self.class.db,
25
- key: rediskey,
26
- type: redistype,
27
- ttl: ttl,
28
- realttl: realttl
29
- }
21
+ def logical_database=(v)
22
+ @logical_database = v.to_i
30
23
  end
31
24
 
32
- def db=(v)
33
- @db = v.to_i
34
- end
35
-
36
- def db
37
- @db || self.class.db
25
+ def logical_database
26
+ @logical_database || self.class.logical_database
38
27
  end
39
28
 
40
29
  def suffix
@@ -1,10 +1,10 @@
1
- # rubocop:disable all
1
+ # lib/familia/horreum/utils.rb
2
2
  #
3
3
  module Familia
4
4
  # InstanceMethods - Module containing instance-level methods for Familia
5
5
  #
6
6
  # This module is included in classes that include Familia, providing
7
- # instance-level functionality for Redis operations and object management.
7
+ # instance-level functionality for Database operations and object management.
8
8
  #
9
9
  class Horreum
10
10
 
@@ -12,30 +12,31 @@ module Familia
12
12
  #
13
13
  module Utils
14
14
 
15
- def redisuri(suffix = nil)
16
- u = Familia.redisuri(self.class.uri) # returns URI::Redis
17
- u.db = db if db # override the db if we have one
18
- u.key = rediskey(suffix)
15
+ def uri(suffix = nil)
16
+ u = Familia.uri(self.class.uri) # returns URI::Redis
17
+ u.logical_database = logical_database if logical_database # override the logical_database if we have one
18
+ u.key = dbkey(suffix)
19
19
  u
20
20
  end
21
21
 
22
- # +suffix+ is the value to be used at the end of the redis key
22
+ # +suffix+ is the value to be used at the end of the db key
23
23
  # (e.g. `customer:customer_id:scores` would have `scores` as the suffix
24
24
  # and `customer_id` would have been the identifier in that case).
25
25
  #
26
26
  # identifier is the value that distinguishes this object from others.
27
- # Whether this is a Horreum or RedisType object, the value is taken
27
+ # Whether this is a Horreum or DataType object, the value is taken
28
28
  # from the `identifier` method).
29
29
  #
30
- def rediskey(suffix = nil, ignored = nil)
30
+ def dbkey(suffix = nil, ignored = nil)
31
31
  raise Familia::NoIdentifier, "No identifier for #{self.class}" if identifier.to_s.empty?
32
32
  suffix ||= self.suffix # use the instance method to get the default suffix
33
- self.class.rediskey identifier, suffix
33
+ self.class.dbkey identifier, suffix
34
34
  end
35
35
 
36
36
  def join(*args)
37
37
  Familia.join(args.map { |field| send(field) })
38
38
  end
39
+
39
40
  end
40
41
 
41
42
  include Utils # these become Horreum instance methods