Module: Familia::Utils

Included in:
Familia
Defined in:
lib/familia/utils.rb

Instance Method Summary collapse

Instance Method Details

#debug?Boolean

Checks if debug mode is enabled

e.g. Familia.debug = true

Returns:

  • (Boolean)

    true if debug mode is on, false otherwise



15
16
17
# File 'lib/familia/utils.rb', line 15

def debug?
  @debug == true
end

#distinguisher(value_to_distinguish, strict_values: true) ⇒ String?

This method determines the appropriate transformation to apply based on the class of the input argument.

The method uses a case statement to handle different classes:

  • For ‘Symbol`, `String`, `Integer`, and `Float` classes, it traces the operation and converts the value to a string.

  • For ‘Familia::Horreum` class, it traces the operation and returns the identifier of the value.

  • For ‘TrueClass`, `FalseClass`, and `NilClass`, it traces the operation and converts the value to a string (“true”, “false”, or “”).

  • For any other class, it traces the operation and returns nil.

Alternative names for ‘value_to_distinguish` could be `input_value`, `value`, or `object`.

Parameters:

  • value_to_distinguish (Object)

    The value to be processed. Keep in mind that all data in redis is stored as a string so whatever the type of the value, it will be converted to a string.

  • strict_values (Boolean) (defaults to: true)

    Whether to enforce strict value handling. Defaults to true.

Returns:

  • (String, nil)

    The processed value as a string or nil for unsupported classes.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/familia/utils.rb', line 151

def distinguisher(value_to_distinguish, strict_values: true)
  case value_to_distinguish
  when ::Symbol, ::String, ::Integer, ::Float
    Familia.trace :TOREDIS_DISTINGUISHER, redis, "string", caller(1..1) if Familia.debug?

    # Symbols and numerics are naturally serializable to strings
    # so it's a relatively low risk operation.
    value_to_distinguish.to_s

  when ::TrueClass, ::FalseClass, ::NilClass
    Familia.trace :TOREDIS_DISTINGUISHER, redis, "true/false/nil", caller(1..1) if Familia.debug?

    # TrueClass, FalseClass, and NilClass are considered high risk because their
    # original types cannot be reliably determined from their serialized string
    # representations. This can lead to unexpected behavior during deserialization.
    # For instance, a TrueClass value serialized as "true" might be deserialized as
    # a String, causing application errors. Even more problematic, a NilClass value
    # serialized as an empty string makes it impossible to distinguish between a
    # nil value and an empty string upon deserialization. Such scenarios can result
    # in subtle, hard-to-diagnose bugs. To mitigate these risks, we raise an
    # exception when encountering these types unless the strict_values option is
    # explicitly set to false.
    #
    raise Familia::HighRiskFactor, value_to_distinguish if strict_values
    value_to_distinguish.to_s #=> "true", "false", ""

  when Familia::Base, Class
    Familia.trace :TOREDIS_DISTINGUISHER, redis, "base", caller(1..1) if Familia.debug?

    # When called with a class we simply transform it to its name. For
    # instances of Familia class, we store the identifier.
    if value_to_distinguish.is_a?(Class)
      value_to_distinguish.name
    else
      value_to_distinguish.identifier
    end

  else
     Familia.trace :TOREDIS_DISTINGUISHER, redis, "else1 #{strict_values}", caller(1..1) if Familia.debug?

    if value_to_distinguish.class.ancestors.member?(Familia::Base)
      Familia.trace :TOREDIS_DISTINGUISHER, redis, "isabase", caller(1..1) if Familia.debug?

      value_to_distinguish.identifier

    else
      Familia.trace :TOREDIS_DISTINGUISHER, redis, "else2 #{strict_values}", caller(1..1) if Familia.debug?
      raise Familia::HighRiskFactor, value_to_distinguish if strict_values
      nil
    end
  end
end

#generate_id(length: 32, encoding: 36) ⇒ String

Generates a unique ID using SHA256 and base-36 encoding

Examples:

Generate a default ID

Familia.generate_id
# => "kuk79w6uxg81tk0kn5hsl6pr7ic16e9p6evjifzozkda9el6z"

Generate a shorter ID with 16 bytes input

Familia.generate_id(length: 16)
# => "z6gqw1b7ftzpvapydkt0iah0h0bev5hkhrs4mkf1gq4nq5csa"

