Module: Xolo::Core::Output

Defined in:
lib/xolo/core/output.rb

Overview

Methods for formattng and sending output to stdout Should be included in classes as needed

NOTE: Help output is auto-generated by ‘optimist’ The methods here are mostly for presenting info like columnizd lists and reports and the like.

Constant Summary collapse

DEFAULT_LINE_WIDTH =

This is used when we are not outputting to a terminal usually we’re being piped or not running in a terminal so the lines should be as long as they want

2000

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(extender) ⇒ Object

when this module is extended



47
48
49
# File 'lib/xolo/core/output.rb', line 47

def self.extended(extender)
  Xolo.verbose_extend extender, self
end

.included(includer) ⇒ Object

when this module is included



42
43
44
# File 'lib/xolo/core/output.rb', line 42

def self.included(includer)
  Xolo.verbose_include includer, self
end

Instance Method Details

#col_widths(data, header_row = []) ⇒ Array<Integer>

Given an Array of Arrays representing rows and columns of data figure out the widest width of each column and return an array of integers representing those widths

Parameters:

  • data (Array<Array>)

    The rows and columns of data

  • header_row (Array) (defaults to: [])

    An optional header row to include in the width calculation.

Returns:

  • (Array<Integer>)

    the max widths of each column of data.



231
232
233
234
235
236
237
238
239
240
# File 'lib/xolo/core/output.rb', line 231

def col_widths(data, header_row = [])
  widths = header_row.map { |c| c.to_s.length }
  data.each do |row|
    row.each_index do |col|
      this_width = row[col].to_s.length
      widths[col] = this_width if this_width > widths[col].to_i
    end # do field
  end # do line
  widths
end

#format_multiline_indent(value, indent:) ⇒ String

format a multi-line value by prepending the desired indentation to all but the first line, which is expected to be indented in-place where its being used.

Parameters:

  • value (String)

    the value to format

  • indent (Integer)

    the number of spaces to indent all but the first line

Returns:

  • (String)

    the formatted value



84
85
86
87
88
89
90
91
# File 'lib/xolo/core/output.rb', line 84

def format_multiline_indent(value, indent:)
  value = value.to_s
  return value unless value.include? "\n"

  lines = value.split("\n")
  lines[1..-1].each { |line| line.prepend ' ' * indent }
  lines.join("\n")
end

#generate_report(lines, type: :fixed, header_row: [], title: nil) ⇒ String

Generate a report of rowed/columned data, either fixed-width or tab-delimited.

Title and header lines are pre-pended with ‘# ’ for easier exclusion when using the report as input for some other program. If the :type is :fixed, so will the column header line. (however, for parsing this data, try using the –json option)

Parameters:

  • lines (Array<Array>)

    the rows and columns of data

  • type (Symbol) (defaults to: :fixed)

    :fixed or :tab, defaults to :fixed

Returns:

  • (String)

    the formatted report.

Raises:

  • (ArgumentError)


146
147
148
149
150
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
# File 'lib/xolo/core/output.rb', line 146

def generate_report(lines, type: :fixed, header_row: [], title: nil)
  return Xolo::BLANK if lines.pix_empty?

  raise ArgumentError, 'The first argument must be an Array' unless lines.is_a?(Array)
  raise ArgumentError, 'The header_row must be an Array' unless header_row.is_a? Array

  # tab delim is easy
  if type == :tab
    report_tab = header_row.join("\t")
    lines.each { |line| report_tab += "\n#{line.join("\t")}" }
    return report_tab.strip
  end # if :tab

  # below here, fixed width

  line_width, format_str = width_and_format(lines, header_row)

  # title if given
  report = title ? +"# #{title}\n" : +''

  unless header_row.empty?
    unless header_row.size == lines[0].size
      raise ArgumentError,
            "Header row must have #{lines[0].count} items"
    end

    # then the header line if given
    report +=  format_str % header_row
    # add a separator
    report +=  '#' + ('-' * (line_width - 1)) + "\n"
  end
  # add the rows
  lines.each { |line| report += format_str % line }

  report
end

#list_in_cols(header, list) ⇒ void

This method returns an undefined value.

TODO: Move this out of Xolo::Core Display a list of items in as many columns as possible based on terminal width, e.g. with 3 cols:

