interact 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +30 -0
- data/README.md +102 -0
- data/Rakefile +19 -0
- data/lib/interact.rb +436 -0
- data/lib/version.rb +5 -0
- metadata +99 -0
data/LICENSE
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Copyright (c)2011, Alex Suraci
|
2
|
+
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
* Redistributions in binary form must reproduce the above
|
12
|
+
copyright notice, this list of conditions and the following
|
13
|
+
disclaimer in the documentation and/or other materials provided
|
14
|
+
with the distribution.
|
15
|
+
|
16
|
+
* Neither the name of Alex Suraci nor the names of other
|
17
|
+
contributors may be used to endorse or promote products derived
|
18
|
+
from this software without specific prior written permission.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
21
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
22
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
23
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
24
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
25
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
26
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
27
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
28
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
29
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Interact
|
2
|
+
|
3
|
+
Another interaction library for Ruby, with an interesting twist: the user can
|
4
|
+
go back in time to re-answer questions.
|
5
|
+
|
6
|
+
*Copyright 2011, Alex Suraci. Licensed under the MIT license, please see the
|
7
|
+
LICENSE file. All rights reserved.*
|
8
|
+
|
9
|
+
|
10
|
+
## Basic Usage
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require "rubygems"
|
14
|
+
require "interact"
|
15
|
+
|
16
|
+
class MyInteractiveClass
|
17
|
+
include Interactive
|
18
|
+
|
19
|
+
def run
|
20
|
+
first = ask "Some question?"
|
21
|
+
second = ask "Boolean default?", :default => true
|
22
|
+
third = ask "Stringy default?", :default => "foo"
|
23
|
+
|
24
|
+
fourth = ask "Multiple choice?", :choices => ["foo", "bar", "fizz"]
|
25
|
+
|
26
|
+
some_mutation = []
|
27
|
+
|
28
|
+
fifth = ask "Multiple choice, indexed list?",
|
29
|
+
:choices => ["foo", "bar", "fizz"],
|
30
|
+
:indexed => true
|
31
|
+
|
32
|
+
some_mutation << fifth
|
33
|
+
|
34
|
+
finalize
|
35
|
+
|
36
|
+
sixth = ask "Password", :echo => "*", :forget => true
|
37
|
+
|
38
|
+
p [first, second, third, fourth, fifth, sixth]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
MyInteractiveClass.new.run
|
43
|
+
```
|
44
|
+
|
45
|
+
After running this, the user will be prompted with each question one-by-one.
|
46
|
+
Interact supports basic editing features such as going to the start/end,
|
47
|
+
editing in the middle of the text, backspace, forward delete, and
|
48
|
+
backwards-kill-word.
|
49
|
+
|
50
|
+
In addition, the user can hit the up arrow to go "back in time" and re-answer
|
51
|
+
questins.
|
52
|
+
|
53
|
+
Make sure you call `finalize` after doing any mutation performed based on user
|
54
|
+
input; this will prevent them from rewinding to before this took place. Or you
|
55
|
+
can just disable rewinding if it's too complicated (see below).
|
56
|
+
|
57
|
+
Anyway, here's an example session:
|
58
|
+
|
59
|
+
```
|
60
|
+
Some question?: hello<enter>
|
61
|
+
Boolean default? [Yn]: <up>
|
62
|
+
Some question? ["hello"]: trying again<enter>
|
63
|
+
Boolean default? [Yn]: n<enter>
|
64
|
+
Stringy default? ["foo"]: <up>
|
65
|
+
Boolean default? [yN]: y<enter>
|
66
|
+
Stringy default? ["foo"]: <enter>
|
67
|
+
Multiple choice? ("foo", "bar", "fizz"): f<enter>
|
68
|
+
Please disambiguate: foo or fizz?
|
69
|
+
Multiple choice? ("foo", "bar", "fizz"): fo<enter>
|
70
|
+
1: foo
|
71
|
+
2: bar
|
72
|
+
3: fizz
|
73
|
+
Multiple choice, indexed list?: 2<enter>
|
74
|
+
Password: ******<enter>
|
75
|
+
["trying again", true, "foo", "foo", "bar", "secret"]
|
76
|
+
```
|
77
|
+
|
78
|
+
Note that the user's previous answers become the new defaults for the question
|
79
|
+
if they rewind.
|
80
|
+
|
81
|
+
## Disabling Rewinding
|
82
|
+
|
83
|
+
Interact provides a nifty user-friendly "rewinding" feature, which allows the user to go back in time and re-answer a question. If you don't want this feature, simply set `@@allow_rewind` to `false` in your class.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class NoRewind
|
87
|
+
include Interactive
|
88
|
+
@@allow_rewind = false
|
89
|
+
|
90
|
+
def run
|
91
|
+
res = ask "Is there no return?", :default => true
|
92
|
+
|
93
|
+
if res == allow_rewind
|
94
|
+
puts "You're right!"
|
95
|
+
else
|
96
|
+
puts "Nope! It's disabled."
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
NoRewind.new.run
|
102
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
task :default => "spec"
|
4
|
+
|
5
|
+
desc "Run specs"
|
6
|
+
task "spec" => ["bundler:install", "test:spec"]
|
7
|
+
|
8
|
+
namespace "bundler" do
|
9
|
+
desc "Install gems"
|
10
|
+
task "install" do
|
11
|
+
sh("bundle install")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace "test" do
|
16
|
+
task "spec" do |t|
|
17
|
+
sh("cd spec && rake spec")
|
18
|
+
end
|
19
|
+
end
|
data/lib/interact.rb
ADDED
@@ -0,0 +1,436 @@
|
|
1
|
+
# Copyright (c) 2011 Alex Suraci
|
2
|
+
|
3
|
+
# Helpers for the main API provided by mixing in +Interactive+.
|
4
|
+
#
|
5
|
+
# Internal use only. Not a stable API.
|
6
|
+
module Interact
|
7
|
+
WINDOWS = !!(RUBY_PLATFORM =~ /mingw|mswin32|cygwin/)
|
8
|
+
|
9
|
+
if defined? callcc
|
10
|
+
HAS_CALLCC = true
|
11
|
+
else
|
12
|
+
begin
|
13
|
+
require "continuation"
|
14
|
+
HAS_CALLCC = true
|
15
|
+
rescue LoadError
|
16
|
+
HAS_CALLCC = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ESCAPES = {
|
21
|
+
"[A" => :up, "H" => :up,
|
22
|
+
"[B" => :down, "P" => :down,
|
23
|
+
"[C" => :right, "M" => :right,
|
24
|
+
"[D" => :left, "K" => :left,
|
25
|
+
"[3~" => :delete, "S" => :delete,
|
26
|
+
"[H" => :home, "G" => :home,
|
27
|
+
"[F" => :end, "O" => :end
|
28
|
+
}
|
29
|
+
|
30
|
+
EVENTS = {
|
31
|
+
"\b" => :backspace,
|
32
|
+
"\t" => :tab,
|
33
|
+
"\x01" => :home,
|
34
|
+
"\x03" => :interrupt,
|
35
|
+
"\x04" => :eof,
|
36
|
+
"\x05" => :end,
|
37
|
+
"\x17" => :kill_word,
|
38
|
+
"\x7f" => :backspace
|
39
|
+
}
|
40
|
+
|
41
|
+
# Used internally to clean up input state before jumping to another prompt.
|
42
|
+
class JumpToPrompt < Exception
|
43
|
+
def initialize(prompt)
|
44
|
+
@prompt = prompt
|
45
|
+
end
|
46
|
+
|
47
|
+
# Print an empty line and jump to the prompt. This is typically called
|
48
|
+
# after the user has pressed the up arrow.
|
49
|
+
def jump
|
50
|
+
print "\n"
|
51
|
+
@prompt[0].call(@prompt)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.handler(which, ans, pos, echo = nil, prompts = [])
|
56
|
+
if block_given?
|
57
|
+
res = yield which, ans, pos, echo
|
58
|
+
return res unless res.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
case which
|
62
|
+
when :up
|
63
|
+
if back = prompts.pop
|
64
|
+
raise Interact::JumpToPrompt, back
|
65
|
+
end
|
66
|
+
|
67
|
+
when :down
|
68
|
+
# nothing
|
69
|
+
|
70
|
+
when :tab
|
71
|
+
# nothing
|
72
|
+
|
73
|
+
when :right
|
74
|
+
unless pos == ans.size
|
75
|
+
print censor(ans[pos .. pos], echo)
|
76
|
+
return pos + 1
|
77
|
+
end
|
78
|
+
|
79
|
+
when :left
|
80
|
+
unless pos == 0
|
81
|
+
print "\b"
|
82
|
+
return pos - 1
|
83
|
+
end
|
84
|
+
|
85
|
+
when :delete
|
86
|
+
unless pos == ans.size
|
87
|
+
ans.slice!(pos, 1)
|
88
|
+
if Interact::WINDOWS
|
89
|
+
rest = ans[pos .. -1]
|
90
|
+
print(censor(rest, echo) + " \b" + ("\b" * rest.size))
|
91
|
+
else
|
92
|
+
print("\e[P")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
when :home
|
97
|
+
print("\b" * pos)
|
98
|
+
return 0
|
99
|
+
|
100
|
+
when :end
|
101
|
+
print(censor(ans[pos .. -1], echo))
|
102
|
+
return ans.size
|
103
|
+
|
104
|
+
when :backspace
|
105
|
+
if pos > 0
|
106
|
+
ans.slice!(pos - 1, 1)
|
107
|
+
|
108
|
+
if Interact::WINDOWS
|
109
|
+
rest = ans[pos - 1 .. -1]
|
110
|
+
print("\b" + censor(rest, echo) + " \b" + ("\b" * rest.size))
|
111
|
+
else
|
112
|
+
print("\b\e[P")
|
113
|
+
end
|
114
|
+
|
115
|
+
return pos - 1
|
116
|
+
end
|
117
|
+
|
118
|
+
when :interrupt
|
119
|
+
raise Interrupt.new
|
120
|
+
|
121
|
+
when :eof
|
122
|
+
return false if ans.empty?
|
123
|
+
|
124
|
+
when :kill_word
|
125
|
+
if pos > 0
|
126
|
+
start = /[^\s]*\s*$/ =~ ans[0 .. pos]
|
127
|
+
length = pos - start
|
128
|
+
ans.slice!(start, length)
|
129
|
+
print("\b" * length + " " * length + "\b" * length)
|
130
|
+
return start
|
131
|
+
end
|
132
|
+
|
133
|
+
when Array
|
134
|
+
case which[0]
|
135
|
+
when :key
|
136
|
+
c = which[1]
|
137
|
+
rest = ans[pos .. -1]
|
138
|
+
|
139
|
+
ans.insert(pos, c)
|
140
|
+
|
141
|
+
print(censor(c + rest, echo) + ("\b" * rest.size))
|
142
|
+
|
143
|
+
return pos + 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
pos
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.censor(str, with)
|
151
|
+
return str unless with
|
152
|
+
with * str.size
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.ask_default(input, question, default = nil,
|
156
|
+
echo = nil, prompts = [], &callback)
|
157
|
+
while true
|
158
|
+
prompt(question, default)
|
159
|
+
|
160
|
+
ans = ""
|
161
|
+
pos = 0
|
162
|
+
escaped = false
|
163
|
+
escape_seq = ""
|
164
|
+
|
165
|
+
with_char_io(input) do
|
166
|
+
until pos == false or (c = get_character(input)) =~ /[\r\n]/
|
167
|
+
if c == "\e" || c == "\xE0"
|
168
|
+
escaped = true
|
169
|
+
elsif escaped
|
170
|
+
escape_seq << c
|
171
|
+
|
172
|
+
if cmd = Interact::ESCAPES[escape_seq]
|
173
|
+
pos = handler(cmd, ans, pos, echo, prompts, &callback)
|
174
|
+
escaped, escape_seq = false, ""
|
175
|
+
elsif Interact::ESCAPES.select { |k, v|
|
176
|
+
k.start_with? escape_seq
|
177
|
+
}.empty?
|
178
|
+
escaped, escape_seq = false, ""
|
179
|
+
end
|
180
|
+
elsif Interact::EVENTS.key? c
|
181
|
+
pos = handler(
|
182
|
+
Interact::EVENTS[c], ans, pos, echo, prompts, &callback
|
183
|
+
)
|
184
|
+
elsif c < " "
|
185
|
+
# ignore
|
186
|
+
else
|
187
|
+
pos = handler([:key, c], ans, pos, echo, prompts, &callback)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
print "\n"
|
193
|
+
|
194
|
+
if ans.empty?
|
195
|
+
return default unless default.nil?
|
196
|
+
else
|
197
|
+
return match_type(ans, default)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.ask_choices(input, question, default, choices, indexed = false,
|
203
|
+
echo = nil, prompts = [], &callback)
|
204
|
+
choices = choices.to_a
|
205
|
+
|
206
|
+
msg = question.dup
|
207
|
+
|
208
|
+
if indexed
|
209
|
+
choices.each.with_index do |o, i|
|
210
|
+
puts "#{i + 1}: #{o}"
|
211
|
+
end
|
212
|
+
else
|
213
|
+
msg << " (#{choices.collect(&:inspect).join ", "})"
|
214
|
+
end
|
215
|
+
|
216
|
+
while true
|
217
|
+
ans = ask_default(input, msg, default, echo, prompts, &callback)
|
218
|
+
|
219
|
+
matches = choices.select { |x| x.start_with? ans }
|
220
|
+
|
221
|
+
if matches.size == 1
|
222
|
+
return matches.first
|
223
|
+
elsif indexed and ans =~ /^\s*\d+\s*$/ and res = choices[ans.to_i - 1]
|
224
|
+
return res
|
225
|
+
elsif matches.size > 1
|
226
|
+
puts "Please disambiguate: #{matches.join " or "}?"
|
227
|
+
else
|
228
|
+
puts "Unknown answer, please try again!"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.prompt(question, default = nil)
|
234
|
+
msg = question.dup
|
235
|
+
|
236
|
+
case default
|
237
|
+
when true
|
238
|
+
msg << " [Yn]"
|
239
|
+
when false
|
240
|
+
msg << " [yN]"
|
241
|
+
else
|
242
|
+
msg << " [#{default.inspect}]" if default
|
243
|
+
end
|
244
|
+
|
245
|
+
print "#{msg}: "
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.match_type(str, x)
|
249
|
+
case x
|
250
|
+
when Integer
|
251
|
+
str.to_i
|
252
|
+
when true, false
|
253
|
+
str.upcase.start_with? "Y"
|
254
|
+
else
|
255
|
+
str
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Definitions for reading character-by-character with no echoing.
|
260
|
+
begin
|
261
|
+
require "Win32API"
|
262
|
+
|
263
|
+
def self.with_char_io(input)
|
264
|
+
yield
|
265
|
+
rescue Interact::JumpToPrompt => e
|
266
|
+
e.jump
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.get_character(input)
|
270
|
+
if input == STDIN
|
271
|
+
begin
|
272
|
+
Win32API.new("msvcrt", "_getch", [], "L").call.chr
|
273
|
+
rescue
|
274
|
+
Win32API.new("crtdll", "_getch", [], "L").call.chr
|
275
|
+
end
|
276
|
+
else
|
277
|
+
input.getc.chr
|
278
|
+
end
|
279
|
+
end
|
280
|
+
rescue LoadError
|
281
|
+
begin
|
282
|
+
require "termios"
|
283
|
+
|
284
|
+
def self.with_char_io(input)
|
285
|
+
return yield unless input.tty?
|
286
|
+
|
287
|
+
before = Termios.getattr(input)
|
288
|
+
|
289
|
+
new = before.dup
|
290
|
+
new.c_lflag &= ~(Termios::ECHO | Termios::ICANON)
|
291
|
+
new.c_cc[Termios::VMIN] = 1
|
292
|
+
|
293
|
+
begin
|
294
|
+
Termios.setattr(input, Termios::TCSANOW, new)
|
295
|
+
yield
|
296
|
+
rescue Interact::JumpToPrompt => e
|
297
|
+
Termios.setattr(input, Termios::TCSANOW, before)
|
298
|
+
e.jump
|
299
|
+
ensure
|
300
|
+
Termios.setattr(input, Termios::TCSANOW, before)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.get_character(input)
|
305
|
+
input.getc.chr
|
306
|
+
end
|
307
|
+
rescue LoadError
|
308
|
+
def self.with_char_io(input)
|
309
|
+
return yield unless input.tty?
|
310
|
+
|
311
|
+
begin
|
312
|
+
before = `stty -g`
|
313
|
+
system("stty raw -echo -icanon isig")
|
314
|
+
yield
|
315
|
+
rescue Interact::JumpToPrompt => e
|
316
|
+
system("stty #{before}")
|
317
|
+
e.jump
|
318
|
+
ensure
|
319
|
+
system("stty #{before}")
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def self.get_character(input)
|
324
|
+
input.getc.chr
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
module Interactive
|
331
|
+
# Allow classes to disable the rewind feature via the +allow_rewind+ class
|
332
|
+
# variable.
|
333
|
+
def self.included klass
|
334
|
+
klass.send :class_variable_set, :@@allow_rewind, true
|
335
|
+
|
336
|
+
klass.class_eval do
|
337
|
+
# Accessor for the +allow_rewind+ class variable, which determines
|
338
|
+
# whether to enable the rewinding feature.
|
339
|
+
def allow_rewind
|
340
|
+
self.class.send :class_variable_get, :@@allow_rewind
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# General-purpose interaction.
|
346
|
+
#
|
347
|
+
# [question] The prompt, without ": " at the end.
|
348
|
+
#
|
349
|
+
# [options] An optional hash containing the following options.
|
350
|
+
#
|
351
|
+
# input::
|
352
|
+
# The input source (defaults to +STDIN+).
|
353
|
+
#
|
354
|
+
# default::
|
355
|
+
# The default value, also used to attempt type conversion of the answer
|
356
|
+
# (e.g. numeric/boolean).
|
357
|
+
#
|
358
|
+
# choices::
|
359
|
+
# An array (or +Enumerable+) of strings to choose from.
|
360
|
+
#
|
361
|
+
# indexed::
|
362
|
+
# Whether to allow choosing from +:choices+ by their index, best for when
|
363
|
+
# there are many choices.
|
364
|
+
#
|
365
|
+
# echo::
|
366
|
+
# A string to echo when showing the input; used for things like censoring
|
367
|
+
# password input.
|
368
|
+
#
|
369
|
+
# forget::
|
370
|
+
# Set to false to prevent rewinding from remembering the answer.
|
371
|
+
#
|
372
|
+
# callback::
|
373
|
+
# A block used to override certain actions.
|
374
|
+
#
|
375
|
+
# The block should take 4 arguments:
|
376
|
+
#
|
377
|
+
# - the event, e.g. +:up+ or +[:key, X]+ where +X+ is a string containing
|
378
|
+
# a single character
|
379
|
+
# - the current answer to the question; you'll probably mutate this
|
380
|
+
# - the current offset from the start of the answer string, e.g. when
|
381
|
+
# typing in the middle of the input, this will be where you insert
|
382
|
+
# characters
|
383
|
+
# - the +:echo+ option from above, may be +nil+
|
384
|
+
#
|
385
|
+
# The block should return the updated +position+, or +nil+ if it didn't
|
386
|
+
# handle the event
|
387
|
+
def ask(question, options = {})
|
388
|
+
rewind = Interact::HAS_CALLCC && allow_rewind
|
389
|
+
|
390
|
+
if rewind
|
391
|
+
prompt, answer = callcc { |cc| [cc, nil] }
|
392
|
+
else
|
393
|
+
prompt, answer = nil, nil
|
394
|
+
end
|
395
|
+
|
396
|
+
if answer.nil?
|
397
|
+
default = options[:default]
|
398
|
+
else
|
399
|
+
default = answer
|
400
|
+
end
|
401
|
+
|
402
|
+
choices = options[:choices]
|
403
|
+
indexed = options[:indexed]
|
404
|
+
callback = options[:callback]
|
405
|
+
input = options[:input] || STDIN
|
406
|
+
echo = options[:echo]
|
407
|
+
|
408
|
+
prompts = (@__prompts ||= [])
|
409
|
+
|
410
|
+
if choices
|
411
|
+
ans = Interact.ask_choices(
|
412
|
+
input, question, default, choices, indexed, echo, prompts, &callback
|
413
|
+
)
|
414
|
+
else
|
415
|
+
ans = Interact.ask_default(
|
416
|
+
input, question, default, echo, prompts, &callback
|
417
|
+
)
|
418
|
+
end
|
419
|
+
|
420
|
+
if rewind
|
421
|
+
prompts << [prompt, options[:forget] ? nil : ans]
|
422
|
+
end
|
423
|
+
|
424
|
+
ans
|
425
|
+
end
|
426
|
+
|
427
|
+
# Clear prompts.
|
428
|
+
#
|
429
|
+
# Questions asked after this are rewindable, but questions asked beforehand
|
430
|
+
# are no longer reachable.
|
431
|
+
#
|
432
|
+
# Use this after you've performed some mutation based on the user's input.
|
433
|
+
def finalize
|
434
|
+
@__prompts = []
|
435
|
+
end
|
436
|
+
end
|
data/lib/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: interact
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alex Suraci
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-10-25 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 2
|
45
|
+
- 0
|
46
|
+
version: "2.0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: A simple API for command-line interaction. Provides a novel 'rewinding' feature, allowing users to go back in time and re-enter a botched answer. Supports multiple-choice, password prompting, overriding input events, defaults, etc.
|
50
|
+
email: i.am@toogeneric.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README.md
|
57
|
+
- LICENSE
|
58
|
+
files:
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- lib/interact.rb
|
63
|
+
- lib/version.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/vito/interact
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.6.2
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: A simple API for command-line interaction.
|
98
|
+
test_files: []
|
99
|
+
|