Module: CLI::UI::Frame

Defined in:
lib/cli/ui/frame.rb,
lib/cli/ui/frame/frame_stack.rb,
lib/cli/ui/frame/frame_style.rb,
lib/cli/ui/frame/frame_style/box.rb,
lib/cli/ui/frame/frame_style/bracket.rb

Defined Under Namespace

Modules: FrameStack, FrameStyle Classes: UnnestedFrameException

Constant Summary collapse

DEFAULT_FRAME_COLOR =
CLI::UI.resolve_color(:cyan)

Class Method Summary collapse

Class Method Details

.close(text, color: nil, elapsed: nil, frame_style: nil) ⇒ Object

Closes a frame Automatically called for a block-form open

Attributes

  • text - (required) the text/title to output in the frame

Options

  • :color - The color of the frame. Defaults to nil

  • :elapsed - How long did the frame take? Defaults to nil

  • frame_style - The frame style to use for this frame. Defaults to nil

Example

CLI::UI::Frame.close('Close')

Default Output:

┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Raises

MUST be inside an open frame or it raises a UnnestedFrameException


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/cli/ui/frame.rb', line 187

def close(text, color: nil, elapsed: nil, frame_style: nil)
  fs_item = FrameStack.pop
  raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item

  color = CLI::UI.resolve_color(color) || fs_item.color
  frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style

  kwargs = {}
  if elapsed
    kwargs[:right_text] = "(#{elapsed.round(2)}s)"
  end

  CLI::UI.raw do
    print prefix.chop
    puts frame_style.close(text, color: color, **kwargs)
  end
end

.divider(text, color: nil, frame_style: nil) ⇒ Object

Adds a divider in a frame Used to separate information within a single frame

Attributes

  • text - (required) the text/title to output in the frame

Options

  • :color - The color of the frame. Defaults to DEFAULT_FRAME_COLOR

  • frame_style - The frame style to use for this frame

Example

CLI::UI::Frame.open('Open') { CLI::UI::Frame.divider('Divider') }

Default Output:

┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Raises

MUST be inside an open frame or it raises a UnnestedFrameException


148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/cli/ui/frame.rb', line 148

def divider(text, color: nil, frame_style: nil)
  fs_item = FrameStack.pop
  raise UnnestedFrameException, "No frame nesting to unnest" unless fs_item

  color = CLI::UI.resolve_color(color) || fs_item.color
  frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style

  CLI::UI.raw do
    print prefix.chop
    puts frame_style.divider(text, color: color)
  end

  FrameStack.push(fs_item)
end

.frame_styleObject


13
14
15
# File 'lib/cli/ui/frame.rb', line 13

def frame_style
  @frame_style ||= FrameStyle::Box
end

.frame_style=(frame_style) ⇒ Object

Set the default frame style.

Raises ArgumentError if frame_style is not valid

Attributes

  • symbol or FrameStyle - the default frame style to use for frames


25
26
27
# File 'lib/cli/ui/frame.rb', line 25

def frame_style=(frame_style)
  @frame_style = CLI::UI.resolve_style(frame_style)
end

.open(text, color: DEFAULT_FRAME_COLOR, failure_text: nil, success_text: nil, timing: nil, frame_style: self.frame_style) ⇒ Object

Opens a new frame. Can be nested Can be invoked in two ways: block and blockless

  • In block form, the frame is closed automatically when the block returns

  • In blockless form, caller MUST call Frame.close when the frame is logically done

  • Blockless form is strongly discouraged in cases where block form can be made to work

The return value of the block determines if the block is a “success” or a “failure”

Attributes

  • text - (required) the text/title to output in the frame

Options

  • :color - The color of the frame. Defaults to DEFAULT_FRAME_COLOR

  • :failure_text - If the block failed, what do we output? Defaults to nil

  • :success_text - If the block succeeds, what do we output? Defaults to nil

  • :timing - How long did the frame content take? Invalid for blockless. Defaults to true for the block form

  • frame_style - The frame style to use for this frame

Example

Block Form (Assumes CLI::UI::StdoutRouter.enable has been called)
CLI::UI::Frame.open('Open') { puts 'hi' }

Default Output:

┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ hi
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (0.0s) ━━
Blockless Form
CLI::UI::Frame.open('Open')

Default Output:

┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/cli/ui/frame.rb', line 70

def open(
  text,
  color: DEFAULT_FRAME_COLOR,
  failure_text: nil,
  success_text: nil,
  timing:       nil,
  frame_style:  self.frame_style
)
  frame_style = CLI::UI.resolve_style(frame_style)
  color = CLI::UI.resolve_color(color)

  unless block_given?
    if failure_text
      raise ArgumentError, "failure_text is not compatible with blockless invocation"
    elsif success_text
      raise ArgumentError, "success_text is not compatible with blockless invocation"
    elsif timing
      raise ArgumentError, "timing is not compatible with blockless invocation"
    end
  end

  t_start = Time.now
  CLI::UI.raw do
    print prefix.chop
    puts frame_style.open(text, color: color)
  end
  FrameStack.push(color: color, style: frame_style)

  return unless block_given?

  closed = false
  begin
    success = false
    success = yield
  rescue
    closed = true
    t_diff = elasped(t_start, timing)
    close(failure_text, color: :red, elapsed: t_diff)
    raise
  else
    success
  ensure
    unless closed
      t_diff = elasped(t_start, timing)
      if success != false
        close(success_text, color: color, elapsed: t_diff)
      else
        close(failure_text, color: :red, elapsed: t_diff)
      end
    end
  end
end

.prefix(color: Thread.current[:cliui_frame_color_override]) ⇒ Object

Determines the prefix of a frame entry taking multi-nested frames into account

Options


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/cli/ui/frame.rb', line 211

def prefix(color: Thread.current[:cliui_frame_color_override])
  +''.tap do |output|
    items = FrameStack.items

    items[0..-2].each do |item|
      output << item.color.code << item.frame_style.prefix
    end

    if (item = items.last)
      final_color = color || item.color
      output << CLI::UI.resolve_color(final_color).code \
        << item.frame_style.prefix \
        << ' ' \
        << CLI::UI::Color::RESET.code
    end
  end
end

.prefix_widthObject

The width of a prefix given the number of Frames in the stack


230
231
232
233
234
235
236
# File 'lib/cli/ui/frame.rb', line 230

def prefix_width
  w = FrameStack.items.reduce(0) do |width, item|
    width + item.frame_style.prefix_width
  end

  w.zero? ? w : w + 1
end

.with_frame_color_override(color) ⇒ Object

Override a color for a given thread.

Attributes

  • color - The color to override to


244
245
246
247
248
249
250
# File 'lib/cli/ui/frame.rb', line 244

def with_frame_color_override(color)
  prev = Thread.current[:cliui_frame_color_override]
  Thread.current[:cliui_frame_color_override] = color
  yield
ensure
  Thread.current[:cliui_frame_color_override] = prev
end