Module: Eft

Defined in:
lib/eft.rb,
lib/eft/version.rb

Defined Under Namespace

Modules: CfgCancel, CfgEsc, CfgNo, CfgOK, CfgYes Classes: Cfg, CfgAsk, CfgAskPass, CfgAskYesNo, CfgCheck, CfgGauge, CfgMenu, CfgRadio, CfgShowInfo, CfgShowMsg, CfgShowText, Error

Constant Summary collapse

OU =
Obfusk::Util
WHIPTAIL =

NB: dialog works as well as whiptail, but these options will be incompatible: ---button (is: ---label), --scrolltext

'whiptail'
EXIT =
{ ok_yes: 0, cancel_no: 1, esc: 255 }
CHECK_OPTS =
['--separate-output']
OPT =
->(o,x) { o.select { |k,v| OPTS[x][k] }
.map { |k,v| OPTS[x][k][v] } }
ON_OFF =
->(x) { x ? 'on' : 'off' }
WHAT =

translate method to option

{                                                      # {{{1
  show_info:  '--infobox'     , show_msg:   '--msgbox'    ,
  show_text:  '--textbox'     , ask:        '--inputbox'  ,
  ask_pass:   '--passwordbox' , ask_yesno:  '--yesno'     ,
  menu:       '--menu'        , check:      '--checklist' ,
  radio:      '--radiolist'   , gauge:      '--gauge'     ,
}
OPTS =

options per category

{                                                      # {{{1
  all: {
    title:          ->(t) { ['--title', t] },
    backtitle:      ->(t) { ['--backtitle', t] },
    scroll:         ->(b) { b ? ['--scrolltext'] : [] },      # ????
  },
  ok: {
    ok_button:      ->(t) { ['--ok-button', t] },
  },
  cancel: {
    cancel_button:  ->(t) { ['--cancel-button', t] },
    no_cancel:      ->(b) { b ? ['--nocancel'] : [] },
  },
  yes: {
    yes_button:     ->(t) { ['--yes-button', t] },
  },
  no: {
    no_button:      ->(t) { ['--no-button', t] },
    default_no:     ->(b) { b ? ['--default-no'] : [] },
  },
  menu: {
    selected:       ->(i) { ['--default-item', i] },
  },
}
VERSION =
'0.4.0'
DATE =
'2014-02-20'

Class Method Summary collapse

Class Method Details

._file_or_temp(opts, &b) ⇒ Object

call block w/ either opts[:file] or a tempfile w/ contents opts[:text]

Raises:



309
310
311
312
313
314
315
316
317
318
# File 'lib/eft.rb', line 309

def self._file_or_temp(opts, &b)                              # {{{1
  file = opts[:file]; text = opts[:text]
  raise Error, 'can\'t have file and text' if file && text
  raise Error, 'must have file or text' if !file && !text
  if file
    b[file]
  else
    Tempfile.open('eft') { |f| f.write text; f.close; b[f.path] }
  end
end

._run_whip(args) ⇒ Hash

run whiptail

Returns:

  • (Hash)

    { exit: exitstatus, lines: chomped_lines }



298
299
300
301
302
303
# File 'lib/eft.rb', line 298

def self._run_whip(args)
  IO.pipe do |r, w|
    s = OU.spawn_w(*args, err: w); w.close
    { exit: s.exitstatus, lines: r.readlines.map(&:chomp) }
  end
end

._whip(what, text, cfg, opts, args = [], &b) ⇒ Object

process options, run whiptail, call b[lines] or on_{cancel,no,esc}[]

Raises:

  • Error if unknown exitstatus



264
265
266
267
268
269
270
271
272
273
274
# File 'lib/eft.rb', line 264

def self._whip(what, text, cfg, opts, args = [], &b)          # {{{1
  o = _whip_opts what, cfg, opts; c = _whip_cmd text, opts, o, args
  r = _run_whip c
  case r[:exit]
  when EXIT[:ok_yes]    ; b[r[:lines]] if b
  when EXIT[:cancel_no] ; cfg.call :on_cancel; cfg.call :on_no
  when EXIT[:esc]       ; cfg.call :on_esc
  else                    raise Error, 'unknown exitstatus'
  end
  nil
end

._whip_cmd(text, opts, opt_args, args) ⇒ Object

whiptail command



288
289
290
291
292
293
294
# File 'lib/eft.rb', line 288

def self._whip_cmd(text, opts, opt_args, args)                # {{{1
  h = opts[:height] || OU::Term.lines   - 4
  w = opts[:width]  || OU::Term.columns - 4
  s = opts.has_key?(:subheight) ? [opts[:subheight] || h - 8] : []
  a = [WHIPTAIL] + opt_args + ['--', text, h, w] + s + args
  a.map(&:to_s)
end

._whip_opts(what, cfg, opts) ⇒ Object

process whiptail options



277
278
279
280
281
282
283
284
285
# File 'lib/eft.rb', line 277

