highline 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +16 -0
- data/Rakefile +1 -1
- data/TODO +2 -2
- data/examples/basic_usage.rb +8 -2
- data/examples/menus.rb +16 -0
- data/examples/password.rb +7 -0
- data/lib/highline.rb +41 -11
- data/lib/highline/question.rb +60 -1
- data/test/tc_highline.rb +91 -0
- metadata +4 -2
data/CHANGELOG
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
Below is a complete listing of changes for each revision of HighLine.
|
4
4
|
|
5
|
+
== 0.5.0
|
6
|
+
|
7
|
+
* Implemented <tt>echo = false</tt> for HighLine::Question objects, primarily to
|
8
|
+
make fetching passwords trivial.
|
9
|
+
* Fixed an auto-complete bug that could cause a crash when the user gave an
|
10
|
+
answer that didn't complete to any valid choice.
|
11
|
+
* Implemented +case+ for HighLine::Question objects to provide character case
|
12
|
+
conversions on given answers. Can be set to <tt>:up</tt>, <tt>:down</tt>, or
|
13
|
+
<tt>:capitalize</tt>.
|
14
|
+
* Exposed <tt>@answer</tt> to the response system, to allow response that are
|
15
|
+
aware of incorrect input.
|
16
|
+
* Implemented +confirm+ for HighLine::Question objects to allow for verification
|
17
|
+
for sensitive user choices. If set to +true+, user will have to answer an
|
18
|
+
"Are you sure? " question. Can also be set to the question to confirm with
|
19
|
+
the user.
|
20
|
+
|
5
21
|
== 0.4.0
|
6
22
|
|
7
23
|
* Added <tt>@wrap_at</tt> and <tt>@page_at</tt> settings and accessors to
|
data/Rakefile
CHANGED
@@ -28,7 +28,7 @@ end
|
|
28
28
|
|
29
29
|
spec = Gem::Specification.new do |spec|
|
30
30
|
spec.name = "highline"
|
31
|
-
spec.version = "0.
|
31
|
+
spec.version = "0.5.0"
|
32
32
|
spec.platform = Gem::Platform::RUBY
|
33
33
|
spec.summary = "HighLine is a high-level line oriented console interface."
|
34
34
|
spec.files = Dir.glob("{examples,lib,test}/**/*.rb").
|
data/TODO
CHANGED
@@ -6,6 +6,6 @@ order.
|
|
6
6
|
* Support <tt>ask(..., Array)</tt>, better than the current
|
7
7
|
<tt>ask(..., lambda { |arr| arr.split(",") })</tt> or similar.
|
8
8
|
* Support <tt>ask(..., Hash)</tt>.
|
9
|
+
* Support <tt>ask(..., File)</tt>.
|
9
10
|
* Implement choose() for simple menu handling.
|
10
|
-
*
|
11
|
-
* Add an "Are you sure?" check for ask().
|
11
|
+
* Add readline support for history and editing.
|
data/examples/basic_usage.rb
CHANGED
@@ -40,7 +40,10 @@ begin
|
|
40
40
|
entry[:company] = ask("Company? ") { |q| q.default = "none" }
|
41
41
|
entry[:address] = ask("Address? ")
|
42
42
|
entry[:city] = ask("City? ")
|
43
|
-
entry[:state] = ask("State? ")
|
43
|
+
entry[:state] = ask("State? ") do |q|
|
44
|
+
q.case = :up
|
45
|
+
q.validate = /\A[A-Z]{2}\Z/
|
46
|
+
end
|
44
47
|
entry[:zip] = ask("Zip? ") do |q|
|
45
48
|
q.validate = /\A\d{5}(?:-?\d{4})?\Z/
|
46
49
|
end
|
@@ -64,6 +67,9 @@ begin
|
|
64
67
|
end while agree("Enter another contact? ", true)
|
65
68
|
|
66
69
|
if agree("Save these contacts? ", true)
|
67
|
-
file_name = ask("Enter a file name: ")
|
70
|
+
file_name = ask("Enter a file name: ") do |q|
|
71
|
+
q.validate = /\A\w+\Z/
|
72
|
+
q.confirm = true
|
73
|
+
end
|
68
74
|
File.open("#{file_name}.yaml", "w") { |file| YAML.dump(contacts, file) }
|
69
75
|
end
|
data/examples/menus.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "highline/import"
|
5
|
+
|
6
|
+
choices = %w{ruby python perl}
|
7
|
+
|
8
|
+
say("Please choose your favorite programming language:")
|
9
|
+
say(choices.map { |c| " #{c}\n" }.join)
|
10
|
+
|
11
|
+
case ask("? ", choices)
|
12
|
+
when "ruby"
|
13
|
+
say("Good choice!")
|
14
|
+
else
|
15
|
+
say("Not from around here, are you?")
|
16
|
+
end
|
data/lib/highline.rb
CHANGED
@@ -132,16 +132,35 @@ class HighLine
|
|
132
132
|
|
133
133
|
say(@question)
|
134
134
|
begin
|
135
|
-
answer = @question.answer_or_default(get_response
|
136
|
-
unless @question.valid_answer?(answer)
|
135
|
+
@answer = @question.answer_or_default(get_response)
|
136
|
+
unless @question.valid_answer?(@answer)
|
137
137
|
explain_error(:not_valid)
|
138
138
|
raise QuestionError
|
139
139
|
end
|
140
140
|
|
141
|
-
answer = @question.convert(answer)
|
141
|
+
@answer = @question.convert(@answer)
|
142
142
|
|
143
|
-
if @question.in_range?(answer)
|
144
|
-
|
143
|
+
if @question.in_range?(@answer)
|
144
|
+
if @question.confirm
|
145
|
+
# need to add a layer of scope to ask a question inside a
|
146
|
+
# question, without destroying instance data
|
147
|
+
context_change = self.class.new( @input, @output,
|
148
|
+
@wrap_at, @page_at )
|
149
|
+
if @question.confirm == true
|
150
|
+
confirm_question = "Are you sure? "
|
151
|
+
else
|
152
|
+
# evaluate ERb under initial scope, so it will have
|
153
|
+
# access to @question and @answer
|
154
|
+
template = ERB.new(@question.confirm, nil, "%")
|
155
|
+
confirm_question = template.result(binding)
|
156
|
+
end
|
157
|
+
unless context_change.agree(confirm_question)
|
158
|
+
explain_error(nil)
|
159
|
+
raise QuestionError
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@answer
|
145
164
|
else
|
146
165
|
explain_error(:not_in_range)
|
147
166
|
raise QuestionError
|
@@ -212,7 +231,7 @@ class HighLine
|
|
212
231
|
# of the question.
|
213
232
|
#
|
214
233
|
def explain_error( error )
|
215
|
-
say(@question.responses[error])
|
234
|
+
say(@question.responses[error]) unless error.nil?
|
216
235
|
if @question.responses[:ask_on_error] == :question
|
217
236
|
say(@question)
|
218
237
|
elsif @question.responses[:ask_on_error]
|
@@ -251,7 +270,7 @@ class HighLine
|
|
251
270
|
# requested by the Question object.
|
252
271
|
#
|
253
272
|
def get_line( )
|
254
|
-
@question.remove_whitespace(@input.gets)
|
273
|
+
@question.change_case(@question.remove_whitespace(@input.gets))
|
255
274
|
end
|
256
275
|
|
257
276
|
#
|
@@ -261,13 +280,24 @@ class HighLine
|
|
261
280
|
#
|
262
281
|
def get_response( )
|
263
282
|
if @question.character.nil?
|
264
|
-
|
283
|
+
if @question.echo
|
284
|
+
get_line
|
285
|
+
else
|
286
|
+
line = ""
|
287
|
+
while character = get_character
|
288
|
+
line << character.chr
|
289
|
+
|
290
|
+
break if character == 13
|
291
|
+
end
|
292
|
+
say("\n")
|
293
|
+
@question.change_case(@question.remove_whitespace(line))
|
294
|
+
end
|
265
295
|
elsif @question.character == :getc
|
266
|
-
@input.getc.chr
|
296
|
+
@question.change_case(@input.getc.chr)
|
267
297
|
else
|
268
298
|
response = get_character.chr
|
269
|
-
|
270
|
-
response
|
299
|
+
say("#{if @question.echo then response else '' end}\n")
|
300
|
+
@question.change_case(response)
|
271
301
|
end
|
272
302
|
end
|
273
303
|
|
data/lib/highline/question.rb
CHANGED
@@ -29,12 +29,15 @@ class HighLine
|
|
29
29
|
@answer_type = answer_type
|
30
30
|
|
31
31
|
@character = nil
|
32
|
+
@echo = true
|
32
33
|
@whitespace = :strip
|
34
|
+
@case = nil
|
33
35
|
@default = nil
|
34
36
|
@validate = nil
|
35
37
|
@above = nil
|
36
38
|
@below = nil
|
37
39
|
@in = nil
|
40
|
+
@confirm = nil
|
38
41
|
@responses = Hash.new
|
39
42
|
|
40
43
|
# allow block to override settings
|
@@ -67,10 +70,23 @@ class HighLine
|
|
67
70
|
#
|
68
71
|
attr_accessor :character
|
69
72
|
#
|
73
|
+
# Can be set to +true+ or +false+ to control whether or not input will
|
74
|
+
# be echoed back to the user.
|
75
|
+
#
|
76
|
+
# This requires HighLine's character reader. See the _character_
|
77
|
+
# attribute for details.
|
78
|
+
#
|
79
|
+
attr_accessor :echo
|
80
|
+
#
|
70
81
|
# Used to control whitespace processing for the answer to this question.
|
71
82
|
# See HighLine::Question.remove_whitespace() for acceptable settings.
|
72
83
|
#
|
73
84
|
attr_accessor :whitespace
|
85
|
+
#
|
86
|
+
# Used to control whitespace processing for the answer to this question.
|
87
|
+
# See HighLine::Question.change_case() for acceptable settings.
|
88
|
+
#
|
89
|
+
attr_accessor :case
|
74
90
|
# Used to provide a default answer to this question.
|
75
91
|
attr_accessor :default
|
76
92
|
#
|
@@ -84,6 +100,14 @@ class HighLine
|
|
84
100
|
# If set, answer must pass an include?() check on this object.
|
85
101
|
attr_accessor :in
|
86
102
|
#
|
103
|
+
# Asks a yes or no confirmation question, to ensure a user knows what
|
104
|
+
# they have just agreed to. If set to +true+ the question will be,
|
105
|
+
# "Are you sure? " Any other true value for this attribute is assumed
|
106
|
+
# to be the question to ask. When +false+ or +nil+ (the default),
|
107
|
+
# answers are not confirmed.
|
108
|
+
#
|
109
|
+
attr_accessor :confirm
|
110
|
+
#
|
87
111
|
# A Hash that stores the various responses used by HighLine to notify
|
88
112
|
# the user. The currently used responses and their purpose are as
|
89
113
|
# follows:
|
@@ -117,6 +141,32 @@ class HighLine
|
|
117
141
|
answer_string
|
118
142
|
end
|
119
143
|
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Returns the provided _answer_string_ after changing character case by
|
147
|
+
# the rules of this Question. Valid settings for whitespace are:
|
148
|
+
#
|
149
|
+
# +nil+:: Do not alter character case.
|
150
|
+
# (Default.)
|
151
|
+
# <tt>:up</tt>:: Calls upcase().
|
152
|
+
# <tt>:upcase</tt>:: Calls upcase().
|
153
|
+
# <tt>:down</tt>:: Calls downcase().
|
154
|
+
# <tt>:downcase</tt>:: Calls downcase().
|
155
|
+
# <tt>:capitalize</tt>:: Calls capitalize().
|
156
|
+
#
|
157
|
+
# An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
|
158
|
+
#
|
159
|
+
def change_case( answer_string )
|
160
|
+
if [:up, :upcase].include?(@case)
|
161
|
+
answer_string.upcase
|
162
|
+
elsif [:down, :downcase].include?(@case)
|
163
|
+
answer_string.downcase
|
164
|
+
elsif @case == :capitalize
|
165
|
+
answer_string.capitalize
|
166
|
+
else
|
167
|
+
answer_string
|
168
|
+
end
|
169
|
+
end
|
120
170
|
|
121
171
|
#
|
122
172
|
# Transforms the given _answer_string_ into the expected type for this
|
@@ -153,7 +203,11 @@ class HighLine
|
|
153
203
|
elsif @answer_type.is_a?(Array)
|
154
204
|
# cheating, using OptionParser's Completion module
|
155
205
|
@answer_type.extend(OptionParser::Completion)
|
156
|
-
@answer_type.complete(answer_string)
|
206
|
+
answer = @answer_type.complete(answer_string)
|
207
|
+
if answer.nil?
|
208
|
+
raise ArgumentError, "Not matching auto-complete choice."
|
209
|
+
end
|
210
|
+
answer.last
|
157
211
|
elsif [Date, DateTime].include?(@answer_type) or
|
158
212
|
@answer_type.is_a?(Class)
|
159
213
|
@answer_type.parse(answer_string)
|
@@ -207,6 +261,8 @@ class HighLine
|
|
207
261
|
#
|
208
262
|
# An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
|
209
263
|
#
|
264
|
+
# This process is skipped, for single character input.
|
265
|
+
#
|
210
266
|
def remove_whitespace( answer_string )
|
211
267
|
if @whitespace.nil?
|
212
268
|
answer_string
|
@@ -233,6 +289,9 @@ class HighLine
|
|
233
289
|
#
|
234
290
|
# Returns +true+ if the provided _answer_string_ is accepted by the
|
235
291
|
# _validate_ attribute or +false+ if it's not.
|
292
|
+
#
|
293
|
+
# It's important to realize that an answer is validated after whitespace
|
294
|
+
# and case handling.
|
236
295
|
#
|
237
296
|
def valid_answer?( answer_string )
|
238
297
|
@validate.nil? or
|
data/test/tc_highline.rb
CHANGED
@@ -44,6 +44,46 @@ class TestHighLine < Test::Unit::TestCase
|
|
44
44
|
assert_equal(name, @terminal.ask("What is your name? "))
|
45
45
|
end
|
46
46
|
|
47
|
+
def test_bug_fixes
|
48
|
+
# auto-complete bug
|
49
|
+
@input << "ruby\nRuby\n"
|
50
|
+
@input.rewind
|
51
|
+
|
52
|
+
languages = [:Perl, :Python, :Ruby]
|
53
|
+
answer = @terminal.ask( "What is your favorite programming language? ",
|
54
|
+
languages )
|
55
|
+
assert_equal(languages.last, answer)
|
56
|
+
|
57
|
+
@input.truncate(@input.rewind)
|
58
|
+
@input << "ruby\n"
|
59
|
+
@input.rewind
|
60
|
+
|
61
|
+
answer = @terminal.ask( "What is your favorite programming language? ",
|
62
|
+
languages ) do |q|
|
63
|
+
q.case = :capitalize
|
64
|
+
end
|
65
|
+
assert_equal(languages.last, answer)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_case_changes
|
69
|
+
@input << "jeg2\n"
|
70
|
+
@input.rewind
|
71
|
+
|
72
|
+
answer = @terminal.ask("Enter your initials ") do |q|
|
73
|
+
q.case = :up
|
74
|
+
end
|
75
|
+
assert_equal("JEG2", answer)
|
76
|
+
|
77
|
+
@input.truncate(@input.rewind)
|
78
|
+
@input << "cRaZY\n"
|
79
|
+
@input.rewind
|
80
|
+
|
81
|
+
answer = @terminal.ask("Enter a search string: ") do |q|
|
82
|
+
q.case = :down
|
83
|
+
end
|
84
|
+
assert_equal("crazy", answer)
|
85
|
+
end
|
86
|
+
|
47
87
|
def test_character_reading
|
48
88
|
# WARNING: This method does NOT cover Unix and Windows savvy testing!
|
49
89
|
@input << "12345"
|
@@ -79,6 +119,35 @@ class TestHighLine < Test::Unit::TestCase
|
|
79
119
|
@output.string )
|
80
120
|
end
|
81
121
|
|
122
|
+
def test_confirm
|
123
|
+
@input << "junk.txt\nno\nsave.txt\ny\n"
|
124
|
+
@input.rewind
|
125
|
+
|
126
|
+
answer = @terminal.ask("Enter a filename: ") do |q|
|
127
|
+
q.confirm = "Are you sure you want to overwrite <%= @answer %>? "
|
128
|
+
q.responses[:ask_on_error] = :question
|
129
|
+
end
|
130
|
+
assert_equal("save.txt", answer)
|
131
|
+
assert_equal( "Enter a filename: " +
|
132
|
+
"Are you sure you want to overwrite junk.txt? " +
|
133
|
+
"Enter a filename: " +
|
134
|
+
"Are you sure you want to overwrite save.txt? ",
|
135
|
+
@output.string )
|
136
|
+
|
137
|
+
@input.truncate(@input.rewind)
|
138
|
+
@input << "junk.txt\nyes\nsave.txt\nn\n"
|
139
|
+
@input.rewind
|
140
|
+
@output.truncate(@output.rewind)
|
141
|
+
|
142
|
+
answer = @terminal.ask("Enter a filename: ") do |q|
|
143
|
+
q.confirm = "Are you sure you want to overwrite <%= @answer %>? "
|
144
|
+
end
|
145
|
+
assert_equal("junk.txt", answer)
|
146
|
+
assert_equal( "Enter a filename: " +
|
147
|
+
"Are you sure you want to overwrite junk.txt? ",
|
148
|
+
@output.string )
|
149
|
+
end
|
150
|
+
|
82
151
|
def test_defaults
|
83
152
|
@input << "\nNo Comment\n"
|
84
153
|
@input.rewind
|
@@ -159,6 +228,28 @@ class TestHighLine < Test::Unit::TestCase
|
|
159
228
|
assert_equal("Edward", answer.middle)
|
160
229
|
end
|
161
230
|
|
231
|
+
def test_no_echo
|
232
|
+
@input << "password\r"
|
233
|
+
@input.rewind
|
234
|
+
|
235
|
+
answer = @terminal.ask("Please enter your password: ") do |q|
|
236
|
+
q.echo = false
|
237
|
+
end
|
238
|
+
assert_equal("password", answer)
|
239
|
+
assert_equal("Please enter your password: \n", @output.string)
|
240
|
+
|
241
|
+
@input.rewind
|
242
|
+
@output.truncate(@output.rewind)
|
243
|
+
|
244
|
+
answer = @terminal.ask("Pick a letter or number: ") do |q|
|
245
|
+
q.character = true
|
246
|
+
q.echo = false
|
247
|
+
end
|
248
|
+
assert_equal("p", answer)
|
249
|
+
assert_equal("a", @input.getc.chr)
|
250
|
+
assert_equal("Pick a letter or number: \n", @output.string)
|
251
|
+
end
|
252
|
+
|
162
253
|
def test_paging
|
163
254
|
@terminal.page_at = 22
|
164
255
|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: highline
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2005-05-
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2005-05-09
|
8
8
|
summary: HighLine is a high-level line oriented console interface.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -31,7 +31,9 @@ authors:
|
|
31
31
|
files:
|
32
32
|
- examples/ansi_colors.rb
|
33
33
|
- examples/basic_usage.rb
|
34
|
+
- examples/menus.rb
|
34
35
|
- examples/page_and_wrap.rb
|
36
|
+
- examples/password.rb
|
35
37
|
- lib/highline.rb
|
36
38
|
- lib/highline/import.rb
|
37
39
|
- lib/highline/question.rb
|