Module: Familia::Horreum::Serialization
- Included in:
- Familia::Horreum
- Defined in:
- lib/familia/horreum/serialization.rb
Overview
Serialization: Where Objects Go to Become Strings (and Vice Versa)!
This module is chock-full of methods that’ll make your head spin (in a good way)! We’ve got loaders, dumpers, and refreshers galore. It’s like a laundromat for your data, but instead of quarters, it runs on Redis commands.
A Note on Our Refreshing Refreshers: In the wild world of Ruby, ‘!’ usually means “Watch out! I’m dangerous!” But here in Familia-land, we march to the beat of a different drummer. Our refresh! method is the real deal, doing all the heavy lifting. The non-bang refresh? Oh, it’s just as rowdy, but it plays nice with method chaining. It’s like the polite twin who still knows how to party.
Remember: In Familia, refreshing isn’t just a chore, it’s a chance to dance with data! Whether you bang(!) or not, you’re still invited to the Redis disco.
(P.S. If you’re reading these docs, lol sorry. I asked Claude 3.5 to write in the style of _why the lucky stiff today and got this uncanny valley response. I hope you enjoy reading it as much as I did writing the prompt for it. - @delano).
(Ahem! What I meant to say was that if you’re reading this, congratulations! You’ve stumbled upon the secret garden of documentation. Feel free to smell the Ruby roses, but watch out for the Redis thorns!)
Instance Attribute Summary collapse
-
#redis ⇒ Redis
Summon the mystical Redis connection from the depths of instance or class.
Instance Method Summary collapse
-
#apply_fields(**fields) ⇒ self
Apply a smattering of fields to this object like fairy dust.
-
#commit_fields ⇒ MultiResult
Commit our precious fields to Redis.
-
#destroy! ⇒ void
Dramatically vanquish this object from the face of Redis! (ed: delete it).
-
#refresh ⇒ self
Refreshes the object’s state and returns self to allow method chaining.
-
#refresh! ⇒ Object
Refreshes the object’s state by querying Redis and overwriting the current field values.
-
#save ⇒ Boolean
Save our precious data to Redis, with a sprinkle of timestamp magic!.
-
#to_a ⇒ Array
Line up all our attributes in a neat little array parade!.
-
#to_h ⇒ Hash
Transform this object into a magical hash of wonders!.
-
#to_redis(val) ⇒ Object
The to_redis method in Familia::Redistype and Familia::Horreum serve similar purposes but have some key differences in their implementation:.
-
#transaction {|conn| ... } ⇒ Object
Perform a sacred Redis transaction ritual.
Instance Attribute Details
#redis ⇒ Redis
Summon the mystical Redis connection from the depths of instance or class.
This method is like a magical divining rod, always pointing to the nearest source of Redis goodness. It first checks if we have a personal Redis connection (@redis), and if not, it borrows the class’s connection.
69 70 71 |
# File 'lib/familia/horreum/serialization.rb', line 69 def redis @redis || self.class.redis end |
Instance Method Details
#apply_fields(**fields) ⇒ self
Apply a smattering of fields to this object like fairy dust.
152 153 154 155 156 157 158 |
# File 'lib/familia/horreum/serialization.rb', line 152 def apply_fields(**fields) fields.each do |field, value| # Whisper the new value into the object's ear (if it's listening) send("#{field}=", value) if respond_to?("#{field}=") end self end |
#commit_fields ⇒ MultiResult
Be warned, young programmer! This method dabbles in the arcane art of transactions. Side effects may include data persistence and a slight tingling sensation. The method does not raise exceptions for unexpected Redis responses, but logs warnings and returns a failure status.
This method performs logging at various levels:
-
Debug: Logs the object’s class, Redis key, and current state before committing
-
Warn: Logs any unexpected return values from Redis commands
-
Debug: Logs the final result, including success status and all return values
The expiration update is only performed for classes that have the expiration feature enabled. For others, it’s a no-op.
Commit our precious fields to Redis.
This method performs a sacred ritual, sending our cherished attributes on a journey through the ethernet to find their resting place in Redis. It executes a transaction that includes setting field values and, if applicable, updating the expiration time.
The MultiResult object responds to:
- successful?: Returns the boolean success value
- results: Returns the array of command return values
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/familia/horreum/serialization.rb', line 203 def commit_fields Familia.ld "[commit_fields] #{self.class} #{rediskey} #{to_h}" command_return_values = transaction do |conn| hmset # Only classes that have the expiration ferature enabled will # actually set an expiration time on their keys. Otherwise # this will be a no-op. update_expiration end # The acceptable redis command return values are defined in the # Horreum class. This is to ensure that all commands return values # are validated against a consistent set of values. acceptable_values = Familia::Horreum.valid_command_return_values # Check if all return values are valid summary_boolean = command_return_values.uniq.all? { |value| acceptable_values.include?(value) } # Log the unexpected unless summary_boolean unexpected_values = command_return_values.reject { |value| acceptable_values.include?(value) } Familia.warn "[commit_fields] Unexpected return values: #{unexpected_values}" end Familia.ld "[commit_fields] #{self.class} #{rediskey} #{summary_boolean}: #{command_return_values}" MultiResult.new(summary_boolean, command_return_values) end |
#destroy! ⇒ void
If debugging is enabled, this method will leave a trace of its destructive path, like breadcrumbs for future data archaeologists.
This method returns an undefined value.
Dramatically vanquish this object from the face of Redis! (ed: delete it)
This method is the doomsday device of our little data world. It will mercilessly eradicate all traces of our object from Redis, leaving naught but digital dust in its wake. Use with caution, lest you accidentally destroy the wrong data-verse!
254 255 256 257 |
# File 'lib/familia/horreum/serialization.rb', line 254 def destroy! Familia.trace :DESTROY, redis, redisuri, caller(1..1) if Familia.debug? delete! end |
#refresh ⇒ self
While this method allows chaining, it still performs a destructive update like refresh!.
Refreshes the object’s state and returns self to allow method chaining. This method calls refresh! internally, performing the actual Redis query and state update.
281 282 283 284 |
# File 'lib/familia/horreum/serialization.rb', line 281 def refresh refresh! self end |
#refresh! ⇒ Object
This is a destructive operation that will overwrite any unsaved changes.
Refreshes the object’s state by querying Redis and overwriting the current field values. This method performs a destructive update on the object, regardless of unsaved changes.
266 267 268 269 270 271 |
# File 'lib/familia/horreum/serialization.rb', line 266 def refresh! Familia.trace :REFRESH, redis, redisuri, caller(1..1) if Familia.debug? fields = hgetall Familia.ld "[refresh!] #{self.class} #{rediskey} #{fields.keys}" optimistic_refresh(**fields) end |
#save ⇒ Boolean
This method will leave breadcrumbs (traces) if you’re in debug mode. It’s like Hansel and Gretel, but for data operations!
Save our precious data to Redis, with a sprinkle of timestamp magic!
This method is like a conscientious historian, not only recording your object’s current state but also meticulously timestamping when it was created and last updated. It’s the record keeper of your data’s life story!
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/familia/horreum/serialization.rb', line 120 def save Familia.trace :SAVE, redis, redisuri, caller(1..1) if Familia.debug? # Update our object's life story self.key ||= self.identifier self.updated = Familia.now.to_i self.created ||= Familia.now.to_i # Commit our tale to the Redis chronicles ret = commit_fields # e.g. MultiResult.new(true, ["OK", "OK"]) Familia.ld "[save] #{self.class} #{rediskey} #{ret}" # Did Redis accept our offering? ret.successful? end |
#to_a ⇒ Array
Each value is carefully disguised in its Redis costume
Line up all our attributes in a neat little array parade!
This method marshals all our object’s attributes into an orderly procession, ready to march into Redis in perfect formation. It’s like a little data army, but friendlier and less prone to conquering neighboring databases.
before joining the parade.
326 327 328 329 330 331 332 333 |
# File 'lib/familia/horreum/serialization.rb', line 326 def to_a self.class.fields.map do |field| val = send(field) prepared = to_redis(val) Familia.ld " [to_a] field: #{field} val: #{val.class} prepared: #{prepared.class}" prepared end end |
#to_h ⇒ Hash
Watch in awe as each field is lovingly prepared for its Redis adventure!
Transform this object into a magical hash of wonders!
This method performs an alchemical transmutation, turning our noble object into a more plebeian hash. But fear not, for in this form, it can slip through the cracks of the universe (or at least, into Redis) with ease.
301 302 303 304 305 306 307 308 309 |
# File 'lib/familia/horreum/serialization.rb', line 301 def to_h self.class.fields.inject({}) do |hsh, field| val = send(field) prepared = to_redis(val) Familia.ld " [to_h] field: #{field} val: #{val.class} prepared: #{prepared.class}" hsh[field] = prepared hsh end end |
#to_redis(val) ⇒ Object
The to_redis method in Familia::Redistype and Familia::Horreum serve similar purposes but have some key differences in their implementation:
Similarities:
-
Both methods aim to serialize various data types for Redis storage
-
Both handle basic data types like String, Symbol, and Numeric
-
Both have provisions for custom serialization methods
Differences:
-
Familia::Redistype uses the opts for type hints
-
Familia::Horreum had more explicit type checking and conversion
-
Familia::Redistype includes more extensive debug tracing
The centralized Familia.distinguisher method accommodates both approaches by:
-
Handling a wide range of data types, including those from both implementations
-
Providing a ‘strict_values’ option for flexible type handling
-
Supporting custom serialization through a dump_method
-
Including debug tracing similar to Familia::Redistype
By using Familia.distinguisher, we achieve more consistent behavior across different parts of the library while maintaining the flexibility to handle various data types and custom serialization needs. This centralization also makes it easier to extend or modify serialization behavior in the future.
362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/familia/horreum/serialization.rb', line 362 def to_redis(val) prepared = Familia.distinguisher(val, false) if prepared.nil? && val.respond_to?(dump_method) prepared = val.send(dump_method) end if prepared.nil? Familia.ld "[#{self.class}#to_redis] nil returned for #{self.class}##{name}" end prepared end |
#transaction {|conn| ... } ⇒ Object
This method temporarily replaces your Redis connection with a multi connection. Don’t worry, it puts everything back where it found it when it’s done.
Perform a sacred Redis transaction ritual.
This method creates a protective circle around your Redis operations, ensuring they all succeed or fail together. It’s like a group hug for your data operations, but with more ACID properties.
91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/familia/horreum/serialization.rb', line 91 def transaction original_redis = self.redis begin redis.multi do |conn| self.instance_variable_set(:@redis, conn) yield(conn) end ensure self.redis = original_redis end end |