Class: OMQ::CLI::Formatter
- Inherits:
-
Object
- Object
- OMQ::CLI::Formatter
- Defined in:
- lib/omq/cli/formatter.rb
Overview
Handles encoding/decoding messages in the configured format.
Constant Summary collapse
- LINE_ESCAPES =
Whitespace/backslash → visible escape sequence used by sanitize. Everything else outside printable ASCII collapses to ‘.’ via a single String#tr call.
{ "\t" => '\\t', "\n" => '\\n', "\r" => '\\r', "\\" => '\\\\', }.freeze
Class Method Summary collapse
-
.preview(parts, format: nil, wire_size: nil, uncompressed_size: nil) ⇒ String
Formats message parts for human-readable preview (logging).
-
.preview_frame(part, limit: 12) ⇒ String
Renders one frame or decoded object for Formatter.preview.
-
.sanitize(bytes) ⇒ String
Escapes bytes so a preview/body line is guaranteed single-line on a shared tty.
Instance Method Summary collapse
-
#decode(line) ⇒ Array<String>
Decodes a formatted input line into message parts.
-
#decode_marshal(io) ⇒ Object?
Decodes one Marshal object from the given IO stream.
-
#decode_msgpack(io) ⇒ Object?
Decodes one MessagePack object from the given IO stream.
-
#encode(parts) ⇒ String
Encodes message parts into a printable string for output.
-
#initialize(format) ⇒ Formatter
constructor
A new instance of Formatter.
Constructor Details
#initialize(format) ⇒ Formatter
Returns a new instance of Formatter.
9 10 11 |
# File 'lib/omq/cli/formatter.rb', line 9 def initialize(format) @format = format end |
Class Method Details
.preview(parts, format: nil, wire_size: nil, uncompressed_size: nil) ⇒ String
Formats message parts for human-readable preview (logging). When wire_size is given (‘omq-zstd`), the header also shows the compressed on-the-wire size: “(29B wire=12B)”. Accepts either wire-side Array<String> (monitor events) or post-decode app parts that may contain non-String objects (e.g. -M Marshal.load output).
When format is :marshal, parts is the raw Ruby object itself (not an Array of frames); the preview inspects it so the reader sees the actual payload structure (e.g. ‘[nil, :foo, “bar”]`) instead of a meaningless “1obj” header. For marshal, uncompressed_size is the Marshal.dump bytesize (known to the caller, which already serialized for send or received the wire frame for recv) — passed through instead of redumping here.
114 115 116 117 118 119 120 121 |
# File 'lib/omq/cli/formatter.rb', line 114 def self.preview(parts, format: nil, wire_size: nil, uncompressed_size: nil) case format when :marshal marshal_preview(parts, uncompressed_size: uncompressed_size, wire_size: wire_size) else frames_preview(parts, format: format, wire_size: wire_size) end end |
.preview_frame(part, limit: 12) ⇒ String
Renders one frame or decoded object for preview. Strings are sanitized byte-wise (first limit bytes); non-String objects fall back to #inspect (always single-line) truncated at limit bytes.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/omq/cli/formatter.rb', line 175 def self.preview_frame(part, limit: 12) unless part.is_a?(String) s = part.inspect return s.bytesize > limit ? "#{s.byteslice(0, limit)}…" : s end bytes = part.b # Empty frames must render as a visible marker, not as the empty # string — otherwise joining with "|" would produce misleading # output like "|body" for REP/REQ-style envelopes where the first # wire frame is an empty delimiter. return "''" if bytes.empty? sample = bytes[0, limit] printable = sample.count("\x20-\x7e") if printable < sample.bytesize / 2 "[#{bytes.bytesize}B]" elsif bytes.bytesize > limit "#{sanitize(sample)}…" else sanitize(sample) end end |
.sanitize(bytes) ⇒ String
Escapes bytes so a preview/body line is guaranteed single-line on a shared tty. Tab/newline/CR/backslash render as literal t/n/r/\; other non-printables collapse to ‘.’. Forced to binary encoding first to prevent UTF-8 quirks from rendering raw LF bytes.
209 210 211 |
# File 'lib/omq/cli/formatter.rb', line 209 def self.sanitize(bytes) bytes.b.gsub(/[\t\n\r\\]/, LINE_ESCAPES).tr("^ -~", ".") end |
Instance Method Details
#decode(line) ⇒ Array<String>
Decodes a formatted input line into message parts.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/omq/cli/formatter.rb', line 43 def decode(line) case @format when :ascii, :marshal line.chomp.split("\t", -1) when :quoted line.chomp.split("\t", -1).map { |p| "\"#{p}\"".undump } when :raw [line] when :jsonl arr = JSON.parse(line.chomp) abort "JSON Lines input must be an array of strings" unless arr.is_a?(Array) && arr.all? { |e| e.is_a?(String) } arr end end |
#decode_marshal(io) ⇒ Object?
Decodes one Marshal object from the given IO stream.
63 64 65 66 67 |
# File 'lib/omq/cli/formatter.rb', line 63 def decode_marshal(io) Marshal.load(io) rescue EOFError, TypeError nil end |
#decode_msgpack(io) ⇒ Object?
Decodes one MessagePack object from the given IO stream.
74 75 76 77 78 79 |
# File 'lib/omq/cli/formatter.rb', line 74 def decode_msgpack(io) @msgpack_unpacker ||= MessagePack::Unpacker.new(io) @msgpack_unpacker.read rescue EOFError nil end |
#encode(parts) ⇒ String
Encodes message parts into a printable string for output.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/omq/cli/formatter.rb', line 18 def encode(parts) case @format when :ascii parts.map { |p| p.b.gsub(/[^[:print:]\t]/, ".") }.join("\t") << "\n" when :quoted parts.map { |p| p.b.dump[1..-2] }.join("\t") << "\n" when :raw parts.each_with_index.map do |p, i| Protocol::ZMTP::Codec::Frame.new(p.to_s, more: i < parts.size - 1).to_wire end.join when :jsonl JSON.generate(parts) << "\n" when :msgpack MessagePack.pack(parts) when :marshal # Under -M, `parts` is a single Ruby object (not a frame array). parts.inspect << "\n" end end |