Module: Familia::Horreum::Serialization

Included in:
Familia::Horreum
Defined in:
lib/familia/horreum/serialization.rb

Overview

Methods that call load and dump (InstanceMethods)

Note on refresh methods: In this class, refresh! is the primary method that performs the Redis query and state update. The non-bang refresh method is provided as a convenience for method chaining, but still performs the same destructive update as refresh!. This deviates from common Ruby conventions to better fit the specific needs of this system.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#redisObject



24
25
26
# File 'lib/familia/horreum/serialization.rb', line 24

def redis
  @redis || self.class.redis
end

Instance Method Details

#commit_fieldsObject

+return: [Array<String>] The return value of the Redis multi command



61
62
63
64
65
66
67
# File 'lib/familia/horreum/serialization.rb', line 61

def commit_fields
  Familia.ld "[commit_fields] #{self.class} #{rediskey} #{to_h}"
  transaction do |conn|
    hmset
    update_expiration
  end
end

#destroy!Object



69
70
71
72
# File 'lib/familia/horreum/serialization.rb', line 69

def destroy!
  Familia.trace :DESTROY, redis, redisuri, caller(1..1) if Familia.debug?
  delete!
end

#refreshself

Note:

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.

Returns:

  • (self)

    Returns the object itself after refreshing, allowing method chaining.



96
97
98
99
# File 'lib/familia/horreum/serialization.rb', line 96

def refresh
  refresh!
  self
end

#refresh!Object

Note:

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.

Returns:

  • The list of field names that were updated.



81
82
83
84
85
86
# File 'lib/familia/horreum/serialization.rb', line 81

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

#saveObject

A thin wrapper around ‘commit_fields` that updates the timestamps and returns a boolean.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/familia/horreum/serialization.rb', line 43

def save
  Familia.trace :SAVE, redis, redisuri, caller(1..1) if Familia.debug?

  # Update timestamp fields
  self.key ||= self.identifier
  self.updated = Familia.now.to_i
  self.created ||= Familia.now.to_i

  # Thr return value of commit_fields is an array of strings: ["OK"].
  ret = commit_fields # e.g. ["OK"]

  Familia.ld "[save] #{self.class} #{rediskey} #{ret}"

  # Convert the return value to a boolean
  ret.all? { |value| value == "OK" }
end

#to_aObject



113
114
115
116
117
118
119
120
# File 'lib/familia/horreum/serialization.rb', line 113

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_hObject



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/familia/horreum/serialization.rb', line 101

def to_h
  # Use self.class.fields to efficiently generate a hash
  # of all the fields for this object
  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:

  1. Handling a wide range of data types, including those from both implementations

  2. Providing a ‘strict_values’ option for flexible type handling

  3. Supporting custom serialization through a dump_method

  4. 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.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/familia/horreum/serialization.rb', line 149

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

#transactionObject



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/familia/horreum/serialization.rb', line 28

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

#update_expiration(ttl = nil) ⇒ Object



163
164
165
166
167
168
169
# File 'lib/familia/horreum/serialization.rb', line 163

def update_expiration(ttl = nil)
  ttl ||= opts[:ttl]
  return if ttl.to_i.zero? # nil will be zero

  Familia.ld "#{rediskey} to #{ttl}"
  expire ttl.to_i
end