tty 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +78 -12
- data/benchmarks/shell.rb +26 -0
- data/benchmarks/table.rb +35 -0
- data/lib/tty.rb +23 -1
- data/lib/tty/coercer.rb +13 -0
- data/lib/tty/coercer/boolean.rb +39 -0
- data/lib/tty/coercer/float.rb +23 -0
- data/lib/tty/coercer/integer.rb +23 -0
- data/lib/tty/coercer/range.rb +33 -0
- data/lib/tty/shell.rb +6 -2
- data/lib/tty/shell/question.rb +158 -138
- data/lib/tty/shell/reader.rb +92 -0
- data/lib/tty/shell/response.rb +219 -0
- data/lib/tty/shell/response_delegation.rb +53 -0
- data/lib/tty/table.rb +90 -16
- data/lib/tty/table/border.rb +34 -8
- data/lib/tty/table/border/ascii.rb +16 -25
- data/lib/tty/table/border/null.rb +0 -6
- data/lib/tty/table/border/unicode.rb +16 -25
- data/lib/tty/table/column_set.rb +1 -1
- data/lib/tty/table/error.rb +10 -0
- data/lib/tty/table/operation/wrapped.rb +0 -6
- data/lib/tty/table/orientation.rb +57 -0
- data/lib/tty/table/orientation/horizontal.rb +19 -0
- data/lib/tty/table/orientation/vertical.rb +19 -0
- data/lib/tty/table/renderer.rb +7 -0
- data/lib/tty/table/renderer/ascii.rb +1 -1
- data/lib/tty/table/renderer/basic.rb +2 -2
- data/lib/tty/table/renderer/unicode.rb +1 -1
- data/lib/tty/table/validatable.rb +20 -0
- data/lib/tty/terminal.rb +15 -14
- data/lib/tty/terminal/color.rb +1 -1
- data/lib/tty/terminal/echo.rb +41 -0
- data/lib/tty/terminal/home.rb +31 -0
- data/lib/tty/text.rb +85 -0
- data/lib/tty/text/truncation.rb +83 -0
- data/lib/tty/text/wrapping.rb +96 -0
- data/lib/tty/version.rb +1 -1
- data/spec/tty/coercer/boolean/coerce_spec.rb +113 -0
- data/spec/tty/coercer/float/coerce_spec.rb +32 -0
- data/spec/tty/coercer/integer/coerce_spec.rb +39 -0
- data/spec/tty/coercer/range/coerce_spec.rb +73 -0
- data/spec/tty/shell/ask_spec.rb +14 -1
- data/spec/tty/shell/question/argument_spec.rb +30 -0
- data/spec/tty/shell/question/character_spec.rb +16 -0
- data/spec/tty/shell/question/default_spec.rb +25 -0
- data/spec/tty/shell/question/in_spec.rb +23 -0
- data/spec/tty/shell/question/initialize_spec.rb +11 -211
- data/spec/tty/shell/question/modifier/whitespace_spec.rb +1 -1
- data/spec/tty/shell/question/modify_spec.rb +44 -0
- data/spec/tty/shell/question/valid_spec.rb +46 -0
- data/spec/tty/shell/question/validate_spec.rb +30 -0
- data/spec/tty/shell/reader/getc_spec.rb +40 -0
- data/spec/tty/shell/response/read_bool_spec.rb +41 -0
- data/spec/tty/shell/response/read_char_spec.rb +17 -0
- data/spec/tty/shell/response/read_date_spec.rb +20 -0
- data/spec/tty/shell/response/read_email_spec.rb +43 -0
- data/spec/tty/shell/response/read_multiple_spec.rb +24 -0
- data/spec/tty/shell/response/read_number_spec.rb +29 -0
- data/spec/tty/shell/response/read_range_spec.rb +29 -0
- data/spec/tty/shell/response/read_spec.rb +68 -0
- data/spec/tty/shell/response/read_string_spec.rb +19 -0
- data/spec/tty/table/access_spec.rb +6 -0
- data/spec/tty/table/border/new_spec.rb +3 -3
- data/spec/tty/table/initialize_spec.rb +17 -1
- data/spec/tty/table/options_spec.rb +7 -1
- data/spec/tty/table/orientation_spec.rb +98 -0
- data/spec/tty/table/renders_with_spec.rb +76 -0
- data/spec/tty/table/rotate_spec.rb +72 -0
- data/spec/tty/table/to_s_spec.rb +13 -1
- data/spec/tty/table/validatable/validate_options_spec.rb +34 -0
- data/spec/tty/terminal/color/remove_spec.rb +34 -1
- data/spec/tty/terminal/echo_spec.rb +22 -0
- data/spec/tty/text/truncate_spec.rb +13 -0
- data/spec/tty/text/truncation/initialize_spec.rb +29 -0
- data/spec/tty/text/truncation/truncate_spec.rb +73 -0
- data/spec/tty/text/wrap_spec.rb +14 -0
- data/spec/tty/text/wrapping/initialize_spec.rb +25 -0
- data/spec/tty/text/wrapping/wrap_spec.rb +80 -0
- data/tty.gemspec +1 -0
- metadata +101 -8
data/README.md
CHANGED
@@ -50,12 +50,19 @@ To instantiate table pass 2-dimensional array:
|
|
50
50
|
table = TTY::Table.new header: ['h1', 'h2'], rows: [['a1', 'a2'], ['b1', 'b2']]
|
51
51
|
```
|
52
52
|
|
53
|
+
or cross header with rows inside a hash like so
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
table = TTY::Table.new [{'h1' => ['a1', 'a2'], 'h2' => ['b1', 'b2']}]
|
57
|
+
```
|
58
|
+
|
53
59
|
Apart from `rows` and `header`, you can provide other customization options such as
|
54
60
|
|
55
61
|
```ruby
|
56
|
-
column_widths #
|
62
|
+
column_widths # array of maximum columns widths
|
57
63
|
column_aligns # array of cell alignments out of :left, :center and :right
|
58
64
|
renderer # enforce display type out of :basic, :color, :unicode, :ascii
|
65
|
+
orientation # either :horizontal or :vertical
|
59
66
|
```
|
60
67
|
|
61
68
|
Table behaves like an Array so `<<`, `each` and familiar methods can be used
|
@@ -106,13 +113,38 @@ To print border around data table you need to specify `renderer` type out of `ba
|
|
106
113
|
└───────┴───────┘
|
107
114
|
```
|
108
115
|
|
116
|
+
You can also create your own custom border by subclassing `TTY::Table::Border`
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
class MyBorder < TTY::Table::Border
|
120
|
+
def_border do
|
121
|
+
{
|
122
|
+
'bottom' => ' ',
|
123
|
+
'bottom_mid' => '*',
|
124
|
+
'bottom_left' => '*',
|
125
|
+
'bottom_right' => '*',
|
126
|
+
'left' => '$',
|
127
|
+
'right' => '$'
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
Next pass the border to your table
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
table.renders_with MyBorder
|
136
|
+
```
|
137
|
+
|
109
138
|
### Terminal
|
110
139
|
|
140
|
+
To read general terminal properties you can use on of the helpers
|
141
|
+
|
111
142
|
```ruby
|
112
143
|
term = TTY::Terminal.new
|
113
|
-
term.width
|
114
|
-
term.height
|
115
|
-
term.color?
|
144
|
+
term.width # => 140
|
145
|
+
term.height # => 60
|
146
|
+
term.color? # => true or false
|
147
|
+
term.echo(false) { } # switch off echo for the block
|
116
148
|
```
|
117
149
|
|
118
150
|
To colorize your output do
|
@@ -127,6 +159,19 @@ To colorize your output do
|
|
127
159
|
|
128
160
|
Main responsibility is to interact with the prompt and provide convenience methods.
|
129
161
|
|
162
|
+
Available methods are
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
shell = TTY::Shell.new
|
166
|
+
shell.ask # print question
|
167
|
+
shell.read # read from stdin
|
168
|
+
shell.say # print message to stdout
|
169
|
+
shell.confirm # print message(s) in green
|
170
|
+
shell.warn # print message(s) in yellow
|
171
|
+
shell.error # print message(s) in red
|
172
|
+
shell.print_table # print table to stdout
|
173
|
+
```
|
174
|
+
|
130
175
|
In order to ask question and parse answers:
|
131
176
|
|
132
177
|
```ruby
|
@@ -137,12 +182,16 @@ In order to ask question and parse answers:
|
|
137
182
|
The library provides small DSL to help with parsing and asking precise questions
|
138
183
|
|
139
184
|
```ruby
|
140
|
-
default # default value used if none is provided
|
141
185
|
argument # :required or :optional
|
186
|
+
character # turn character based input, otherwise line (default: false)
|
187
|
+
clean # reset question
|
188
|
+
default # default value used if none is provided
|
189
|
+
echo # turn echo on and off (default: true)
|
190
|
+
mask # mask characters i.e '****' (default: false)
|
191
|
+
modify # apply answer modification :upcase, :downcase, :trim, :chomp etc..
|
192
|
+
range # specify range '0-9', '0..9', '0...9' or negative '-1..-9'
|
142
193
|
validate # regex against which stdin input is checked
|
143
194
|
valid # a list of expected valid options
|
144
|
-
modify # apply answer modification :upcase, :downcase, :trim, :chomp etc..
|
145
|
-
clean # reset question
|
146
195
|
```
|
147
196
|
|
148
197
|
You can chain question methods or configure them inside a block
|
@@ -162,14 +211,31 @@ You can chain question methods or configure them inside a block
|
|
162
211
|
Reading answers and converting them into required types can be done with custom readers
|
163
212
|
|
164
213
|
```ruby
|
165
|
-
read_string # return string
|
166
214
|
read_bool # return true or false for strings such as "Yes", "No"
|
167
|
-
read_int # return integer or error if cannot convert
|
168
|
-
read_float # return decimal or error if cannot convert
|
169
215
|
read_date # return date type
|
170
216
|
read_datetime # return datetime type
|
171
|
-
read_multiple # return multiple line string
|
172
217
|
read_email # validate answer against email regex
|
218
|
+
read_float # return decimal or error if cannot convert
|
219
|
+
read_int # return integer or error if cannot convert
|
220
|
+
read_multiple # return multiple line string
|
221
|
+
read_password # return string with echo turned off
|
222
|
+
read_range # return range type
|
223
|
+
read_string # return string
|
224
|
+
```
|
225
|
+
|
226
|
+
For example, if we wanted to ask a user for a single digit in given range
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
ask("Provide number in range: 0-9") do
|
230
|
+
range '0-9'
|
231
|
+
on_error :retry
|
232
|
+
end.read_int
|
233
|
+
```
|
234
|
+
|
235
|
+
on the other hand, if we are interested in range answer then
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
ask("Provide range of numbers?").read_range
|
173
239
|
```
|
174
240
|
|
175
241
|
### System
|
@@ -189,4 +255,4 @@ Reading answers and converting them into required types can be done with custom
|
|
189
255
|
|
190
256
|
## Copyright
|
191
257
|
|
192
|
-
Copyright (c) 2012 Piotr Murach. See LICENSE for further details.
|
258
|
+
Copyright (c) 2012-2013 Piotr Murach. See LICENSE for further details.
|
data/benchmarks/shell.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Benchmark speed of shell operations
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
6
|
+
|
7
|
+
require 'tty'
|
8
|
+
require 'benchmark'
|
9
|
+
require 'benchmark/ips'
|
10
|
+
require 'stringio'
|
11
|
+
|
12
|
+
input = ::StringIO.new
|
13
|
+
output = ::StringIO.new
|
14
|
+
shell = TTY::Shell.new(input, output)
|
15
|
+
|
16
|
+
Benchmark.ips do |r|
|
17
|
+
|
18
|
+
r.report("Ruby #puts") do
|
19
|
+
output.puts "What is your name?"
|
20
|
+
end
|
21
|
+
|
22
|
+
r.report("TTY #ask") do
|
23
|
+
shell.ask("What is your name?")
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/benchmarks/table.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Benchmark speed of table operations
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
6
|
+
|
7
|
+
require 'tty'
|
8
|
+
require 'benchmark'
|
9
|
+
require 'benchmark/ips'
|
10
|
+
|
11
|
+
header = [:name, :color]
|
12
|
+
rows = (1..100).map { |n| ["row#{n}", "red"] }
|
13
|
+
table = TTY::Table.new(header, rows)
|
14
|
+
table_ascii = TTY::Table.new(header, rows, :renderer => :ascii)
|
15
|
+
table_unicode = TTY::Table.new(header, rows, :renderer => :unicode)
|
16
|
+
|
17
|
+
Benchmark.ips do |r|
|
18
|
+
|
19
|
+
r.report("Ruby #to_s") do
|
20
|
+
rows.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
r.report("TTY #to_s") do
|
24
|
+
table.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
r.report("TTY ASCII #to_s") do
|
28
|
+
table_ascii.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
r.report("TTY Unicode #to_s") do
|
32
|
+
table_unicode.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/tty.rb
CHANGED
@@ -10,16 +10,32 @@ require 'tty/support/equatable'
|
|
10
10
|
require 'tty/support/unicode'
|
11
11
|
|
12
12
|
require 'tty/terminal'
|
13
|
-
require 'tty/terminal/color'
|
14
13
|
require 'tty/system'
|
15
14
|
require 'tty/table'
|
15
|
+
require 'tty/text'
|
16
16
|
require 'tty/vector'
|
17
17
|
require 'tty/shell'
|
18
|
+
require 'tty/coercer'
|
19
|
+
|
20
|
+
require 'tty/coercer/range'
|
21
|
+
require 'tty/coercer/integer'
|
22
|
+
require 'tty/coercer/float'
|
23
|
+
require 'tty/coercer/boolean'
|
18
24
|
|
25
|
+
require 'tty/shell/response_delegation'
|
19
26
|
require 'tty/shell/question'
|
20
27
|
require 'tty/shell/question/validation'
|
21
28
|
require 'tty/shell/question/modifier'
|
22
29
|
require 'tty/shell/statement'
|
30
|
+
require 'tty/shell/reader'
|
31
|
+
require 'tty/shell/response'
|
32
|
+
|
33
|
+
require 'tty/terminal/color'
|
34
|
+
require 'tty/terminal/echo'
|
35
|
+
require 'tty/terminal/home'
|
36
|
+
|
37
|
+
require 'tty/text/wrapping'
|
38
|
+
require 'tty/text/truncation'
|
23
39
|
|
24
40
|
require 'tty/table/border'
|
25
41
|
require 'tty/table/border/unicode'
|
@@ -27,6 +43,9 @@ require 'tty/table/border/ascii'
|
|
27
43
|
require 'tty/table/border/null'
|
28
44
|
|
29
45
|
require 'tty/table/column_set'
|
46
|
+
require 'tty/table/orientation'
|
47
|
+
require 'tty/table/orientation/horizontal'
|
48
|
+
require 'tty/table/orientation/vertical'
|
30
49
|
|
31
50
|
require 'tty/table/operation/alignment_set'
|
32
51
|
require 'tty/table/operation/alignment'
|
@@ -47,6 +66,9 @@ module TTY
|
|
47
66
|
# Raised when the argument is not expected
|
48
67
|
class InvalidArgument < ArgumentError; end
|
49
68
|
|
69
|
+
# Raised when the table orientation is unkown
|
70
|
+
class InvalidOrientationError < ArgumentError; end
|
71
|
+
|
50
72
|
# Raised when the passed in validation argument is of wrong type
|
51
73
|
class ValidationCoercion < TypeError; end
|
52
74
|
|
data/lib/tty/coercer.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Coercer
|
5
|
+
|
6
|
+
# A class responsible for boolean type coercion
|
7
|
+
class Boolean
|
8
|
+
|
9
|
+
# Coerce value to boolean type including range of strings such as
|
10
|
+
#
|
11
|
+
# @param [Object] value
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# coerce("True") # => true
|
15
|
+
#
|
16
|
+
# other values coerced to true are:
|
17
|
+
# 1, t, T, TRUE, true, True, y, Y, YES, yes, Yes
|
18
|
+
#
|
19
|
+
# coerce("False") # => false
|
20
|
+
#
|
21
|
+
# other values coerced to false are:
|
22
|
+
# 0, f, F, FALSE, false, False, n, N, No, no, No
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def self.coerce(value)
|
26
|
+
case value.to_s
|
27
|
+
when %r/^(yes|y|t(rue)?|1)$/i
|
28
|
+
return true
|
29
|
+
when %r/^(no|n|f(alse)?|0)$/i
|
30
|
+
return false
|
31
|
+
else
|
32
|
+
raise TypeError, "Expected boolean type, got #{value}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end # Boolean
|
37
|
+
|
38
|
+
end # Coercer
|
39
|
+
end # TTY
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Coercer
|
5
|
+
|
6
|
+
class Float
|
7
|
+
|
8
|
+
def self.coerce(value, strict=true)
|
9
|
+
begin
|
10
|
+
Kernel.send(:Float, value.to_s)
|
11
|
+
rescue
|
12
|
+
if strict
|
13
|
+
raise InvalidArgument, "#{value} could not be coerced into Float"
|
14
|
+
else
|
15
|
+
value.to_f
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end # Float
|
21
|
+
|
22
|
+
end # Coercer
|
23
|
+
end # TTY
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Coercer
|
5
|
+
|
6
|
+
class Integer
|
7
|
+
|
8
|
+
def self.coerce(value, strict=true)
|
9
|
+
begin
|
10
|
+
Kernel.send(:Integer, value.to_s)
|
11
|
+
rescue
|
12
|
+
if strict
|
13
|
+
raise InvalidArgument, "#{value} could not be coerced into Integer"
|
14
|
+
else
|
15
|
+
value.to_i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end # Integer
|
21
|
+
|
22
|
+
end # Coercer
|
23
|
+
end # TTY
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Coercer
|
5
|
+
|
6
|
+
# A class responsible for range type coercion
|
7
|
+
class Range
|
8
|
+
|
9
|
+
# Coerce value to Range type with possible ranges
|
10
|
+
#
|
11
|
+
# @param [Object] value
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# coerce('0-9') # => (0..9)
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def self.coerce(value)
|
18
|
+
case value.to_s
|
19
|
+
when /\A(\-?\d+)\Z/
|
20
|
+
::Range.new($1.to_i, $1.to_i)
|
21
|
+
when /\A(-?\d+?)(\.{2}\.?|-|,)(-?\d+)\Z/
|
22
|
+
::Range.new($1.to_i, $3.to_i, $2 == '...')
|
23
|
+
when /\A(\w)(\.{2}\.?|-|,)(\w)\Z/
|
24
|
+
::Range.new($1.to_s, $3.to_s, $2 == '...')
|
25
|
+
else
|
26
|
+
raise InvalidArgument, "#{value} could not be coerced into Range type"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end # Range
|
31
|
+
|
32
|
+
end # Coercer
|
33
|
+
end # TTY
|
data/lib/tty/shell.rb
CHANGED
@@ -11,12 +11,16 @@ module TTY
|
|
11
11
|
# @api private
|
12
12
|
attr_reader :output
|
13
13
|
|
14
|
+
# @api private
|
15
|
+
attr_reader :prefix
|
16
|
+
|
14
17
|
# Initialize a Shell
|
15
18
|
#
|
16
19
|
# @api public
|
17
|
-
def initialize(input=stdin, output=stdout)
|
20
|
+
def initialize(input=stdin, output=stdout, options={})
|
18
21
|
@input = input
|
19
22
|
@output = output
|
23
|
+
@prefix = options.fetch(:prefix) { '' }
|
20
24
|
end
|
21
25
|
|
22
26
|
# Ask a question.
|
@@ -39,7 +43,7 @@ module TTY
|
|
39
43
|
def ask(statement, *args, &block)
|
40
44
|
options = Utils.extract_options!(args)
|
41
45
|
|
42
|
-
question = Question.new self,
|
46
|
+
question = Question.new self, options
|
43
47
|
question.instance_eval(&block) if block_given?
|
44
48
|
question.prompt(statement)
|
45
49
|
end
|
data/lib/tty/shell/question.rb
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
+
require 'date'
|
4
|
+
|
3
5
|
module TTY
|
6
|
+
# A class responsible for shell prompt interactions.
|
4
7
|
class Shell
|
5
8
|
|
6
9
|
# A class representing a question.
|
7
10
|
class Question
|
11
|
+
include TTY::Shell::ResponseDelegation
|
8
12
|
|
9
13
|
PREFIX = " + "
|
10
14
|
MULTIPLE_PREFIX = " * "
|
11
15
|
ERROR_PREFIX = " ERROR:"
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
# Store question.
|
17
|
+
# Store statement.
|
16
18
|
#
|
17
19
|
# @api private
|
18
|
-
attr_accessor :
|
20
|
+
attr_accessor :statement
|
19
21
|
|
20
22
|
# Store default value.
|
21
23
|
#
|
@@ -27,40 +29,50 @@ module TTY
|
|
27
29
|
|
28
30
|
attr_reader :validation
|
29
31
|
|
30
|
-
# Controls character processing
|
32
|
+
# Controls character processing of the answer
|
31
33
|
#
|
32
34
|
# @api public
|
33
35
|
attr_reader :modifier
|
34
36
|
|
37
|
+
# Returns valid answers
|
38
|
+
#
|
39
|
+
# @api public
|
35
40
|
attr_reader :valid_values
|
36
41
|
|
37
42
|
attr_reader :error
|
38
43
|
|
39
|
-
|
44
|
+
# Returns echo mode
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
attr_reader :echo
|
40
48
|
|
41
|
-
#
|
49
|
+
# Returns character mask
|
42
50
|
#
|
43
|
-
# @api
|
44
|
-
attr_reader :
|
51
|
+
# @api public
|
52
|
+
attr_reader :mask
|
53
|
+
|
54
|
+
# Returns character mode
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
attr_reader :character
|
45
58
|
|
46
59
|
# @api private
|
47
60
|
attr_reader :shell
|
48
61
|
private :shell
|
49
62
|
|
50
|
-
|
51
|
-
@shell = shell || Shell.new
|
52
|
-
@statement = statement
|
53
|
-
@required = options.fetch :required, false
|
54
|
-
@modifier = Modifier.new options.fetch(:modifier, [])
|
55
|
-
@valid_values = options.fetch :valid, []
|
56
|
-
@validation = Validation.new options.fetch(:validation, nil)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Check if required argument present.
|
63
|
+
# Initialize a Question
|
60
64
|
#
|
61
|
-
# @api
|
62
|
-
def
|
63
|
-
|
65
|
+
# @api public
|
66
|
+
def initialize(shell, options={})
|
67
|
+
@shell = shell || Shell.new
|
68
|
+
@required = options.fetch(:required) { false }
|
69
|
+
@echo = options.fetch(:echo) { true }
|
70
|
+
@mask = options.fetch(:mask) { false }
|
71
|
+
@character = options.fetch(:character) { false }
|
72
|
+
@in = options.fetch(:in) { false }
|
73
|
+
@modifier = Modifier.new options.fetch(:modifier) { [] }
|
74
|
+
@valid_values = options.fetch(:valid) { [] }
|
75
|
+
@validation = Validation.new options.fetch(:validation) { nil }
|
64
76
|
end
|
65
77
|
|
66
78
|
# Set a new prompt
|
@@ -70,8 +82,8 @@ module TTY
|
|
70
82
|
# @return [self]
|
71
83
|
#
|
72
84
|
def prompt(message)
|
73
|
-
self.
|
74
|
-
shell.say
|
85
|
+
self.statement = message
|
86
|
+
shell.say shell.prefix + statement
|
75
87
|
self
|
76
88
|
end
|
77
89
|
|
@@ -84,6 +96,11 @@ module TTY
|
|
84
96
|
self
|
85
97
|
end
|
86
98
|
|
99
|
+
# Check if default value is set
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
#
|
103
|
+
# @api public
|
87
104
|
def default?
|
88
105
|
!!@default_value
|
89
106
|
end
|
@@ -103,6 +120,15 @@ module TTY
|
|
103
120
|
self
|
104
121
|
end
|
105
122
|
|
123
|
+
# Check if required argument present.
|
124
|
+
#
|
125
|
+
# @return [Boolean]
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
def required?
|
129
|
+
required
|
130
|
+
end
|
131
|
+
|
106
132
|
# Set validation rule for an argument
|
107
133
|
#
|
108
134
|
# @param [Object] value
|
@@ -115,26 +141,24 @@ module TTY
|
|
115
141
|
self
|
116
142
|
end
|
117
143
|
|
144
|
+
# Set expected values
|
145
|
+
#
|
146
|
+
# @param [Array] values
|
147
|
+
#
|
148
|
+
# @return [self]
|
149
|
+
#
|
118
150
|
# @api public
|
119
|
-
def valid(
|
120
|
-
@valid_values =
|
151
|
+
def valid(values)
|
152
|
+
@valid_values = values
|
121
153
|
self
|
122
154
|
end
|
123
155
|
|
124
|
-
# @api private
|
125
|
-
def check_valid(value)
|
126
|
-
if Array(value).all? { |val| @valid_values.include? val }
|
127
|
-
return value
|
128
|
-
else raise InvalidArgument, "Valid values are: #{@valid_values.join(', ')}"
|
129
|
-
end
|
130
|
-
end
|
131
156
|
|
132
157
|
# Reset question object.
|
133
158
|
#
|
134
159
|
# @api public
|
135
160
|
def clean
|
136
|
-
@
|
137
|
-
@type = nil
|
161
|
+
@statement = nil
|
138
162
|
@default_value = nil
|
139
163
|
@required = false
|
140
164
|
@modifier = nil
|
@@ -150,152 +174,148 @@ module TTY
|
|
150
174
|
self
|
151
175
|
end
|
152
176
|
|
177
|
+
# Setup behaviour when error(s) occur
|
178
|
+
#
|
153
179
|
# @api public
|
154
180
|
def on_error(action=nil)
|
155
181
|
@error = action
|
156
182
|
self
|
157
183
|
end
|
158
184
|
|
159
|
-
#
|
160
|
-
def read(type=nil)
|
161
|
-
result = shell.input.gets
|
162
|
-
if !result && default?
|
163
|
-
return default_value
|
164
|
-
end
|
165
|
-
if required? && !default? && !result
|
166
|
-
raise ArgumentRequired, 'No value provided for required'
|
167
|
-
end
|
168
|
-
validation.valid_value? result
|
169
|
-
modifier.apply_to result
|
170
|
-
end
|
171
|
-
|
172
|
-
# Read answer and cast to String type
|
173
|
-
#
|
174
|
-
# @param [String] error
|
175
|
-
# error to display on failed conversion to string type
|
185
|
+
# Check if error behaviour is set
|
176
186
|
#
|
177
187
|
# @api public
|
178
|
-
def
|
179
|
-
|
188
|
+
def error?
|
189
|
+
!!@error
|
180
190
|
end
|
181
191
|
|
182
|
-
#
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
def read_int(error=nil)
|
193
|
-
Kernel.send(:Integer, read)
|
194
|
-
end
|
195
|
-
|
196
|
-
def read_float(error=nil)
|
197
|
-
Kernel.send(:Float, read)
|
198
|
-
end
|
199
|
-
|
200
|
-
def read_regex(error=nil)
|
201
|
-
Kernel.send(:Regex, read)
|
192
|
+
# Turn terminal echo on or off. This is used to secure the display so
|
193
|
+
# that the entered characters are not echoed back to the screen.
|
194
|
+
#
|
195
|
+
# @api public
|
196
|
+
def echo(value=(not_set=true))
|
197
|
+
return @echo if not_set
|
198
|
+
@echo = value
|
199
|
+
self
|
202
200
|
end
|
203
201
|
|
204
|
-
|
205
|
-
|
202
|
+
# Chec if echo is set
|
203
|
+
#
|
204
|
+
# @api public
|
205
|
+
def echo?
|
206
|
+
!!@echo
|
206
207
|
end
|
207
208
|
|
208
|
-
|
209
|
-
|
209
|
+
# Set character for masking the STDIN input
|
210
|
+
#
|
211
|
+
# @param [String] character
|
212
|
+
#
|
213
|
+
# @return [self]
|
214
|
+
#
|
215
|
+
# @api public
|
216
|
+
def mask(character=(not_set=true))
|
217
|
+
return @mask if not_set
|
218
|
+
@mask = character
|
219
|
+
self
|
210
220
|
end
|
211
221
|
|
212
|
-
|
213
|
-
|
222
|
+
# Check if character mask is set
|
223
|
+
#
|
224
|
+
# @return [Boolean]
|
225
|
+
#
|
226
|
+
# @api public
|
227
|
+
def mask?
|
228
|
+
!!@mask
|
214
229
|
end
|
215
230
|
|
216
|
-
|
217
|
-
|
218
|
-
|
231
|
+
# Set if the input is character based or not
|
232
|
+
#
|
233
|
+
# @param [Boolean] value
|
234
|
+
#
|
235
|
+
# @return [self]
|
236
|
+
#
|
237
|
+
# @api public
|
238
|
+
def character(value=(not_set=true))
|
239
|
+
return @character if not_set
|
240
|
+
@character = value
|
241
|
+
self
|
219
242
|
end
|
220
243
|
|
221
|
-
|
222
|
-
|
244
|
+
# Check if character intput is set
|
245
|
+
#
|
246
|
+
# @return [Boolean]
|
247
|
+
#
|
248
|
+
# @api public
|
249
|
+
def character?
|
250
|
+
!!@character
|
223
251
|
end
|
224
252
|
|
225
|
-
#
|
253
|
+
# Set expect range of values
|
226
254
|
#
|
227
|
-
# @
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
255
|
+
# @param [String] value
|
256
|
+
#
|
257
|
+
# @api public
|
258
|
+
def in(value=(not_set=true))
|
259
|
+
return @in if not_set
|
260
|
+
@in = TTY::Coercer::Range.coerce value
|
261
|
+
self
|
232
262
|
end
|
233
263
|
|
234
|
-
#
|
264
|
+
# Check if range is set
|
235
265
|
#
|
236
|
-
# @return [
|
266
|
+
# @return [Boolean]
|
237
267
|
#
|
238
268
|
# @api public
|
239
|
-
def
|
240
|
-
|
241
|
-
if error
|
242
|
-
self.prompt statement
|
243
|
-
with_exception { read_string }
|
244
|
-
else
|
245
|
-
read_string
|
246
|
-
end
|
269
|
+
def in?
|
270
|
+
!!@in
|
247
271
|
end
|
248
272
|
|
249
|
-
#
|
273
|
+
# Check if response matches all the requirements set by the question
|
250
274
|
#
|
251
|
-
# @
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
275
|
+
# @param [Object] value
|
276
|
+
#
|
277
|
+
# @return [Object]
|
278
|
+
#
|
279
|
+
# @api private
|
280
|
+
def evaluate_response(value)
|
281
|
+
return default_value if !value && default?
|
282
|
+
|
283
|
+
check_required value
|
284
|
+
check_valid value unless valid_values.empty?
|
285
|
+
within? value
|
286
|
+
validation.valid_value? value
|
287
|
+
modifier.apply_to value
|
260
288
|
end
|
261
289
|
|
262
|
-
|
290
|
+
private
|
263
291
|
|
264
|
-
#
|
265
|
-
# :boolean, :string, :numeric, :array
|
292
|
+
# Check if value is present
|
266
293
|
#
|
267
294
|
# @api private
|
268
|
-
def
|
269
|
-
|
270
|
-
|
271
|
-
when :string
|
272
|
-
read_string
|
273
|
-
when :symbol
|
274
|
-
read_symbol
|
275
|
-
when :float
|
276
|
-
read_float
|
295
|
+
def check_required(value)
|
296
|
+
if required? && !default? && !value
|
297
|
+
raise ArgumentRequired, 'No value provided for required'
|
277
298
|
end
|
278
299
|
end
|
279
300
|
|
280
|
-
|
281
|
-
|
301
|
+
# Check if value matches any of the expected values
|
302
|
+
#
|
303
|
+
# @api private
|
304
|
+
def check_valid(value)
|
305
|
+
if Array(value).all? { |val| valid_values.include? val }
|
306
|
+
return value
|
307
|
+
else raise InvalidArgument, "Valid values are: #{valid_values.join(', ')}"
|
308
|
+
end
|
282
309
|
end
|
283
310
|
|
284
|
-
#
|
285
|
-
#
|
286
|
-
# @param [String] message
|
287
|
-
#
|
288
|
-
# @return [Boolean]
|
311
|
+
# Check if value is within expected range
|
289
312
|
#
|
290
313
|
# @api private
|
291
|
-
def
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
return false
|
297
|
-
else
|
298
|
-
raise TypeError, "Expected boolean type, got #{message}"
|
314
|
+
def within?(value)
|
315
|
+
if in? && value
|
316
|
+
if @in.include?(value)
|
317
|
+
else raise InvalidArgument, "Value #{value} is not included in the range #{@in}"
|
318
|
+
end
|
299
319
|
end
|
300
320
|
end
|
301
321
|
|