Module: Mailkube::Serialization

Defined in:
lib/mailkube/serialization.rb,
sig/mailkube/serialization.rbs

Overview

How a Ruby value becomes JSON, a query-string parameter, or a path segment.

One home for every "how does this go on the wire" decision, shared by every resource. Nothing here validates: the server is the authority on what a value means, and its error names are richer than anything the SDK would reproduce. These functions only make values transmissible.

Class Method Summary collapse

Class Method Details

.encode_attachments(attachments) ⇒ Array<Hash>?

Returns JSON-serializable attachments, or nil when there are none.

Parameters:

Returns:

  • (Array<Hash>, nil)

    JSON-serializable attachments, or nil when there are none.



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/mailkube/serialization.rb', line 77

def self.encode_attachments(attachments)
  return nil if attachments.nil? || attachments.empty?

  attachments.map do |item|
    # `[bytes].pack("m0")` rather than `Base64.strict_encode64`: `base64` is a bundled gem from
    # Ruby 3.4, so requiring it without declaring it fails under Bundler, and declaring it would
    # cost this gem its zero-dependency claim.
    entry = { "filename" => item.filename, "content" => [item.content].pack("m0") }
    entry["content_type"] = item.content_type unless item.content_type.nil?
    entry
  end
end

.encode_tags(tags) ⇒ Array<Hash>?

Returns JSON-serializable tags, or nil when there are none.

Parameters:

  • tags (Array<Tag>, nil)

    the tags as supplied.

  • (Array[Tag], nil)

Returns:

  • (Array<Hash>, nil)

    JSON-serializable tags, or nil when there are none.



92
93
94
95
96
# File 'lib/mailkube/serialization.rb', line 92

def self.encode_tags(tags)
  return nil if tags.nil? || tags.empty?

  tags.map { |tag| { "name" => tag.name, "value" => tag.value } }
end

.escape_segment(value) ⇒ String

Escape one interpolated path segment.

ERB::Util.url_encode, and specifically not CGI.escape or URI.encode_www_form_component: those render a space as +, which is the form-encoding rule for a query string, not the percent-encoding rule for a path segment. It is also not cosmetic — an identifier carrying an encoded / or ? would otherwise re-target the request at a different route. erb is a default gem on every supported Ruby, so this costs no dependency.

Parameters:

  • value (String)

    the identifier to interpolate.

  • (String)

Returns:

  • (String)

    the percent-encoded segment.



73
# File 'lib/mailkube/serialization.rb', line 73

def self.escape_segment(value) = ERB::Util.url_encode(value)

.query(filters) ⇒ Hash{String => String}

Render a whole filter set for the query string, dropping the filters the caller omitted.

compact is what makes both wire rules true in one pass, at a cyclomatic complexity of 1: an omitted filter never reaches the wire, and no filters at all yields an empty hash, which Config#build_url turns into no query string rather than a bare ?.

Accumulated into an annotated hash rather than returned from to_h { [k, v] }, because Steep infers a two-element array literal in block-body position as Array[String], not as the [String, String] tuple to_h's signature demands, and reports a BlockBodyTypeMismatch that no annotation on the block can settle.

Parameters:

  • filters (Hash{Symbol => Object})

    the caller's filters, nils included.

  • (Hash[Symbol, untyped])

Returns:

  • (Hash{String => String})

    the query parameters.



56
57
58
59
60
# File 'lib/mailkube/serialization.rb', line 56

def self.query(filters)
  rendered = {} #: Hash[String, String]
  filters.compact.each { |name, value| rendered[name.to_s] = query_value(value) }
  rendered
end

.query_value(value) ⇒ String

Render one query-string parameter, always as a String.

A list becomes a comma-joined value rather than a repeated parameter: the API accepts both, and a flat Hash[String, String] keeps the transport seam simple in every SDK that mirrors this design. A query string has no types, which is why this cannot just call to_iso: that would hand back the Integer 2 for page: 2.

Parameters:

  • value (Object)

    a scalar, a Time, or an array of either.

  • (Object)

Returns:

  • (String)

    the parameter's string form.



32
33
34
35
36
# File 'lib/mailkube/serialization.rb', line 32

def self.query_value(value)
  return value.map { |item| query_scalar(item) }.join(",") if value.is_a?(Array)

  query_scalar(value)
end

.to_iso(value) ⇒ String?

Render an instant for a JSON body, passing an already-formatted string through.

Returns nil for nil on purpose: that is what lets a request body be one hash literal followed by a single compact, and why an unset field is absent from the wire rather than sent as null. Compare query_value, which can never return nil.

Parameters:

  • value (Time, String, nil)

    the caller's instant.

  • (Time, String, nil)

Returns:

  • (String, nil)

    the ISO-8601 rendering, or nil.



21
# File 'lib/mailkube/serialization.rb', line 21

def self.to_iso(value) = value.is_a?(Time) ? value.iso8601 : value