tty 0.0.6 → 0.0.7
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/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
|
|