highline 2.0.0.pre.develop.2 → 2.0.0.pre.develop.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +28 -0
- data/README.md +188 -0
- data/Rakefile +0 -11
- data/highline.gemspec +2 -4
- data/lib/highline.rb +170 -73
- data/lib/highline/builtin_styles.rb +18 -2
- data/lib/highline/color_scheme.rb +15 -5
- data/lib/highline/compatibility.rb +6 -1
- data/lib/highline/custom_errors.rb +43 -6
- data/lib/highline/import.rb +10 -3
- data/lib/highline/list.rb +95 -7
- data/lib/highline/list_renderer.rb +36 -17
- data/lib/highline/menu.rb +73 -18
- data/lib/highline/paginator.rb +10 -0
- data/lib/highline/question.rb +98 -41
- data/lib/highline/question/answer_converter.rb +19 -0
- data/lib/highline/question_asker.rb +21 -17
- data/lib/highline/simulate.rb +10 -1
- data/lib/highline/statement.rb +62 -38
- data/lib/highline/string.rb +26 -25
- data/lib/highline/string_extensions.rb +60 -32
- data/lib/highline/style.rb +125 -25
- data/lib/highline/template_renderer.rb +25 -1
- data/lib/highline/terminal.rb +124 -1
- data/lib/highline/terminal/io_console.rb +6 -75
- data/lib/highline/terminal/ncurses.rb +7 -8
- data/lib/highline/terminal/unix_stty.rb +35 -81
- data/lib/highline/version.rb +1 -1
- data/lib/highline/wrapper.rb +9 -0
- data/test/test_highline.rb +1 -1
- metadata +6 -13
- data/INSTALL +0 -59
- data/README.rdoc +0 -77
- data/setup.rb +0 -1360
@@ -4,6 +4,7 @@ require 'forwardable'
|
|
4
4
|
|
5
5
|
class HighLine
|
6
6
|
class Question
|
7
|
+
# It provides all answer conversion flow.
|
7
8
|
class AnswerConverter
|
8
9
|
extend Forwardable
|
9
10
|
|
@@ -11,10 +12,19 @@ class HighLine
|
|
11
12
|
:answer, :answer=, :check_range,
|
12
13
|
:directory, :answer_type, :choices_complete
|
13
14
|
|
15
|
+
# It should be initialized with a Question object.
|
16
|
+
# The class will get the answer from {Question#answer}
|
17
|
+
# and then convert it to the proper {Question#answer_type}.
|
18
|
+
# It is mainly used by {Question#convert}
|
19
|
+
#
|
20
|
+
# @param question [Question]
|
14
21
|
def initialize(question)
|
15
22
|
@question = question
|
16
23
|
end
|
17
24
|
|
25
|
+
# Based on the given Question object's settings,
|
26
|
+
# it makes the conversion and returns the answer.
|
27
|
+
# @return [Object] the converted answer.
|
18
28
|
def convert
|
19
29
|
return unless answer_type
|
20
30
|
|
@@ -23,6 +33,7 @@ class HighLine
|
|
23
33
|
answer
|
24
34
|
end
|
25
35
|
|
36
|
+
# @return [HighLine::String] answer converted to a HighLine::String
|
26
37
|
def to_string
|
27
38
|
HighLine::String(answer)
|
28
39
|
end
|
@@ -33,37 +44,45 @@ class HighLine
|
|
33
44
|
HighLine::String(answer)
|
34
45
|
end
|
35
46
|
|
47
|
+
# @return [Integer] answer converted to an Integer
|
36
48
|
def to_integer
|
37
49
|
Kernel.send(:Integer, answer)
|
38
50
|
end
|
39
51
|
|
52
|
+
# @return [Float] answer converted to a Float
|
40
53
|
def to_float
|
41
54
|
Kernel.send(:Float, answer)
|
42
55
|
end
|
43
56
|
|
57
|
+
# @return [Symbol] answer converted to an Symbol
|
44
58
|
def to_symbol
|
45
59
|
answer.to_sym
|
46
60
|
end
|
47
61
|
|
62
|
+
# @return [Regexp] answer converted to a Regexp
|
48
63
|
def to_regexp
|
49
64
|
Regexp.new(answer)
|
50
65
|
end
|
51
66
|
|
67
|
+
# @return [File] answer converted to a File
|
52
68
|
def to_file
|
53
69
|
self.answer = choices_complete(answer)
|
54
70
|
File.open(File.join(directory.to_s, answer.last))
|
55
71
|
end
|
56
72
|
|
73
|
+
# @return [Pathname] answer converted to an Pathname
|
57
74
|
def to_pathname
|
58
75
|
self.answer = choices_complete(answer)
|
59
76
|
Pathname.new(File.join(directory.to_s, answer.last))
|
60
77
|
end
|
61
78
|
|
79
|
+
# @return [Array] answer converted to an Array
|
62
80
|
def to_array
|
63
81
|
self.answer = choices_complete(answer)
|
64
82
|
answer.last
|
65
83
|
end
|
66
84
|
|
85
|
+
# @return [Proc] answer converted to an Proc
|
67
86
|
def to_proc
|
68
87
|
answer_type.call(answer)
|
69
88
|
end
|
@@ -1,9 +1,17 @@
|
|
1
1
|
class HighLine
|
2
|
+
# Deals with the task of "asking" a question
|
2
3
|
class QuestionAsker
|
4
|
+
# @return [Question] question to be asked
|
3
5
|
attr_reader :question
|
4
6
|
|
5
7
|
include CustomErrors
|
6
8
|
|
9
|
+
# To do its work QuestionAsker needs a Question
|
10
|
+
# to be asked and a HighLine context where to
|
11
|
+
# direct output.
|
12
|
+
#
|
13
|
+
# @param question [Question] question to be asked
|
14
|
+
# @param highline [HighLine] context
|
7
15
|
def initialize(question, highline)
|
8
16
|
@question = question
|
9
17
|
@highline = highline
|
@@ -12,6 +20,7 @@ class HighLine
|
|
12
20
|
#
|
13
21
|
# Gets just one answer, as opposed to #gather_answers
|
14
22
|
#
|
23
|
+
# @return [String] answer
|
15
24
|
def ask_once
|
16
25
|
question.show_question(@highline)
|
17
26
|
|
@@ -25,19 +34,8 @@ class HighLine
|
|
25
34
|
raise NoConfirmationQuestionError unless @highline.send(:confirm, question)
|
26
35
|
end
|
27
36
|
|
28
|
-
rescue
|
29
|
-
explain_error(
|
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
|
37
|
+
rescue ExplainableError => e
|
38
|
+
explain_error(e.explanation_key)
|
41
39
|
retry
|
42
40
|
|
43
41
|
rescue ArgumentError => error
|
@@ -54,11 +52,8 @@ class HighLine
|
|
54
52
|
else
|
55
53
|
raise
|
56
54
|
end
|
57
|
-
|
58
|
-
rescue NoAutoCompleteMatch
|
59
|
-
explain_error(:no_completion)
|
60
|
-
retry
|
61
55
|
end
|
56
|
+
|
62
57
|
question.answer
|
63
58
|
end
|
64
59
|
|
@@ -68,6 +63,7 @@ class HighLine
|
|
68
63
|
# Collects an Array/Hash full of answers as described in
|
69
64
|
# HighLine::Question.gather().
|
70
65
|
#
|
66
|
+
# @return [Array, Hash] answers
|
71
67
|
def gather_answers
|
72
68
|
original_question_template = question.template
|
73
69
|
verify_match = question.verify_match
|
@@ -87,12 +83,17 @@ class HighLine
|
|
87
83
|
question.verify_match ? @highline.send(:last_answer, answers) : answers
|
88
84
|
end
|
89
85
|
|
86
|
+
# Gather multiple integer values based on {Question#gather} count
|
87
|
+
# @return [Array] answers
|
90
88
|
def gather_integer
|
91
89
|
gather_with_array do |answers|
|
92
90
|
(question.gather-1).times { answers << ask_once }
|
93
91
|
end
|
94
92
|
end
|
95
93
|
|
94
|
+
# Gather multiple values until any of them matches the
|
95
|
+
# {Question#gather} Regexp.
|
96
|
+
# @return [Array] answers
|
96
97
|
def gather_regexp
|
97
98
|
gather_with_array do |answers|
|
98
99
|
answers << ask_once until answer_matches_regex(answers.last)
|
@@ -100,6 +101,9 @@ class HighLine
|
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
104
|
+
# Gather multiple values and store them on a Hash
|
105
|
+
# with keys provided by the Hash on {Question#gather}
|
106
|
+
# @return [Hash]
|
103
107
|
def gather_hash
|
104
108
|
answers = {}
|
105
109
|
|
data/lib/highline/simulate.rb
CHANGED
@@ -10,12 +10,15 @@
|
|
10
10
|
#
|
11
11
|
# adapted from https://gist.github.com/194554
|
12
12
|
|
13
|
+
|
13
14
|
class HighLine
|
14
15
|
|
15
16
|
# Simulates Highline input for use in tests.
|
16
17
|
class Simulate
|
17
18
|
|
18
19
|
# Creates a simulator with an array of Strings as a script
|
20
|
+
# @param strings [Array<String>] preloaded string to be used
|
21
|
+
# as input buffer when simulating.
|
19
22
|
def initialize(strings)
|
20
23
|
@strings = strings
|
21
24
|
end
|
@@ -40,7 +43,13 @@ class HighLine
|
|
40
43
|
false
|
41
44
|
end
|
42
45
|
|
43
|
-
# A wrapper method that temporarily replaces the Highline
|
46
|
+
# A wrapper method that temporarily replaces the Highline
|
47
|
+
# instance in $terminal with an instance of this object
|
48
|
+
# for the duration of the block
|
49
|
+
#
|
50
|
+
# @param strings [String] preloaded string buffer that
|
51
|
+
# will feed the input operations when simulating.
|
52
|
+
|
44
53
|
def self.with(*strings)
|
45
54
|
@input = $terminal.instance_variable_get :@input
|
46
55
|
$terminal.instance_variable_set :@input, new(strings)
|
data/lib/highline/statement.rb
CHANGED
@@ -4,55 +4,79 @@ require 'highline/wrapper'
|
|
4
4
|
require 'highline/paginator'
|
5
5
|
require 'highline/template_renderer'
|
6
6
|
|
7
|
-
class HighLine
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
class HighLine
|
8
|
+
# This class handles proper formatting based
|
9
|
+
# on a HighLine context, applying wrapping,
|
10
|
+
# pagination, indentation and color rendering
|
11
|
+
# when necessary. It's used by {HighLine#render_statement}
|
12
|
+
# @see HighLine#render_statement
|
13
|
+
class Statement
|
14
|
+
# The source object to be stringfied and formatted.
|
15
|
+
attr_reader :source
|
16
16
|
|
17
|
-
|
18
|
-
@
|
19
|
-
|
17
|
+
# The HighLine context
|
18
|
+
# @return [HighLine]
|
19
|
+
attr_reader :highline
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
# The stringfied source object
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :template_string
|
24
24
|
|
25
|
-
|
25
|
+
# It needs the input String and the HighLine context
|
26
|
+
# @param source [#to_s]
|
27
|
+
# @param highline [HighLine] context
|
28
|
+
def initialize(source, highline)
|
29
|
+
@highline = highline
|
30
|
+
@source = source
|
31
|
+
@template_string = stringfy(source)
|
32
|
+
end
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
# Returns the formated statement.
|
35
|
+
# Applies wrapping, pagination, indentation and color rendering
|
36
|
+
# based on HighLine instance settings.
|
37
|
+
# @return [String] formated statement
|
38
|
+
def statement
|
39
|
+
@statement ||= format_statement
|
40
|
+
end
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
# (see #statement)
|
43
|
+
# Delegates to {#statement}
|
44
|
+
def to_s
|
45
|
+
statement
|
46
|
+
end
|
33
47
|
|
34
|
-
|
48
|
+
private
|
35
49
|
|
36
|
-
|
37
|
-
|
50
|
+
def stringfy(template_string)
|
51
|
+
String(template_string || "").dup
|
52
|
+
end
|
38
53
|
|
39
|
-
|
40
|
-
|
41
|
-
end
|
54
|
+
def format_statement
|
55
|
+
return template_string unless template_string.length > 0
|
42
56
|
|
43
|
-
|
44
|
-
# Assigning to a local var so it may be
|
45
|
-
# used inside instance eval block
|
57
|
+
statement = render_template
|
46
58
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
59
|
+
statement = HighLine::Wrapper.wrap(statement, highline.wrap_at)
|
60
|
+
statement = HighLine::Paginator.new(highline).page_print(statement)
|
50
61
|
|
51
|
-
|
52
|
-
|
53
|
-
|
62
|
+
statement = statement.gsub(/\n(?!$)/,"\n#{highline.indentation}") if highline.multi_indent
|
63
|
+
statement
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_template
|
67
|
+
# Assigning to a local var so it may be
|
68
|
+
# used inside instance eval block
|
69
|
+
|
70
|
+
template_renderer = TemplateRenderer.new(template, source, highline)
|
71
|
+
template_renderer.render
|
72
|
+
end
|
73
|
+
|
74
|
+
def template
|
75
|
+
@template ||= ERB.new(template_string, nil, "%")
|
76
|
+
end
|
54
77
|
|
55
|
-
|
56
|
-
|
78
|
+
def self.const_missing(constant)
|
79
|
+
HighLine.const_get(constant)
|
80
|
+
end
|
57
81
|
end
|
58
82
|
end
|
data/lib/highline/string.rb
CHANGED
@@ -2,32 +2,33 @@
|
|
2
2
|
|
3
3
|
require "highline/string_extensions"
|
4
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
5
|
class HighLine
|
6
|
+
|
7
|
+
#
|
8
|
+
# HighLine::String is a subclass of String with convenience methods added for colorization.
|
9
|
+
#
|
10
|
+
# Available convenience methods include:
|
11
|
+
# * 'color' method e.g. highline_string.color(:bright_blue, :underline)
|
12
|
+
# * colors e.g. highline_string.magenta
|
13
|
+
# * RGB colors e.g. highline_string.rgb_ff6000
|
14
|
+
# or highline_string.rgb(255,96,0)
|
15
|
+
# * background colors e.g. highline_string.on_magenta
|
16
|
+
# * RGB background colors e.g. highline_string.on_rgb_ff6000
|
17
|
+
# or highline_string.on_rgb(255,96,0)
|
18
|
+
# * styles e.g. highline_string.underline
|
19
|
+
#
|
20
|
+
# Additionally, convenience methods can be chained, for instance the following are equivalent:
|
21
|
+
# highline_string.bright_blue.blink.underline
|
22
|
+
# highline_string.color(:bright_blue, :blink, :underline)
|
23
|
+
# HighLine.color(highline_string, :bright_blue, :blink, :underline)
|
24
|
+
#
|
25
|
+
# For those less squeamish about possible conflicts, the same convenience methods can be
|
26
|
+
# added to the built-in String class, as follows:
|
27
|
+
#
|
28
|
+
# require 'highline'
|
29
|
+
# Highline.colorize_strings
|
30
|
+
#
|
31
|
+
|
31
32
|
class String < ::String
|
32
33
|
include StringExtensions
|
33
34
|
end
|
@@ -1,40 +1,30 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
class HighLine
|
4
|
+
# Returns a HighLine::String from any given String.
|
5
|
+
# @param s [String]
|
6
|
+
# @return [HighLine::String] from the given string.
|
4
7
|
def self.String(s)
|
5
8
|
HighLine::String.new(s)
|
6
9
|
end
|
7
10
|
|
8
|
-
# HighLine extensions for String class
|
9
|
-
# Included by HighLine::String
|
11
|
+
# HighLine extensions for String class.
|
12
|
+
# Included by HighLine::String.
|
10
13
|
module StringExtensions
|
14
|
+
# Included hook. Actions to take when being included.
|
15
|
+
# @param base [Class, Module] base class
|
11
16
|
def self.included(base)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
undef :#{color} if method_defined? :#{color}
|
16
|
-
def #{color}
|
17
|
-
color(:#{color})
|
18
|
-
end
|
19
|
-
END
|
20
|
-
|
21
|
-
base.class_eval <<-END
|
22
|
-
undef :on_#{color} if method_defined? :on_#{color}
|
23
|
-
def on_#{color}
|
24
|
-
on(:#{color})
|
25
|
-
end
|
26
|
-
END
|
27
|
-
HighLine::STYLES.each do |style|
|
28
|
-
style = style.downcase
|
29
|
-
base.class_eval <<-END
|
30
|
-
undef :#{style} if method_defined? :#{style}
|
31
|
-
def #{style}
|
32
|
-
color(:#{style})
|
33
|
-
end
|
34
|
-
END
|
35
|
-
end
|
36
|
-
end
|
17
|
+
define_builtin_style_methods(base)
|
18
|
+
define_style_support_methods(base)
|
19
|
+
end
|
37
20
|
|
21
|
+
# At include time, it defines all basic style
|
22
|
+
# support method like #color, #on, #uncolor,
|
23
|
+
# #rgb, #on_rgb and the #method_missing callback
|
24
|
+
# @return [void]
|
25
|
+
# @param base [Class, Module] the base class/module
|
26
|
+
#
|
27
|
+
def self.define_style_support_methods(base)
|
38
28
|
base.class_eval do
|
39
29
|
undef :color if method_defined? :color
|
40
30
|
undef :foreground if method_defined? :foreground
|
@@ -55,19 +45,17 @@ class HighLine
|
|
55
45
|
|
56
46
|
undef :rgb if method_defined? :rgb
|
57
47
|
def rgb(*colors)
|
58
|
-
color_code = colors
|
59
|
-
raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/
|
48
|
+
color_code = setup_color_code(*colors)
|
60
49
|
color("rgb_#{color_code}".to_sym)
|
61
50
|
end
|
62
51
|
|
63
52
|
undef :on_rgb if method_defined? :on_rgb
|
64
53
|
def on_rgb(*colors)
|
65
|
-
color_code = colors
|
66
|
-
raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/
|
54
|
+
color_code = setup_color_code(*colors)
|
67
55
|
color("on_rgb_#{color_code}".to_sym)
|
68
56
|
end
|
69
57
|
|
70
|
-
#
|
58
|
+
# @todo Chain existing method_missing?
|
71
59
|
undef :method_missing if method_defined? :method_missing
|
72
60
|
def method_missing(method, *args, &blk)
|
73
61
|
if method.to_s =~ /^(on_)?rgb_([0-9a-fA-F]{6})$/
|
@@ -76,10 +64,50 @@ class HighLine
|
|
76
64
|
raise NoMethodError, "undefined method `#{method}' for #<#{self.class}:#{'%#x'%self.object_id}>"
|
77
65
|
end
|
78
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def setup_color_code(*colors)
|
71
|
+
color_code = colors.map{|color| color.is_a?(Numeric) ? '%02x'%color : color.to_s}.join
|
72
|
+
raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/
|
73
|
+
color_code
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# At include time, it defines all basic builtin styles.
|
79
|
+
# @param base [Class, Module] base Class/Module
|
80
|
+
def self.define_builtin_style_methods(base)
|
81
|
+
HighLine::COLORS.each do |color|
|
82
|
+
color = color.downcase
|
83
|
+
base.class_eval <<-END
|
84
|
+
undef :#{color} if method_defined? :#{color}
|
85
|
+
def #{color}
|
86
|
+
color(:#{color})
|
87
|
+
end
|
88
|
+
END
|
89
|
+
|
90
|
+
base.class_eval <<-END
|
91
|
+
undef :on_#{color} if method_defined? :on_#{color}
|
92
|
+
def on_#{color}
|
93
|
+
on(:#{color})
|
94
|
+
end
|
95
|
+
END
|
96
|
+
|
97
|
+
HighLine::STYLES.each do |style|
|
98
|
+
style = style.downcase
|
99
|
+
base.class_eval <<-END
|
100
|
+
undef :#{style} if method_defined? :#{style}
|
101
|
+
def #{style}
|
102
|
+
color(:#{style})
|
103
|
+
end
|
104
|
+
END
|
105
|
+
end
|
79
106
|
end
|
80
107
|
end
|
81
108
|
end
|
82
109
|
|
110
|
+
# Adds color support to the base String class
|
83
111
|
def self.colorize_strings
|
84
112
|
::String.send(:include, StringExtensions)
|
85
113
|
end
|