a thing        another thing    third thing
thing2         line2 thing      third line 2
line3 thing    another one      and yet a 3rd
oh my line4    4 line ok?       yes, this is it

and if the list is longer than terminal height, pipe it through ‘less’

Parameters:

  • header (String)

    A string to display at the top prepended with a ‘#’ and appended with a newline and a line of ######‘s of the same length.

  • list (Array<String>)

    the items to list



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/xolo/core/output.rb', line 112

def list_in_cols(header, list)
  longest_list_item = list.map(&:size).max
  use_columns = (longest_list_item + 5) < terminal_width

  list_to_display = use_columns ? highline_cli.list(list, :columns_down) : list.join("\n")

  output = +"# #{header}\n"
  output << '#' * (terminal_width - 5)
  output << "\n"
  output << list_to_display

  show_text output
end

#show_text(text, show_help = true) ⇒ Object

Send a string to the terminal, possibly piping it through ‘less’ if the number of lines is greater than the number of terminal lines

Parameters:

  • text (String)

    the text to send to the terminal

  • show_help (Boolean) (defaults to: true)

    should the text have a line at the top showing basic ‘less’ key commands.



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/xolo/core/output.rb', line 262

def show_text(text, show_help = true)
  unless use_less?(text)
    puts text
    return
  end

  if show_help
    help = "# -- Using /usr/bin/less: ' ' next, 'b' prev, 'q' exit, 'h' help --"
    text = "#{help}\n#{text}"
  end

  # point stdout through less, print, then restore stdout
  less = IO.popen('/usr/bin/less', 'w')

  begin
    less.puts text

  # this catches the quitting of 'less' before all the output
  # is displayed
  rescue Errno::EPIPE
    true
  ensure
    less&.close
  end
end

#terminal_heightInteger

Returns how many rows high is our terminal?.

Returns:

  • (Integer)

    how many rows high is our terminal?



60
61
62
# File 'lib/xolo/core/output.rb', line 60

def terminal_height
  IO.console.winsize.first
end

#terminal_widthInteger

Returns how many columns wide is our terminal?.

Returns:

  • (Integer)

    how many columns wide is our terminal?



66
67
68
# File 'lib/xolo/core/output.rb', line 66

def terminal_width
  IO.console.winsize.last
end

#terminal_word_wrapInteger

Returns how wide is our word wrap? terminal-width minus 5.

Returns:

  • (Integer)

    how wide is our word wrap? terminal-width minus 5



72
73
74
# File 'lib/xolo/core/output.rb', line 72

def terminal_word_wrap
  @terminal_word_wrap ||= terminal_width - 5
end

#use_less?(text) ⇒ Boolean

Should a given string be displayed via /usr/bin/less? true if stdout is a tty AND the string is > (terminal height - 2) The - 2 accounts for the final newline and an extra line at the bottom of the terminal, for better visual results

Returns:

  • (Boolean)


247
248
249
# File 'lib/xolo/core/output.rb', line 247

def use_less?(text)
  $stdout.tty? && text.lines.size > (terminal_height - 2)
end

#width_and_format(lines, header_row = []) ⇒ Array<Integer, String>

Given an Array of Arrays representing rows and columns of data, figure out the appropriate line-width for the longest line and the printf format string to create the columns

Parameters:

  • lines (Array<Array>)

    The rows and columns of data

  • header_row (Array) (defaults to: [])

    An optional header row to include in the width calculation.

Returns:

  • (Array<Integer, String>)

    the line width and format string



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/xolo/core/output.rb', line 196

def width_and_format(lines, header_row = [])
  # below here, fixed width
  format_str = +''
  line_width = 0
  header_row[0] = "# #{header_row[0]}"

  col_widths(lines, header_row).each do |w|
    # make sure there's a space between columns
    col_width = w + 1

    # add the column to the printf format
    format_str += "%-#{col_width}s"
    line_width += col_width
  end
  format_str += "\n"

  # if needed, limit the total line width for the header the width of the terminal
  max_width = $stdout.tty? ? terminal_word_wrap : DEFAULT_LINE_WIDTH
  line_width = max_width if line_width > max_width

  [line_width, format_str]
end