Module: Familia::Utils

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

Overview

Family-related utility methods

Instance Method Summary collapse

Instance Method Details

#dbkey(*val) ⇒ String

Creates a dbkey from given values

Parameters:

  • val (Array)

    elements to join for the key

Returns:

  • (String)

    dbkey



91
92
93
# File 'lib/familia/utils.rb', line 91

def dbkey(*val)
  join(*val)
end

#join(*val) ⇒ String

Joins array elements with Familia delimiter

Parameters:

  • val (Array)

    elements to join

Returns:

  • (String)

    joined string



77
78
79
# File 'lib/familia/utils.rb', line 77

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

#legacy_json_encoded?(val) ⇒ Boolean

Detects the legacy JSON-encoded string format written by pre-2.10.0 reference collections (e.g. unique_index hashkeys stored "\"u1\"" instead of the raw "u1").

This is the single source of truth for the format check: the read path (DataType::Serialization#strip_legacy_json_encoding) strips such values, and the index introspection layer (IndexDescriptor#stale_format?) samples for them to flag indexes that need a rebuild. Side-effect free.

Parameters:

  • val (Object)

    the raw stored value

Returns:

  • (Boolean)

    true when val is a JSON-encoded string identifier



70
71
72
# File 'lib/familia/utils.rb', line 70

def legacy_json_encoded?(val)
  val.is_a?(String) && val.length > 2 && val.start_with?('"') && val.end_with?('"')
end

#now(current_time = Time.now) ⇒ Float

Returns current time in UTC as a float

Parameters:

  • current_time (Time) (defaults to: Time.now)

    time object (default: current time)

Returns:

  • (Float)

    time in seconds since epoch



106
107
108
# File 'lib/familia/utils.rb', line 106

def now(current_time = Time.now)
  current_time.utc.to_f
end

#now_in_μsInteger Also known as: now_in_microseconds

Returns the current time in microseconds. This is used to measure the duration of Database commands.

Alias: now_in_microseconds

Returns:

  • (Integer)

    The current time in microseconds.



116
117
118
# File 'lib/familia/utils.rb', line 116

def now_in_μs
  Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
end

#positive?(ret) ⇒ Boolean, Redis::Future

Future-aware positive check for Redis command return values.

For commands where 0 means "nothing happened" and positive means "something happened" (e.g., EXISTS, DEL, LINSERT, TTL checks).

Examples:

Check if key exists

ret = dbclient.exists(key)
Familia.positive?(ret)  #=> true if key exists

Check TTL inside pipeline

pipelined do
  ret = dbclient.ttl(key)
  Familia.positive?(ret)  #=> Redis::Future (passthrough)
end

Parameters:

  • ret (Integer, Redis::Future)

    The return value from a Redis command

Returns:

  • (Boolean, Redis::Future)

    true/false for concrete values, passthrough for Futures



55
56
57
# File 'lib/familia/utils.rb', line 55

def positive?(ret)
  ret.is_a?(Redis::Future) ? ret : ret.positive?
end

#pretty_path(filepath) ⇒ Pathname, ...

Converts an absolute file path to a path relative to the current working directory. This simplifies logging and error reporting by showing only the relevant parts of file paths instead of lengthy absolute paths.

Examples:

Using current directory as base

Utils.pretty_path("/home/dev/project/lib/config.rb") # => "lib/config.rb"

Path outside current directory

Utils.pretty_path("/etc/hosts") # => "hosts"

Nil input

Utils.pretty_path(nil) # => nil

Parameters:

  • filepath (String, Pathname)

    The file path to convert

Returns:

  • (Pathname, String, nil)

    A relative path from current directory, basename if path goes outside current directory, or nil if filepath is nil

See Also:

  • Ruby standard library documentation


166
167
168
169
170
171
172
173
174
175
176
# File 'lib/familia/utils.rb', line 166

def pretty_path(filepath)
  return nil if filepath.nil?

  basepath = Dir.pwd
  relative_path = Pathname.new(filepath).relative_path_from(basepath)
  if relative_path.to_s.start_with?('..')
    File.basename(filepath)
  else
    relative_path
  end
end

#pretty_stack(skip: 1, limit: 5) ⇒ String

Formats a stack trace with pretty file paths for improved readability

Examples:

Utils.pretty_stack(limit: 10)
# => "lib/models/user.rb:25:in `save'\n lib/controllers/app.rb:45:in `create'"

Parameters:

  • limit (Integer) (defaults to: 5)

    Maximum number of stack frames to include (default: 3)

Returns:

  • (String)

    Formatted stack trace with relative paths joined by newlines



186
187
188
# File 'lib/familia/utils.rb', line 186

def pretty_stack(skip: 1, limit: 5)
  caller(skip..(skip + limit + 1)).first(limit).map { |frame| pretty_path(frame) }.join("\n")
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: Familia.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.



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/familia/utils.rb', line 135

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

#serverid(uri) ⇒ Object

Gets server ID without DB component for pool identification



96
97
98
99
100
101
# File 'lib/familia/utils.rb', line 96

def serverid(uri)
  # Create a copy of URI without DB for server identification
  uri = uri.dup
  uri.db = nil
  uri.serverid
end

#split(val) ⇒ Array

Splits a string using Familia delimiter

Parameters:

  • val (String)

    string to split

Returns:

  • (Array)

    split elements



84
85
86
# File 'lib/familia/utils.rb', line 84

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

#success?(ret) ⇒ Boolean, Redis::Future

Future-aware success check for Redis command return values.

Redis commands like HSET return 0 (updated existing) or 1 (created new). Both are success states. Inside a pipeline or transaction, the return value is a Redis::Future which cannot be inspected until the block completes.

Examples:

Normal usage

ret = dbclient.hset(key, field, value)
Familia.success?(ret)  #=> true (for 0 or 1)

Inside pipeline

pipelined do
  ret = dbclient.hset(key, field, value)
  Familia.success?(ret)  #=> Redis::Future (passthrough)
end

Parameters:

  • ret (Integer, Redis::Future)

    The return value from a Redis command

Returns:

  • (Boolean, Redis::Future)

    true/false for concrete values, passthrough for Futures



32
33
34
# File 'lib/familia/utils.rb', line 32

def success?(ret)
  ret.is_a?(Redis::Future) ? ret : (ret.zero? || ret.positive?)
end