eft 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 801797fc4df36523e6baaad909c55c6cb0221d60
4
+ data.tar.gz: f3496e92b58c74b9d1cfa9b3624e06c0f79b34b5
5
+ SHA512:
6
+ metadata.gz: 208b327f0aa6d1ead392ca2682113dfea63807361abdc942f5e64417f9da5bf89763652926c8aaf2165524254aaa3c8b1d1dc1094f43321c5ab53bba3808a508
7
+ data.tar.gz: 8ea0d807d62731ffaca846db012209d848cd05f4f97a94131e3c47fcc184cb694bd9bff84603938d00c6e44c276704daa862cddc0bd6bd5eddf3d540c2d18784
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ []: {{{1
2
+
3
+ File : README.md
4
+ Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
+ Date : 2014-02-20
6
+
7
+ Copyright : Copyright (C) 2014 Felix C. Stegerman
8
+ Version : v0.4.0
9
+
10
+ []: }}}1
11
+
12
+ [![Gem Version](https://badge.fury.io/rb/eft.png)](http://badge.fury.io/rb/eft)
13
+
14
+ ## Description
15
+ []: {{{1
16
+
17
+ eft - ruby + whiptail
18
+
19
+ Eft is a ruby dsl that wraps `whiptail` [1] to display dialog boxes;
20
+ see `example.rb` for examples.
21
+
22
+ ```ruby
23
+ Eft.ask('What is your name?') do |q|
24
+ q.on_ok { |name| puts "Hello, #{name}!" }
25
+ end
26
+ ```
27
+
28
+ []: }}}1
29
+
30
+ ## Specs & Docs
31
+
32
+ ```bash
33
+ $ rake spec # TODO
34
+ $ rake docs
35
+ ```
36
+
37
+ ## TODO
38
+
39
+ * specs! (how to automate tests of whiptail? - I don't know!)
40
+ * --noitem?
41
+ * choose between whiptail and dialog?
42
+ * extend w/ dialog's other dialogs?
43
+ * remove dependency on obfusk-util?
44
+
45
+ ## License
46
+
47
+ LGPLv3+ [2].
48
+
49
+ ## References
50
+
51
+ [1] Newt (and whiptail)
52
+ --- http://en.wikipedia.org/wiki/Newt_(programming_library)
53
+
54
+ [2] GNU Lesser General Public License, version 3
55
+ --- http://www.gnu.org/licenses/lgpl-3.0.html
56
+
57
+ []: ! ( vim: set tw=70 sw=2 sts=2 et fdm=marker : )
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ desc 'Run specs'
2
+ task :spec do
3
+ puts 'No automated tests yet - please manually test the example'
4
+ # sh 'rspec -c'
5
+ end
6
+
7
+ desc 'Run specs verbosely'
8
+ task 'spec:verbose' do
9
+ puts 'No automated tests yet - please manually test the example'
10
+ # sh 'rspec -cfd'
11
+ end
12
+
13
+ desc 'Run specs verbosely, view w/ less'
14
+ task 'spec:less' do
15
+ puts 'No automated tests yet - please manually test the example'
16
+ # sh 'rspec -cfd --tty | less -R'
17
+ end
18
+
19
+ desc 'Check for warnings'
20
+ task :warn do
21
+ sh 'ruby -w -I lib -I ../rb-obfusk-util/lib -r eft -e ""'
22
+ end
23
+
24
+ desc 'Run example'
25
+ task :example do
26
+ sh 'ruby -w -I lib -I ../rb-obfusk-util/lib example.rb'
27
+ end
28
+
29
+ desc 'Generate docs'
30
+ task :docs do
31
+ sh 'yardoc | cat'
32
+ end
33
+
34
+ desc 'List undocumented objects'
35
+ task 'docs:undoc' do
36
+ sh 'yard stats --list-undoc'
37
+ end
38
+
39
+ desc 'Cleanup'
40
+ task :clean do
41
+ sh 'rm -rf .yardoc/ doc/ *.gem'
42
+ end
43
+
44
+ desc 'Build SNAPSHOT gem'
45
+ task :snapshot do
46
+ v = Time.new.strftime '%Y%m%d%H%M%S'
47
+ f = 'lib/eft/version.rb'
48
+ sh "sed -ri~ 's!(SNAPSHOT)!\\1.#{v}!' #{f}"
49
+ sh 'gem build eft.gemspec'
50
+ end
51
+
52
+ desc 'Undo SNAPSHOT gem'
53
+ task 'snapshot:undo' do
54
+ sh 'git checkout -- lib/eft/version.rb'
55
+ end
data/eft.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ require File.expand_path('../lib/eft/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'eft'
5
+ s.homepage = 'https://github.com/obfusk/eft'
6
+ s.summary = 'ruby + whiptail'
7
+
8
+ s.description = <<-END.gsub(/^ {4}/, '')
9
+ ruby + whiptail
10
+
11
+ Eft is a ruby dsl that wraps whiptail to display dialog boxes.
12
+ END
13
+
14
+ s.version = Eft::VERSION
15
+ s.date = Eft::DATE
16
+
17
+ s.authors = [ 'Felix C. Stegerman' ]
18
+ s.email = %w{ flx@obfusk.net }
19
+
20
+ s.licenses = %w{ LGPLv3+ }
21
+
22
+ s.files = %w{ .yardopts README.md Rakefile } \
23
+ + %w{ eft.gemspec example.rb } \
24
+ + Dir['lib/**/*.rb']
25
+
26
+ s.add_runtime_dependency 'obfusk-util'
27
+
28
+ s.add_development_dependency 'rake'
29
+ # s.add_development_dependency 'rspec'
30
+
31
+ s.required_ruby_version = '>= 1.9.1'
32
+ end
data/example.rb ADDED
@@ -0,0 +1,64 @@
1
+ require 'eft'
2
+
3
+ Eft.menu('Choices:', selected: 'bar', title: 'Menu',
4
+ backtitle: 'Choose!') do |m|
5
+ m.on('foo', 'Do The Foo') do |x|
6
+ Eft.ask('What is your name?', ok_button: 'Next',
7
+ default: 'Anonymous') do |q1|
8
+ q1.on_ok do |name|
9
+ Eft.ask_pass('Secret?', ok_button: 'Go!') do |q2|
10
+ q2.on_ok do |sec|
11
+ Eft.show_msg "Welcome #{name}, your secret is #{sec}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ m.on('bar', 'Do Bar!') do |x|
18
+ Eft.ask_yesno 'Is everything going well?' do |q|
19
+ q.on_yes { puts 'Nice!' }
20
+ q.on_no { puts 'Too bad!' }
21
+ end
22
+ end
23
+ m.on('baz', 'Baz!?') do |x|
24
+ Eft.gauge '...', 0, title: 'Progress' do |mv|
25
+ 0.upto(100) do |i|
26
+ i % 5 == 0 ? mv[i, "We're at ##{i} ..."] : mv[i]
27
+ sleep 0.05
28
+ end
29
+ end
30
+ end
31
+ m.on('qux', 'Quixotic?') do |x|
32
+ Eft.show_info 'Hi!'
33
+ end
34
+ m.on('quux', 'Quixotic!') do |x|
35
+ Eft.show_text file: '/etc/services', scroll: true
36
+ end
37
+ m.on('quuux', 'There\'s more ...') do |x|
38
+ Eft.check 'Which ones?' do |c|
39
+ c.choice('1', 'One')
40
+ c.choice('2', 'Two', true)
41
+ c.choice('3', 'Three')
42
+ c.on_ok do |choices|
43
+ Eft.show_text text: choices*10*"\n", title: 'Multiplied!'
44
+ end
45
+ end
46
+ end
47
+ m.on('almost there', 'Just one more') do |x|
48
+ Eft.radio 'One or none' do |c|
49
+ c.choice('1', 'One')
50
+ c.choice('2', 'Two')
51
+ c.on_ok { |choice| puts "You chose: #{choice || 'none!?'}" }
52
+ end
53
+ end
54
+ m.on('last one', 'Really! I promise.') do |x|
55
+ Eft.radio 'Which one?', selected: '2' do |c|
56
+ c.choice('1', 'One')
57
+ c.choice('2', 'Two')
58
+ c.choice('3', 'Three')
59
+ c.on_ok { |choice| puts "You chose: #{choice}" }
60
+ end
61
+ end
62
+ m.on_cancel { puts 'Not interested.' }
63
+ m.on_esc { puts 'Escaped!' }
64
+ end
@@ -0,0 +1,4 @@
1
+ module Eft
2
+ VERSION = '0.4.0'
3
+ DATE = '2014-02-20'
4
+ end
data/lib/eft.rb ADDED
@@ -0,0 +1,322 @@
1
+ # -- ; {{{1
2
+ #
3
+ # File : eft.rb
4
+ # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
+ # Date : 2014-02-20
6
+ #
7
+ # Copyright : Copyright (C) 2014 Felix C. Stegerman
8
+ # Licence : LGPLv3+
9
+ #
10
+ # -- ; }}}1
11
+
12
+ require 'tempfile'
13
+
14
+ require 'obfusk/util/run'
15
+ require 'obfusk/util/term'
16
+
17
+ module Eft
18
+
19
+ OU = Obfusk::Util
20
+
21
+ # --
22
+
23
+ # NB: dialog works as well as whiptail, but these options will be
24
+ # incompatible: --*-button (is: --*-label), --scrolltext
25
+
26
+ WHIPTAIL = 'whiptail'
27
+ EXIT = { ok_yes: 0, cancel_no: 1, esc: 255 }
28
+ CHECK_OPTS = ['--separate-output']
29
+
30
+ OPT = ->(o,x) { o.select { |k,v| OPTS[x][k] }
31
+ .map { |k,v| OPTS[x][k][v] } }
32
+ ON_OFF = ->(x) { x ? 'on' : 'off' }
33
+
34
+ # --
35
+
36
+ # translate method to option
37
+ WHAT = { # {{{1
38
+ show_info: '--infobox' , show_msg: '--msgbox' ,
39
+ show_text: '--textbox' , ask: '--inputbox' ,
40
+ ask_pass: '--passwordbox' , ask_yesno: '--yesno' ,
41
+ menu: '--menu' , check: '--checklist' ,
42
+ radio: '--radiolist' , gauge: '--gauge' ,
43
+ } # }}}1
44
+
45
+ # options per category
46
+ OPTS = { # {{{1
47
+ all: {
48
+ title: ->(t) { ['--title', t] },
49
+ backtitle: ->(t) { ['--backtitle', t] },
50
+ scroll: ->(b) { b ? ['--scrolltext'] : [] }, # ????
51
+ },
52
+ ok: {
53
+ ok_button: ->(t) { ['--ok-button', t] },
54
+ },
55
+ cancel: {
56
+ cancel_button: ->(t) { ['--cancel-button', t] },
57
+ no_cancel: ->(b) { b ? ['--nocancel'] : [] },
58
+ },
59
+ yes: {
60
+ yes_button: ->(t) { ['--yes-button', t] },
61
+ },
62
+ no: {
63
+ no_button: ->(t) { ['--no-button', t] },
64
+ default_no: ->(b) { b ? ['--default-no'] : [] },
65
+ },
66
+ menu: {
67
+ selected: ->(i) { ['--default-item', i] },
68
+ },
69
+ } # }}}1
70
+
71
+ # --
72
+
73
+ class Error < RuntimeError; end
74
+
75
+ # configuration class
76
+ class Cfg # {{{1
77
+ # set from hash, pass to block, freeze
78
+ def initialize(cfg = {}, &b)
79
+ @cfg = cfg; b[self] if b; @cfg.freeze
80
+ end
81
+
82
+ # call if set
83
+ def call(k, *a)
84
+ @cfg[k][*a] if @cfg[k]
85
+ end
86
+
87
+ # nothing by default
88
+ def _opts; [] end
89
+ end # }}}1
90
+
91
+ #
92
+
93
+ module CfgEsc ; def on_esc(&b) @cfg[:on_esc] = b end; end
94
+ module CfgOK ; def on_ok(&b) @cfg[:on_ok] = b end; end
95
+ module CfgCancel; def on_cancel(&b) @cfg[:on_cancel] = b end; end
96
+ module CfgYes ; def on_yes(&b) @cfg[:on_yes] = b end; end
97
+ module CfgNo ; def on_no(&b) @cfg[:on_no] = b end; end
98
+
99
+ #
100
+
101
+ class CfgShowInfo < Cfg ; end
102
+ class CfgShowMsg < Cfg; include CfgEsc, CfgOK ; end
103
+ class CfgShowText < Cfg; include CfgEsc, CfgOK ; end
104
+ class CfgAsk < Cfg; include CfgEsc, CfgOK, CfgCancel ; end
105
+ class CfgAskPass < Cfg; include CfgEsc, CfgOK, CfgCancel ; end
106
+ class CfgAskYesNo < Cfg; include CfgEsc, CfgYes, CfgNo ; end
107
+ class CfgGauge < Cfg ; end
108
+
109
+ #
110
+
111
+ # menu config
112
+ class CfgMenu < Cfg # {{{1
113
+ include CfgEsc, CfgOK, CfgCancel
114
+
115
+ def initialize(*a, &b)
116
+ @menu = []; super; @menu.freeze
117
+ end
118
+
119
+ # add menu item
120
+ def on(tag, item, &b)
121
+ @menu << { tag: tag, item: item, block: b }
122
+ end
123
+
124
+ def _menu; @menu end
125
+ end # }}}1
126
+
127
+ # checkboxes config
128
+ class CfgCheck < Cfg # {{{1
129
+ include CfgEsc, CfgOK, CfgCancel
130
+
131
+ def initialize(*a, &b)
132
+ @check = []; super; @check.freeze
133
+ end
134
+
135
+ # add a choice
136
+ def choice(tag, item, selected = false)
137
+ @check << { tag: tag, item: item, selected: selected }
138
+ end
139
+
140
+ def _check; @check end
141
+ def _opts; CHECK_OPTS; end
142
+ end # }}}1
143
+
144
+ # radiolist config
145
+ class CfgRadio < Cfg # {{{1
146
+ include CfgEsc, CfgOK, CfgCancel
147
+
148
+ def initialize(*a, &b)
149
+ @radio = []; super; @radio.freeze
150
+ end
151
+
152
+ # add a choice
153
+ def choice(tag, item)
154
+ @radio << { tag: tag, item: item }
155
+ end
156
+
157
+ def _radio; @radio end
158
+ end # }}}1
159
+
160
+ # --
161
+
162
+ # show message w/o buttons, don't clear screen
163
+ def self.show_info(text, opts = {})
164
+ _whip :show_info, text, CfgShowInfo.new, opts
165
+ end
166
+
167
+ # show message w/ OK button
168
+ def self.show_msg(text, opts = {}, &b)
169
+ c = CfgShowMsg.new(&b)
170
+ _whip(:show_msg, text, c, opts) { c.call :on_ok }
171
+ end
172
+
173
+ # show file contents or text w/ OK button
174
+ def self.show_text(opts = {}, &b)
175
+ _file_or_temp opts do |f|
176
+ c = CfgShowText.new(&b)
177
+ _whip(:show_text, f, c, opts) { c.call :on_ok }
178
+ end
179
+ end
180
+
181
+ # --
182
+
183
+ # ask for input w/ OK/Cancel buttons (and default)
184
+ def self.ask(text, opts = {}, &b)
185
+ c = CfgAsk.new(&b); a = opts[:default] ? [opts[:default]] : []
186
+ _whip(:ask, text, c, opts, a) do |lines|
187
+ c.call :on_ok, lines.first
188
+ end
189
+ end
190
+
191
+ # ask for password w/ OK/Cancel buttons
192
+ def self.ask_pass(text, opts = {}, &b)
193
+ c = CfgAskPass.new(&b)
194
+ _whip(:ask_pass, text, c, opts) do |lines|
195
+ c.call :on_ok, lines.first
196
+ end
197
+ end
198
+
199
+ # ask w/ Yes/No buttons
200
+ def self.ask_yesno(text, opts = {}, &b)
201
+ c = CfgAskYesNo.new(&b)
202
+ _whip(:ask_yesno, text, c, opts) { c.call :on_yes }
203
+ end
204
+
205
+ # --
206
+
207
+ # choose from menu w/ OK/Cancel buttons
208
+ def self.menu(text, opts = {}, &b) # {{{1
209
+ c = CfgMenu.new(&b); m = c._menu
210
+ o = opts.merge(subheight: opts[:menu_height]).freeze
211
+ a = m.map { |x| [x[:tag],x[:item]] } .flatten
212
+ t = Hash[m.map { |x| [x[:tag],x] }]
213
+ _whip(:menu, text, c, o, a) do |lines|
214
+ tag = lines.first; t[tag][:block][tag]
215
+ end
216
+ end # }}}1
217
+
218
+ # --
219
+
220
+ # choose checkboxes w/ OK/Cancel buttons
221
+ def self.check(text, opts = {}, &b) # {{{1
222
+ c = CfgCheck.new(&b); i = c._check
223
+ o = opts.merge(subheight: opts[:list_height]).freeze
224
+ a = i.map { |x| [x[:tag],x[:item],ON_OFF[x[:selected]]] } .flatten
225
+ _whip(:check, text, c, o, a) do |lines|
226
+ c.call :on_ok, lines
227
+ end
228
+ end # }}}1
229
+
230
+ # choose radiobutton w/ OK/Cancel buttons
231
+ def self.radio(text, opts = {}, &b) # {{{1
232
+ s = opts[:selected] ; f = ->(x) { ON_OFF[x == s] }
233
+ c = CfgRadio.new(&b); i = c._radio
234
+ o = opts.merge(subheight: opts[:list_height]).freeze
235
+ a = i.map { |x| [x[:tag],x[:item],f[x[:tag]]] } .flatten
236
+ _whip(:radio, text, c, o, a) do |lines|
237
+ c.call :on_ok, lines.first
238
+ end # }}}1
239
+ end
240
+
241
+ # --
242
+
243
+ # show gauge; use lambda passed to block to move it forward by
244
+ # passing it `percent[, message]`
245
+ def self.gauge(text, percent, opts = {}, &b) # {{{1
246
+ IO.pipe do |r, w|
247
+ mv = ->(pct, msg = nil) {
248
+ msg ? w.puts('XXX', pct, msg, 'XXX', pct) : w.puts(pct) # WTF!
249
+ }
250
+ c = CfgGauge.new
251
+ o = _whip_opts :gauge, c, opts
252
+ c = _whip_cmd text, opts, o, [percent]
253
+ pid = OU.spawn(*c, in: r)
254
+ r.close; b[mv]; w.close; Process.wait pid
255
+ raise Error, 'exitstatus != 0' if $?.exitstatus != 0
256
+ end # }}}1
257
+ end
258
+
259
+ # --
260
+
261
+ # process options, run whiptail, call `b[lines]` or
262
+ # `on_{cancel,no,esc}[]`
263
+ # @raise Error if unknown exitstatus
264
+ def self._whip(what, text, cfg, opts, args = [], &b) # {{{1
265
+ o = _whip_opts what, cfg, opts; c = _whip_cmd text, opts, o, args
266
+ r = _run_whip c
267
+ case r[:exit]
268
+ when EXIT[:ok_yes] ; b[r[:lines]] if b
269
+ when EXIT[:cancel_no] ; cfg.call :on_cancel; cfg.call :on_no
270
+ when EXIT[:esc] ; cfg.call :on_esc
271
+ else raise Error, 'unknown exitstatus'
272
+ end
273
+ nil
274
+ end # }}}1
275
+
276
+ # process whiptail options
277
+ def self._whip_opts(what, cfg, opts) # {{{1
278
+ cfg._opts + [ OPT[opts,:all],
279
+ cfg.respond_to?(:on_ok) ? OPT[opts,:ok] : [],
280
+ cfg.respond_to?(:on_cancel) ? OPT[opts,:cancel] : [],
281
+ cfg.respond_to?(:on_yes) ? OPT[opts,:yes] : [],
282
+ cfg.respond_to?(:on_no) ? OPT[opts,:no] : [],
283
+ what == :menu ? OPT[opts,:menu] : [],
284
+ ] .flatten + [WHAT[what]]
285
+ end # }}}1
286
+
287
+ # whiptail command
288
+ def self._whip_cmd(text, opts, opt_args, args) # {{{1
289
+ h = opts[:height] || OU::Term.lines - 4
290
+ w = opts[:width] || OU::Term.columns - 4
291
+ s = opts.has_key?(:subheight) ? [opts[:subheight] || h - 8] : []
292
+ a = [WHIPTAIL] + opt_args + ['--', text, h, w] + s + args
293
+ a.map(&:to_s)
294
+ end # }}}1
295
+
296
+ # run whiptail
297
+ # @return [Hash] `{ exit: exitstatus, lines: chomped_lines }`
298
+ def self._run_whip(args)
299
+ IO.pipe do |r, w|
300
+ s = OU.spawn_w(*args, err: w); w.close
301
+ { exit: s.exitstatus, lines: r.readlines.map(&:chomp) }
302
+ end
303
+ end
304
+
305
+ # --
306
+
307
+ # call block w/ either `opts[:file]` or a tempfile w/ contents
308
+ # `opts[:text]`
309
+ def self._file_or_temp(opts, &b) # {{{1
310
+ file = opts[:file]; text = opts[:text]
311
+ raise Error, 'can\'t have file and text' if file && text
312
+ raise Error, 'must have file or text' if !file && !text
313
+ if file
314
+ b[file]
315
+ else
316
+ Tempfile.open('eft') { |f| f.write text; f.close; b[f.path] }
317
+ end
318
+ end # }}}1
319
+
320
+ end
321
+
322
+ # vim: set tw=70 sw=2 sts=2 et fdm=marker :
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eft
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Felix C. Stegerman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: obfusk-util
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |
42
+ ruby + whiptail
43
+
44
+ Eft is a ruby dsl that wraps whiptail to display dialog boxes.
45
+ email:
46
+ - flx@obfusk.net
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - .yardopts
52
+ - README.md
53
+ - Rakefile
54
+ - eft.gemspec
55
+ - example.rb
56
+ - lib/eft/version.rb
57
+ - lib/eft.rb
58
+ homepage: https://github.com/obfusk/eft
59
+ licenses:
60
+ - LGPLv3+
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - '>='
69
+ - !ruby/object:Gem::Version
70
+ version: 1.9.1
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.0.14
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: ruby + whiptail
82
+ test_files: []