def self._whip_opts(what, cfg, opts)                          # {{{1
   cfg._opts + [                  OPT[opts,:all],
    cfg.respond_to?(:on_ok)     ? OPT[opts,:ok]     : [],
    cfg.respond_to?(:on_cancel) ? OPT[opts,:cancel] : [],
    cfg.respond_to?(:on_yes)    ? OPT[opts,:yes]    : [],
    cfg.respond_to?(:on_no)     ? OPT[opts,:no]     : [],
    what == :menu               ? OPT[opts,:menu]   : [],
  ] .flatten + [WHAT[what]]
end

.ask(text, opts = {}, &b) ⇒ Object

ask for input w/ OK/Cancel buttons (and default)



184
185
186
187
188
189
# File 'lib/eft.rb', line 184

def self.ask(text, opts = {}, &b)
  c = CfgAsk.new(&b); a = opts[:default] ? [opts[:default]] : []
  _whip(:ask, text, c, opts, a) do |lines|
    c.call :on_ok, lines.first
  end
end

.ask_pass(text, opts = {}, &b) ⇒ Object

ask for password w/ OK/Cancel buttons



192
193
194
195
196
197
# File 'lib/eft.rb', line 192

def self.ask_pass(text, opts = {}, &b)
  c = CfgAskPass.new(&b)
  _whip(:ask_pass, text, c, opts) do |lines|
    c.call :on_ok, lines.first
  end
end

.ask_yesno(text, opts = {}, &b) ⇒ Object

ask w/ Yes/No buttons



200
201
202
203
# File 'lib/eft.rb', line 200

def self.ask_yesno(text, opts = {}, &b)
  c = CfgAskYesNo.new(&b)
  _whip(:ask_yesno, text, c, opts) { c.call :on_yes }
end

.check(text, opts = {}, &b) ⇒ Object

choose checkboxes w/ OK/Cancel buttons



221
222
223
224
225
226
227
228
# File 'lib/eft.rb', line 221

def self.check(text, opts = {}, &b)                           # {{{1
  c = CfgCheck.new(&b); i = c._check
  o = opts.merge(subheight: opts[:list_height]).freeze
  a = i.map { |x| [x[:tag],x[:item],ON_OFF[x[:selected]]] } .flatten
  _whip(:check, text, c, o, a) do |lines|
    c.call :on_ok, lines
  end
end

.gauge(text, percent, opts = {}, &b) ⇒ Object

show gauge; use lambda passed to block to move it forward by passing it percent[, message]



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/eft.rb', line 245

def self.gauge(text, percent, opts = {}, &b)                  # {{{1
  IO.pipe do |r, w|
    mv  = ->(pct, msg = nil) {
      msg ? w.puts('XXX', pct, msg, 'XXX', pct) : w.puts(pct) # WTF!
    }
    c   = CfgGauge.new
    o   = _whip_opts :gauge, c, opts
    c   = _whip_cmd text, opts, o, [percent]
    pid = OU.spawn(*c, in: r)
    r.close; b[mv]; w.close; Process.wait pid
    raise Error, 'exitstatus != 0' if $?.exitstatus != 0
  end                                                         # }}}1
end

choose from menu w/ OK/Cancel buttons



208
209
210
211
212
213
214
215
216
# File 'lib/eft.rb', line 208

def self.menu(text, opts = {}, &b)                            # {{{1
  c = CfgMenu.new(&b); m = c._menu
  o = opts.merge(subheight: opts[:menu_height]).freeze
  a = m.map { |x| [x[:tag],x[:item]] } .flatten
  t = Hash[m.map { |x| [x[:tag],x] }]
  _whip(:menu, text, c, o, a) do |lines|
    tag = lines.first; t[tag][:block][tag]
  end
end

.radio(text, opts = {}, &b) ⇒ Object

choose radiobutton w/ OK/Cancel buttons



231
232
233
234
235
236
237
238
239
# File 'lib/eft.rb', line 231

def self.radio(text, opts = {}, &b)                           # {{{1
  s = opts[:selected] ; f = ->(x) { ON_OFF[x == s] }
  c = CfgRadio.new(&b); i = c._radio
  o = opts.merge(subheight: opts[:list_height]).freeze
  a = i.map { |x| [x[:tag],x[:item],f[x[:tag]]] } .flatten
  _whip(:radio, text, c, o, a) do |lines|
    c.call :on_ok, lines.first
  end                                                         # }}}1
end

.show_info(text, opts = {}) ⇒ Object

show message w/o buttons, don't clear screen



163
164
165
# File 'lib/eft.rb', line 163

def self.show_info(text, opts = {})
  _whip :show_info, text, CfgShowInfo.new, opts
end

.show_msg(text, opts = {}, &b) ⇒ Object

show message w/ OK button



168
169
170
171
# File 'lib/eft.rb', line 168

def self.show_msg(text, opts = {}, &b)
  c = CfgShowMsg.new(&b)
  _whip(:show_msg, text, c, opts) { c.call :on_ok }
end

.show_text(opts = {}, &b) ⇒ Object

show file contents or text w/ OK button



174
175
176
177
178
179
# File 'lib/eft.rb', line 174

def self.show_text(opts = {}, &b)
  _file_or_temp opts do |f|
    c = CfgShowText.new(&b)
    _whip(:show_text, f, c, opts) { c.call :on_ok }
  end
end