Generate an ID with hexadecimal encoding

Familia.generate_id(encoding: 16)
# => "d06a2a70cba543cd2bbd352c925bc30b0a9029ca79e72d6556f8d6d8603d5716"

Generate a shorter ID with custom encoding

Familia.generate_id(length: 8, encoding: 32)
# => "193tosc85k3u513do2mtmibchpd2ruh5l3nsp6dnl0ov1i91h7m7"

Parameters:

  • length (Integer) (defaults to: 32)

    length of the random input in bytes (default: 32)

  • encoding (Integer) (defaults to: 36)

    base encoding for the output (default: 36)

Returns:

  • (String)

    a unique identifier

Raises:

  • (ArgumentError)


40
41
42
43
44
45
# File 'lib/familia/utils.rb', line 40

def generate_id(length: 32, encoding: 36)
  raise ArgumentError, "Encoding must be between 2 and 36" unless (1..36).include?(encoding)

  input = SecureRandom.hex(length)
  Digest::SHA256.hexdigest(input).to_i(16).to_s(encoding)
end

#generate_sha_hash(*elements) ⇒ Object



123
124
125
126
# File 'lib/familia/utils.rb', line 123

def generate_sha_hash(*elements)
  concatenated_string = Familia.join(*elements)
  DIGEST_CLASS.hexdigest(concatenated_string)
end

#join(*val) ⇒ String

Joins array elements with Familia delimiter

Parameters:

  • val (Array)

    elements to join

Returns:



50
51
52
# File 'lib/familia/utils.rb', line 50

def join(*val)
  val.compact.join(Familia.delim)
end

#qstamp(quantum = 10.minutes, pattern: nil, time: nil) ⇒ Integer, String

A quantized timestamp

Examples:

Familia.qstamp  # Returns an integer timestamp rounded to the nearest 10 minutes
Familia.qstamp(1.hour)  # Uses 1 hour quantum
Familia.qstamp(10.minutes, pattern: '%H:%M')  # Returns a formatted string like "12:30"
Familia.qstamp(10.minutes, time: 1302468980)  # Quantizes the given Unix timestamp
Familia.qstamp(10.minutes, time: Time.now)  # Quantizes the given Time object
Familia.qstamp(10.minutes, pattern: '%H:%M', time: 1302468980)  # Formats a specific time

Parameters:

  • quantum (Integer) (defaults to: 10.minutes)

    The time quantum in seconds (default: 10 minutes).

  • pattern (String, nil) (defaults to: nil)

    The strftime pattern to format the timestamp.

  • time (Integer, Float, Time, nil) (defaults to: nil)

    A specific time to quantize (default: current time).

Returns:

  • (Integer, String)

    A unix timestamp or formatted timestamp string.



110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/familia/utils.rb', line 110

def qstamp(quantum = 10.minutes, pattern: nil, time: nil)
  time ||= Familia.now
  time = time.to_f if time.is_a?(Time)

  rounded = time - (time % quantum)

  if pattern
    Time.at(rounded).utc.strftime(pattern)
  else
    Time.at(rounded).utc.to_i
  end
end

#rediskey(*val) ⇒ String

Creates a Redis key from given values

Parameters:

  • val (Array)

    elements to join for the key

Returns:



64
65
66
# File 'lib/familia/utils.rb', line 64

def rediskey(*val)
  join(*val)
end

#redisuri(uri) ⇒ URI::Redis

Converts a generic URI to a Redis URI

Parameters:

  • uri (String, URI)

    URI to convert

Returns:

  • (URI::Redis)

    Redis URI object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/familia/utils.rb', line 71

def redisuri(uri)
  uri ||= Familia.uri
  generic_uri = URI.parse(uri.to_s)

  # Create a new URI::Redis object
  URI::Redis.build(
    scheme: generic_uri.scheme,
    userinfo: generic_uri.userinfo,
    host: generic_uri.host,
    port: generic_uri.port,
    path: generic_uri.path, # the db is stored in the path
    query: generic_uri.query,
    fragment: generic_uri.fragment
  )
end

#split(val) ⇒ Array

Splits a string using Familia delimiter

Parameters:

  • val (String)

    string to split

Returns:

  • (Array)

    split elements



57
58
59
# File 'lib/familia/utils.rb', line 57

def split(val)
  val.split(Familia.delim)
end