highline 0.4.0 → 0.5.0
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/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
|