interact 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|