highline 1.7.10 → 2.0.0.pre.develop.2
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.simplecov +5 -0
- data/.travis.yml +11 -6
- data/Changelog.md +112 -20
- data/Gemfile +8 -7
- data/README.rdoc +3 -0
- data/Rakefile +7 -2
- data/appveyor.yml +19 -0
- data/examples/asking_for_arrays.rb +3 -0
- data/examples/basic_usage.rb +3 -0
- data/examples/get_character.rb +3 -0
- data/examples/limit.rb +3 -0
- data/examples/menus.rb +3 -0
- data/examples/overwrite.rb +3 -0
- data/examples/password.rb +3 -0
- data/examples/repeat_entry.rb +4 -1
- data/lib/highline.rb +182 -704
- data/lib/highline/builtin_styles.rb +109 -0
- data/lib/highline/color_scheme.rb +4 -1
- data/lib/highline/compatibility.rb +2 -0
- data/lib/highline/custom_errors.rb +19 -0
- data/lib/highline/import.rb +4 -2
- data/lib/highline/list.rb +93 -0
- data/lib/highline/list_renderer.rb +232 -0
- data/lib/highline/menu.rb +20 -20
- data/lib/highline/paginator.rb +43 -0
- data/lib/highline/question.rb +157 -97
- data/lib/highline/question/answer_converter.rb +84 -0
- data/lib/highline/question_asker.rb +147 -0
- data/lib/highline/simulate.rb +5 -1
- data/lib/highline/statement.rb +58 -0
- data/lib/highline/string.rb +34 -0
- data/lib/highline/string_extensions.rb +3 -28
- data/lib/highline/style.rb +18 -8
- data/lib/highline/template_renderer.rb +38 -0
- data/lib/highline/terminal.rb +78 -0
- data/lib/highline/terminal/io_console.rb +98 -0
- data/lib/highline/terminal/ncurses.rb +38 -0
- data/lib/highline/terminal/unix_stty.rb +94 -0
- data/lib/highline/version.rb +3 -1
- data/lib/highline/wrapper.rb +43 -0
- data/test/acceptance/acceptance.rb +62 -0
- data/test/acceptance/acceptance_test.rb +69 -0
- data/test/acceptance/at_color_output_using_erb_templates.rb +17 -0
- data/test/acceptance/at_echo_false.rb +23 -0
- data/test/acceptance/at_readline.rb +37 -0
- data/test/io_console_compatible.rb +37 -0
- data/test/string_methods.rb +3 -0
- data/test/test_answer_converter.rb +26 -0
- data/test/{tc_color_scheme.rb → test_color_scheme.rb} +7 -9
- data/test/test_helper.rb +26 -0
- data/test/{tc_highline.rb → test_highline.rb} +193 -136
- data/test/{tc_import.rb → test_import.rb} +5 -2
- data/test/test_list.rb +60 -0
- data/test/{tc_menu.rb → test_menu.rb} +6 -3
- data/test/test_paginator.rb +73 -0
- data/test/test_question_asker.rb +20 -0
- data/test/test_simulator.rb +24 -0
- data/test/test_string_extension.rb +72 -0
- data/test/{tc_string_highline.rb → test_string_highline.rb} +7 -3
- data/test/{tc_style.rb → test_style.rb} +70 -35
- data/test/test_wrapper.rb +188 -0
- metadata +57 -22
- data/lib/highline/system_extensions.rb +0 -254
- data/test/tc_simulator.rb +0 -33
- data/test/tc_string_extension.rb +0 -33
@@ -0,0 +1,84 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
class HighLine
|
6
|
+
class Question
|
7
|
+
class AnswerConverter
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@question,
|
11
|
+
:answer, :answer=, :check_range,
|
12
|
+
:directory, :answer_type, :choices_complete
|
13
|
+
|
14
|
+
def initialize(question)
|
15
|
+
@question = question
|
16
|
+
end
|
17
|
+
|
18
|
+
def convert
|
19
|
+
return unless answer_type
|
20
|
+
|
21
|
+
self.answer = convert_by_answer_type
|
22
|
+
check_range
|
23
|
+
answer
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_string
|
27
|
+
HighLine::String(answer)
|
28
|
+
end
|
29
|
+
|
30
|
+
# That's a weird name for a method!
|
31
|
+
# But it's working ;-)
|
32
|
+
define_method "to_highline::string" do
|
33
|
+
HighLine::String(answer)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_integer
|
37
|
+
Kernel.send(:Integer, answer)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_float
|
41
|
+
Kernel.send(:Float, answer)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_symbol
|
45
|
+
answer.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_regexp
|
49
|
+
Regexp.new(answer)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_file
|
53
|
+
self.answer = choices_complete(answer)
|
54
|
+
File.open(File.join(directory.to_s, answer.last))
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_pathname
|
58
|
+
self.answer = choices_complete(answer)
|
59
|
+
Pathname.new(File.join(directory.to_s, answer.last))
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_array
|
63
|
+
self.answer = choices_complete(answer)
|
64
|
+
answer.last
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_proc
|
68
|
+
answer_type.call(answer)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def convert_by_answer_type
|
74
|
+
if answer_type.respond_to? :parse
|
75
|
+
answer_type.parse(answer)
|
76
|
+
elsif answer_type.is_a? Class
|
77
|
+
send("to_#{answer_type.name.downcase}")
|
78
|
+
else
|
79
|
+
send("to_#{answer_type.class.name.downcase}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
class HighLine
|
2
|
+
class QuestionAsker
|
3
|
+
attr_reader :question
|
4
|
+
|
5
|
+
include CustomErrors
|
6
|
+
|
7
|
+
def initialize(question, highline)
|
8
|
+
@question = question
|
9
|
+
@highline = highline
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Gets just one answer, as opposed to #gather_answers
|
14
|
+
#
|
15
|
+
def ask_once
|
16
|
+
question.show_question(@highline)
|
17
|
+
|
18
|
+
begin
|
19
|
+
question.get_response_or_default(@highline)
|
20
|
+
raise NotValidQuestionError unless question.valid_answer?
|
21
|
+
|
22
|
+
question.convert
|
23
|
+
|
24
|
+
if question.confirm
|
25
|
+
raise NoConfirmationQuestionError unless @highline.send(:confirm, question)
|
26
|
+
end
|
27
|
+
|
28
|
+
rescue NoConfirmationQuestionError
|
29
|
+
explain_error(nil)
|
30
|
+
retry
|
31
|
+
|
32
|
+
rescue NotInRangeQuestionError
|
33
|
+
explain_error(:not_in_range)
|
34
|
+
retry
|
35
|
+
|
36
|
+
rescue NotValidQuestionError
|
37
|
+
explain_error(:not_valid)
|
38
|
+
retry
|
39
|
+
|
40
|
+
rescue QuestionError
|
41
|
+
retry
|
42
|
+
|
43
|
+
rescue ArgumentError => error
|
44
|
+
case error.message
|
45
|
+
when /ambiguous/
|
46
|
+
# the assumption here is that OptionParser::Completion#complete
|
47
|
+
# (used for ambiguity resolution) throws exceptions containing
|
48
|
+
# the word 'ambiguous' whenever resolution fails
|
49
|
+
explain_error(:ambiguous_completion)
|
50
|
+
retry
|
51
|
+
when /invalid value for/
|
52
|
+
explain_error(:invalid_type)
|
53
|
+
retry
|
54
|
+
else
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
|
58
|
+
rescue NoAutoCompleteMatch
|
59
|
+
explain_error(:no_completion)
|
60
|
+
retry
|
61
|
+
end
|
62
|
+
question.answer
|
63
|
+
end
|
64
|
+
|
65
|
+
## Multiple questions
|
66
|
+
|
67
|
+
#
|
68
|
+
# Collects an Array/Hash full of answers as described in
|
69
|
+
# HighLine::Question.gather().
|
70
|
+
#
|
71
|
+
def gather_answers
|
72
|
+
original_question_template = question.template
|
73
|
+
verify_match = question.verify_match
|
74
|
+
|
75
|
+
begin # when verify_match is set this loop will repeat until unique_answers == 1
|
76
|
+
question.template = original_question_template
|
77
|
+
|
78
|
+
answers = gather_answers_based_on_type
|
79
|
+
|
80
|
+
if verify_match && (@highline.send(:unique_answers, answers).size > 1)
|
81
|
+
explain_error(:mismatch)
|
82
|
+
else
|
83
|
+
verify_match = false
|
84
|
+
end
|
85
|
+
end while verify_match
|
86
|
+
|
87
|
+
question.verify_match ? @highline.send(:last_answer, answers) : answers
|
88
|
+
end
|
89
|
+
|
90
|
+
def gather_integer
|
91
|
+
gather_with_array do |answers|
|
92
|
+
(question.gather-1).times { answers << ask_once }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def gather_regexp
|
97
|
+
gather_with_array do |answers|
|
98
|
+
answers << ask_once until answer_matches_regex(answers.last)
|
99
|
+
answers.pop
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def gather_hash
|
104
|
+
answers = {}
|
105
|
+
|
106
|
+
question.gather.keys.sort.each do |key|
|
107
|
+
@highline.key = key
|
108
|
+
answers[key] = ask_once
|
109
|
+
end
|
110
|
+
answers
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
## Delegate to Highline
|
117
|
+
def explain_error(error)
|
118
|
+
@highline.say(question.responses[error]) if error
|
119
|
+
@highline.say(question.ask_on_error_msg)
|
120
|
+
end
|
121
|
+
|
122
|
+
def gather_with_array
|
123
|
+
[].tap do |answers|
|
124
|
+
answers << ask_once
|
125
|
+
question.template = ""
|
126
|
+
|
127
|
+
yield answers
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def answer_matches_regex(answer)
|
132
|
+
(question.gather.is_a?(::String) && answer.to_s == question.gather) ||
|
133
|
+
(question.gather.is_a?(Regexp) && answer.to_s =~ question.gather)
|
134
|
+
end
|
135
|
+
|
136
|
+
def gather_answers_based_on_type
|
137
|
+
case question.gather
|
138
|
+
when Integer
|
139
|
+
gather_integer
|
140
|
+
when ::String, Regexp
|
141
|
+
gather_regexp
|
142
|
+
when Hash
|
143
|
+
gather_hash
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
data/lib/highline/simulate.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#--
|
1
4
|
# simulate.rb
|
2
5
|
#
|
3
6
|
# Created by Andy Rossmeissl on 2012-04-29.
|
@@ -6,6 +9,7 @@
|
|
6
9
|
# This is Free Software. See LICENSE and COPYING for details.
|
7
10
|
#
|
8
11
|
# adapted from https://gist.github.com/194554
|
12
|
+
|
9
13
|
class HighLine
|
10
14
|
|
11
15
|
# Simulates Highline input for use in tests.
|
@@ -23,7 +27,7 @@ class HighLine
|
|
23
27
|
|
24
28
|
# Simulate StringIO#getbyte by shifting a single character off of the next line of the script
|
25
29
|
def getbyte
|
26
|
-
line = gets
|
30
|
+
line = gets
|
27
31
|
if line.length > 0
|
28
32
|
char = line.slice! 0
|
29
33
|
@strings.unshift line
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'highline/wrapper'
|
4
|
+
require 'highline/paginator'
|
5
|
+
require 'highline/template_renderer'
|
6
|
+
|
7
|
+
class HighLine::Statement
|
8
|
+
attr_reader :source, :highline
|
9
|
+
attr_reader :template_string
|
10
|
+
|
11
|
+
def initialize(source, highline)
|
12
|
+
@highline = highline
|
13
|
+
@source = source
|
14
|
+
@template_string = stringfy(source)
|
15
|
+
end
|
16
|
+
|
17
|
+
def statement
|
18
|
+
@statement ||= format_statement
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
statement
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def stringfy(template_string)
|
28
|
+
String(template_string || "").dup
|
29
|
+
end
|
30
|
+
|
31
|
+
def format_statement
|
32
|
+
return template_string unless template_string.length > 0
|
33
|
+
|
34
|
+
statement = render_template
|
35
|
+
|
36
|
+
statement = HighLine::Wrapper.wrap(statement, highline.wrap_at)
|
37
|
+
statement = HighLine::Paginator.new(highline).page_print(statement)
|
38
|
+
|
39
|
+
statement = statement.gsub(/\n(?!$)/,"\n#{highline.indentation}") if highline.multi_indent
|
40
|
+
statement
|
41
|
+
end
|
42
|
+
|
43
|
+
def render_template
|
44
|
+
# Assigning to a local var so it may be
|
45
|
+
# used inside instance eval block
|
46
|
+
|
47
|
+
template_renderer = TemplateRenderer.new(template, source, highline)
|
48
|
+
template_renderer.render
|
49
|
+
end
|
50
|
+
|
51
|
+
def template
|
52
|
+
@template ||= ERB.new(template_string, nil, "%")
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.const_missing(constant)
|
56
|
+
HighLine.const_get(constant)
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "highline/string_extensions"
|
4
|
+
|
5
|
+
#
|
6
|
+
# HighLine::String is a subclass of String with convenience methods added for colorization.
|
7
|
+
#
|
8
|
+
# Available convenience methods include:
|
9
|
+
# * 'color' method e.g. highline_string.color(:bright_blue, :underline)
|
10
|
+
# * colors e.g. highline_string.magenta
|
11
|
+
# * RGB colors e.g. highline_string.rgb_ff6000
|
12
|
+
# or highline_string.rgb(255,96,0)
|
13
|
+
# * background colors e.g. highline_string.on_magenta
|
14
|
+
# * RGB background colors e.g. highline_string.on_rgb_ff6000
|
15
|
+
# or highline_string.on_rgb(255,96,0)
|
16
|
+
# * styles e.g. highline_string.underline
|
17
|
+
#
|
18
|
+
# Additionally, convenience methods can be chained, for instance the following are equivalent:
|
19
|
+
# highline_string.bright_blue.blink.underline
|
20
|
+
# highline_string.color(:bright_blue, :blink, :underline)
|
21
|
+
# HighLine.color(highline_string, :bright_blue, :blink, :underline)
|
22
|
+
#
|
23
|
+
# For those less squeamish about possible conflicts, the same convenience methods can be
|
24
|
+
# added to the built-in String class, as follows:
|
25
|
+
#
|
26
|
+
# require 'highline'
|
27
|
+
# Highline.colorize_strings
|
28
|
+
#
|
29
|
+
|
30
|
+
class HighLine
|
31
|
+
class String < ::String
|
32
|
+
include StringExtensions
|
33
|
+
end
|
34
|
+
end
|
@@ -1,33 +1,12 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# HighLine::String is a subclass of String with convenience methods added for colorization.
|
4
|
-
#
|
5
|
-
# Available convenience methods include:
|
6
|
-
# * 'color' method e.g. highline_string.color(:bright_blue, :underline)
|
7
|
-
# * colors e.g. highline_string.magenta
|
8
|
-
# * RGB colors e.g. highline_string.rgb_ff6000
|
9
|
-
# or highline_string.rgb(255,96,0)
|
10
|
-
# * background colors e.g. highline_string.on_magenta
|
11
|
-
# * RGB background colors e.g. highline_string.on_rgb_ff6000
|
12
|
-
# or highline_string.on_rgb(255,96,0)
|
13
|
-
# * styles e.g. highline_string.underline
|
14
|
-
#
|
15
|
-
# Additionally, convenience methods can be chained, for instance the following are equivalent:
|
16
|
-
# highline_string.bright_blue.blink.underline
|
17
|
-
# highline_string.color(:bright_blue, :blink, :underline)
|
18
|
-
# HighLine.color(highline_string, :bright_blue, :blink, :underline)
|
19
|
-
#
|
20
|
-
# For those less squeamish about possible conflicts, the same convenience methods can be
|
21
|
-
# added to the built-in String class, as follows:
|
22
|
-
#
|
23
|
-
# require 'highline'
|
24
|
-
# Highline.colorize_strings
|
1
|
+
# coding: utf-8
|
25
2
|
|
26
3
|
class HighLine
|
27
4
|
def self.String(s)
|
28
5
|
HighLine::String.new(s)
|
29
6
|
end
|
30
7
|
|
8
|
+
# HighLine extensions for String class
|
9
|
+
# Included by HighLine::String
|
31
10
|
module StringExtensions
|
32
11
|
def self.included(base)
|
33
12
|
HighLine::COLORS.each do |color|
|
@@ -101,10 +80,6 @@ class HighLine
|
|
101
80
|
end
|
102
81
|
end
|
103
82
|
|
104
|
-
class HighLine::String < ::String
|
105
|
-
include StringExtensions
|
106
|
-
end
|
107
|
-
|
108
83
|
def self.colorize_strings
|
109
84
|
::String.send(:include, StringExtensions)
|
110
85
|
end
|
data/lib/highline/style.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#--
|
1
4
|
# color_scheme.rb
|
2
5
|
#
|
3
6
|
# Created by Richard LeBer on 2011-06-27.
|
@@ -42,18 +45,25 @@ class HighLine
|
|
42
45
|
|
43
46
|
def self.index(style)
|
44
47
|
if style.name
|
45
|
-
|
46
|
-
|
48
|
+
@styles ||= {}
|
49
|
+
@styles[style.name] = style
|
47
50
|
end
|
48
51
|
if !style.list
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
@code_index ||= {}
|
53
|
+
@code_index[style.code] ||= []
|
54
|
+
@code_index[style.code].reject!{|indexed_style| indexed_style.name == style.name}
|
55
|
+
@code_index[style.code] << style
|
53
56
|
end
|
54
57
|
style
|
55
58
|
end
|
56
59
|
|
60
|
+
def self.clear_index
|
61
|
+
# reset to builtin only styles
|
62
|
+
@styles = list.select { |name, style| style.builtin }
|
63
|
+
@code_index = {}
|
64
|
+
@styles.each { |name, style| index(style) }
|
65
|
+
end
|
66
|
+
|
57
67
|
def self.rgb_hex(*colors)
|
58
68
|
colors.map do |color|
|
59
69
|
color.is_a?(Numeric) ? '%02x'%color : color.to_s
|
@@ -87,11 +97,11 @@ class HighLine
|
|
87
97
|
end
|
88
98
|
|
89
99
|
def self.list
|
90
|
-
|
100
|
+
@styles ||= {}
|
91
101
|
end
|
92
102
|
|
93
103
|
def self.code_index
|
94
|
-
|
104
|
+
@code_index ||= {}
|
95
105
|
end
|
96
106
|
|
97
107
|
def self.uncolor(string)
|