Module: CLI::UI::Frame

Defined in:
lib/cli/ui/frame.rb

Defined Under Namespace

Modules: FrameStack Classes: UnnestedFrameException

Constant Summary collapse

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

Class Method Summary collapse

Class Method Details

.close(text, color: DEFAULT_FRAME_COLOR, elapsed: 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 DEFAULT_FRAME_COLOR

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

Example

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

Output:

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


122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/cli/ui/frame.rb', line 122

def close(text, color: DEFAULT_FRAME_COLOR, elapsed: nil)
  color = CLI::UI.resolve_color(color)

  FrameStack.pop
  kwargs = {}
  if elapsed
    kwargs[:right_text] = "(#{elapsed.round(2)}s)"
  end
  CLI::UI.raw do
    puts edge(text, color: color, first: CLI::UI::Box::Heavy::BL, **kwargs)
  end
end

.divider(text, color: 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

Example

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

Output:

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

Raises

MUST be inside an open frame or it raises a UnnestedFrameException



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/cli/ui/frame.rb', line 159

def divider(text, color: nil)
  fs_item = FrameStack.pop
  raise UnnestedFrameException, "no frame nesting to unnest" unless fs_item
  color = CLI::UI.resolve_color(color)
  item  = CLI::UI.resolve_color(fs_item)

  CLI::UI.raw do
    puts edge(text, color: (color || item), first: CLI::UI::Box::Heavy::DIV)
  end
  FrameStack.push(item)
end

.open(text, color: DEFAULT_FRAME_COLOR, failure_text: nil, success_text: nil, timing: nil) ⇒ 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

Example

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

Output:

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

Output:

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


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
# File 'lib/cli/ui/frame.rb', line 50

def open(
  text,
  color: DEFAULT_FRAME_COLOR,
  failure_text: nil,
  success_text: nil,
  timing:       nil
)
  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.nil?
      raise ArgumentError, "timing is not compatible with blockless invocation"
    end
  end

  timing = true if timing.nil?

  t_start = Time.now.to_f
  CLI::UI.raw do
    puts edge(text, color: color, first: CLI::UI::Box::Heavy::TL)
  end
  FrameStack.push(color)

  return unless block_given?

  closed = false
  begin
    success = false
    success = yield
  rescue
    closed = true
    t_diff = timing ? (Time.now.to_f - t_start) : nil
    close(failure_text, color: :red, elapsed: t_diff)
    raise
  else
    success
  ensure
    unless closed
      t_diff = timing ? (Time.now.to_f - t_start) : nil
      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: nil) ⇒ Object

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

Options

  • :color - The color of the prefix. Defaults to +Thread.current+ or nil



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/cli/ui/frame.rb', line 177

def prefix(color: nil)
  pfx = +''
  items = FrameStack.items
  items[0..-2].each do |item|
    pfx << CLI::UI.resolve_color(item).code << CLI::UI::Box::Heavy::VERT
  end
  if item = items.last
    c = Thread.current[:cliui_frame_color_override] || color || item
    pfx << CLI::UI.resolve_color(c).code \
      << CLI::UI::Box::Heavy::VERT << ' ' << CLI::UI::Color::RESET.code
  end
  pfx
end

.prefix_widthObject

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



207
208
209
210
# File 'lib/cli/ui/frame.rb', line 207

def prefix_width
  w = FrameStack.items.size
  w.zero? ? 0 : w + 1
end

.with_frame_color_override(color) ⇒ Object

Override a color for a given thread.

Attributes

  • color - The color to override to



197
198
199
200
201
202
203
# File 'lib/cli/ui/frame.rb', line 197

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