Module: Nombaone::Internal::Util Private

Defined in:
lib/nombaone/internal/util.rb,
sig/nombaone/internal.rbs

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Pure, dependency-free helpers shared across the transport and resources.

Constant Summary collapse

PRESERVE_VALUE_KEYS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Keys whose values are arbitrary user JSON and must never have their inner keys rewritten (a customer's metadata, a sandbox webhook payload). The key name itself is still normalized (both are already single words, so normalization is a no-op).

Returns:

  • (Array[String])
%w[metadata payload].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.backoff_seconds(attempt) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Full-jitter exponential backoff, in seconds: a random delay in [0, min(8, 0.5 * 2**attempt)). Jitter keeps a fleet of retrying clients from stampeding the API in lockstep.

Parameters:

  • attempt (Integer)

    zero-based attempt index.

Returns:

  • (Float)


113
114
115
# File 'lib/nombaone/internal/util.rb', line 113

def backoff_seconds(attempt)
  rand * [8.0, 0.5 * (2**attempt)].min
end

.camelize(key) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Convert a snake_case key to the camelCase name the wire expects. The wire field name is law; this is the single place the SDK's idiomatic Ruby names become it (customer_idcustomerId, amount_in_koboamountInKobo, plan_refplanRef).

Parameters:

  • key (String, Symbol)

Returns:

  • (String)


40
41
42
43
44
45
46
# File 'lib/nombaone/internal/util.rb', line 40

def camelize(key)
  parts = key.to_s.split("_")
  return parts.first.to_s if parts.length <= 1

  head, *tail = parts
  (head + tail.map { |word| word.empty? ? "" : word[0].upcase + word[1..] }.join)
end

.deep_camelize_keys(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Recursively rewrite Hash keys to camelCase, except that the value of a PRESERVE_VALUE_KEYS key is passed through untouched.

Parameters:

  • value (Object)

Returns:

  • (Object)


53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/nombaone/internal/util.rb', line 53

def deep_camelize_keys(value)
  case value
  when Hash
    value.each_with_object({}) do |(key, val), out|
      out[camelize(key)] =
        PRESERVE_VALUE_KEYS.include?(key.to_s) ? val : deep_camelize_keys(val)
    end
  when Array
    value.map { |item| deep_camelize_keys(item) }
  else
    value
  end
end

.encode_path_segment(value) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Percent-encode one URL path segment. Ids come from user input, so they are never trusted raw; this matches JavaScript's encodeURIComponent for the characters that appear in NombaOne ids.

Parameters:

  • value (String)

Returns:

  • (String)


95
96
97
# File 'lib/nombaone/internal/util.rb', line 95

def encode_path_segment(value)
  CGI.escape(value.to_s).gsub("+", "%20")
end

.generate_idempotency_keyString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A fresh idempotency key for one logical POST. Computed once, before the retry loop, so every automatic retry replays the same operation.

Returns:

  • (String)


103
104
105
# File 'lib/nombaone/internal/util.rb', line 103

def generate_idempotency_key
  SecureRandom.uuid
end

.merge_headers(*layers) ⇒ Hash{String => String}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Merge header layers left-to-right; later layers win. A nil value deletes a header (lets a caller strip an SDK default for one request). Header names are case-insensitive, so everything is lowercased once.

Parameters:

  • layers (Array<Hash, nil>)

Returns:

  • (Hash{String => String})


143
144
145
146
147
148
149
150
151
152
# File 'lib/nombaone/internal/util.rb', line 143

def merge_headers(*layers)
  layers.each_with_object({}) do |layer, out|
    next unless layer

    layer.each do |name, value|
      key = name.to_s.downcase
      value.nil? ? out.delete(key) : out[key] = value.to_s
    end
  end
end

.retry_after_seconds(raw) ⇒ Float?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parse a Retry-After header into seconds. Accepts delta-seconds or an HTTP-date; returns nil when absent or unparseable so the caller falls back to its own backoff.

Parameters:

  • raw (String, nil)

Returns:

  • (Float, nil)


123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/nombaone/internal/util.rb', line 123

def retry_after_seconds(raw)
  return nil if raw.nil?

  seconds = Float(raw, exception: false)
  return [seconds, 0.0].max if seconds

  date = begin
    Time.httpdate(raw)
  rescue ArgumentError
    nil
  end
  date ? [date - Time.now, 0.0].max : nil
end

.serialize_body(hash) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prepare a request body hash for the wire: drop omitted keys (keeping explicit nils so nullable fields can be cleared), then camelize.

Parameters:

  • hash (Hash)

Returns:

  • (Hash)


72
73
74
# File 'lib/nombaone/internal/util.rb', line 72

def serialize_body(hash)
  deep_camelize_keys(hash.reject { |_, v| OMIT.equal?(v) })
end

.serialize_query(hash) ⇒ Hash{String => String}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Prepare query params: drop omitted and nil filters (an absent filter, not a null one), camelize keys, and stringify values.

Parameters:

  • hash (Hash)

Returns:

  • (Hash{String => String})


81
82
83
84
85
86
87
# File 'lib/nombaone/internal/util.rb', line 81

def serialize_query(hash)
  hash.each_with_object({}) do |(key, value), out|
    next if OMIT.equal?(value) || value.nil?

    out[camelize(key)] = value.to_s
  end
end

Instance Method Details

#self?.backoff_secondsFloat

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • attempt (Integer)

Returns:

  • (Float)


14
# File 'sig/nombaone/internal.rbs', line 14

def self?.backoff_seconds: (Integer attempt) -> Float

#self?.camelizeString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • key (String, Symbol)

Returns:

  • (String)


8
# File 'sig/nombaone/internal.rbs', line 8

def self?.camelize: (String | Symbol key) -> String

#self?.deep_camelize_keysObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • value (Object)

Returns:

  • (Object)


9
# File 'sig/nombaone/internal.rbs', line 9

def self?.deep_camelize_keys: (untyped value) -> untyped

#self?.encode_path_segmentString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • value (Object)

Returns:

  • (String)


12
# File 'sig/nombaone/internal.rbs', line 12

def self?.encode_path_segment: (untyped value) -> String

#self?.generate_idempotency_keyString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (String)


13
# File 'sig/nombaone/internal.rbs', line 13

def self?.generate_idempotency_key: () -> String

#self?.merge_headersHash[String, String]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • layers (Hash[untyped, untyped], nil)

Returns:

  • (Hash[String, String])


16
# File 'sig/nombaone/internal.rbs', line 16

def self?.merge_headers: (*Hash[untyped, untyped]? layers) -> Hash[String, String]

#self?.retry_after_secondsFloat?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • raw (String, nil)

Returns:

  • (Float, nil)


15
# File 'sig/nombaone/internal.rbs', line 15

def self?.retry_after_seconds: (String? raw) -> Float?

#self?.serialize_bodyHash[String, untyped]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • hash (Hash[untyped, untyped])

Returns:

  • (Hash[String, untyped])


10
# File 'sig/nombaone/internal.rbs', line 10

def self?.serialize_body: (Hash[untyped, untyped] hash) -> Hash[String, untyped]

#self?.serialize_queryHash[String, String]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • hash (Hash[untyped, untyped])

Returns:

  • (Hash[String, String])


11
# File 'sig/nombaone/internal.rbs', line 11

def self?.serialize_query: (Hash[untyped, untyped] hash) -> Hash[String, String]