tty 0.0.5 → 0.0.6
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 +60 -5
- data/lib/tty/shell/question/modifier.rb +96 -0
- data/lib/tty/shell/question/validation.rb +91 -0
- data/lib/tty/shell/question.rb +304 -0
- data/lib/tty/shell/statement.rb +55 -0
- data/lib/tty/shell.rb +159 -0
- data/lib/tty/table/operation/wrapped.rb +6 -0
- data/lib/tty/terminal/color.rb +143 -0
- data/lib/tty/terminal.rb +46 -21
- data/lib/tty/version.rb +1 -1
- data/lib/tty.rb +19 -1
- data/spec/tty/shell/ask_spec.rb +65 -0
- data/spec/tty/shell/error_spec.rb +28 -0
- data/spec/tty/shell/print_table_spec.rb +25 -0
- data/spec/tty/shell/question/initialize_spec.rb +227 -0
- data/spec/tty/shell/question/modifier/apply_to_spec.rb +30 -0
- data/spec/tty/shell/question/modifier/letter_case_spec.rb +27 -0
- data/spec/tty/shell/question/modifier/whitespace_spec.rb +33 -0
- data/spec/tty/shell/question/validation/coerce_spec.rb +25 -0
- data/spec/tty/shell/question/validation/valid_value_spec.rb +28 -0
- data/spec/tty/shell/say_spec.rb +64 -0
- data/spec/tty/shell/statement/initialize_spec.rb +15 -0
- data/spec/tty/shell/warn_spec.rb +28 -0
- data/spec/tty/table/renderer_spec.rb +0 -1
- data/spec/tty/terminal/color/code_spec.rb +19 -0
- data/spec/tty/terminal/color/remove_spec.rb +12 -0
- data/spec/tty/terminal/color/set_spec.rb +30 -0
- data/spec/tty/terminal/color_spec.rb +15 -0
- data/spec/tty/terminal/home_spec.rb +37 -0
- data/tasks/metrics/reek.rake +1 -3
- metadata +48 -11
- data/lib/tty/color.rb +0 -14
- data/spec/tty/color_spec.rb +0 -5
data/README.md
CHANGED
@@ -4,18 +4,20 @@
|
|
4
4
|
[travis]: http://travis-ci.org/peter-murach/tty
|
5
5
|
[codeclimate]: https://codeclimate.com/github/peter-murach/tty
|
6
6
|
|
7
|
-
Toolbox for developing CLI clients in Ruby.
|
7
|
+
Toolbox for developing CLI clients in Ruby. This library provides a fluid interface for working with terminals.
|
8
8
|
|
9
9
|
## Features
|
10
10
|
|
11
11
|
Jump-start development of your command line app:
|
12
12
|
|
13
13
|
* Fully customizable table rendering with an easy-to-use API. (status: In Progress)
|
14
|
-
* Terminal output colorization.
|
14
|
+
* Terminal output colorization. (status: DONE)
|
15
15
|
* Terminal & System detection utilities. (status: In Progress)
|
16
|
-
* Text alignment/padding
|
17
|
-
* Shell user interface.
|
18
|
-
*
|
16
|
+
* Text alignment/padding/indentation. (status: In Progress)
|
17
|
+
* Shell user interface. (status: In Progress)
|
18
|
+
* File diffs. (status: TODO)
|
19
|
+
* Progress bar. (status: TODO)
|
20
|
+
* Configuration file management. (status: TODO)
|
19
21
|
* Fully tested with major ruby interpreters.
|
20
22
|
* No dependencies to allow for easy gem vendoring.
|
21
23
|
|
@@ -113,10 +115,63 @@ To print border around data table you need to specify `renderer` type out of `ba
|
|
113
115
|
term.color? # => true or false
|
114
116
|
```
|
115
117
|
|
118
|
+
To colorize your output do
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
term.color.set 'text...', :bold, :red, :on_green # => red bold text on green background
|
122
|
+
term.color.remove 'text...' # strips off ansi escape sequences
|
123
|
+
term.color.code :red # ansi escape code for the supplied color
|
124
|
+
```
|
125
|
+
|
116
126
|
### Shell
|
117
127
|
|
118
128
|
Main responsibility is to interact with the prompt and provide convenience methods.
|
119
129
|
|
130
|
+
In order to ask question and parse answers:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
shell = TTY::Shell.new
|
134
|
+
answer = shell.ask("What is your name?").read_string
|
135
|
+
```
|
136
|
+
|
137
|
+
The library provides small DSL to help with parsing and asking precise questions
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
default # default value used if none is provided
|
141
|
+
argument # :required or :optional
|
142
|
+
validate # regex against which stdin input is checked
|
143
|
+
valid # a list of expected valid options
|
144
|
+
modify # apply answer modification :upcase, :downcase, :trim, :chomp etc..
|
145
|
+
clean # reset question
|
146
|
+
```
|
147
|
+
|
148
|
+
You can chain question methods or configure them inside a block
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
shell.ask("What is your name?").argument(:required).default('Piotr').validate(/\w+\s\w+/).read_string
|
152
|
+
|
153
|
+
shell.ask "What is your name?" do
|
154
|
+
argument :required
|
155
|
+
default 'Piotr'
|
156
|
+
validate /\w+\s\w+/
|
157
|
+
valid ['Piotr', 'Piotrek']
|
158
|
+
modify :capitalize
|
159
|
+
end.read_string
|
160
|
+
```
|
161
|
+
|
162
|
+
Reading answers and converting them into required types can be done with custom readers
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
read_string # return string
|
166
|
+
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
|
+
read_date # return date type
|
170
|
+
read_datetime # return datetime type
|
171
|
+
read_multiple # return multiple line string
|
172
|
+
read_email # validate answer against email regex
|
173
|
+
```
|
174
|
+
|
120
175
|
### System
|
121
176
|
|
122
177
|
```ruby
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Shell
|
5
|
+
class Question
|
6
|
+
|
7
|
+
# A class representing String modifications.
|
8
|
+
class Modifier
|
9
|
+
|
10
|
+
attr_reader :modifiers
|
11
|
+
private :modifiers
|
12
|
+
|
13
|
+
# Initialize a Modifier
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
def initialize(*modifiers)
|
17
|
+
@modifiers = Array(modifiers)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Change supplied value according to the given string transformation.
|
21
|
+
# Valid settings are:
|
22
|
+
#
|
23
|
+
# @param [String] value
|
24
|
+
# the string to be modified
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def apply_to(value)
|
30
|
+
modifiers.inject(value) do |result, mod|
|
31
|
+
result = Modifier.letter_case mod, result
|
32
|
+
result = Modifier.whitespace mod, result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Changes letter casing in a string according to valid modifications.
|
37
|
+
# For invalid modification option the string is preserved.
|
38
|
+
#
|
39
|
+
# @param [Symbol] mod
|
40
|
+
# the modification to change the string
|
41
|
+
#
|
42
|
+
# @option mod [Symbol] :up change to upper case
|
43
|
+
# @option mod [Symbol] :upcase change to upper case
|
44
|
+
# @option mod [Symbol] :uppercase change to upper case
|
45
|
+
# @option mod [Symbol] :down change to lower case
|
46
|
+
# @option mod [Symbol] :downcase change to lower case
|
47
|
+
# @option mod [Symbol] :capitalize change all words to start with uppercase case letter
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def self.letter_case(mod, value)
|
53
|
+
case mod
|
54
|
+
when :up, :upcase, :uppercase
|
55
|
+
value.upcase
|
56
|
+
when :down, :downcase, :lowercase
|
57
|
+
value.downcase
|
58
|
+
when :capitalize
|
59
|
+
value.capitalize
|
60
|
+
else
|
61
|
+
value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Changes whitespace in a string according to valid modifications.
|
66
|
+
#
|
67
|
+
# @param [Symbol] mod
|
68
|
+
# the modification to change the string
|
69
|
+
#
|
70
|
+
# @option mod [String] :trim, :strip
|
71
|
+
# remove whitespace for the start and end
|
72
|
+
# @option mod [String] :chomp remove record separator from the end
|
73
|
+
# @option mod [String] :collapse remove any duplicate whitespace
|
74
|
+
# @option mod [String] :remove remove all whitespace
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def self.whitespace(mod, value)
|
78
|
+
case mod
|
79
|
+
when :trim, :strip
|
80
|
+
value.strip
|
81
|
+
when :chomp
|
82
|
+
value.chomp
|
83
|
+
when :collapse
|
84
|
+
value.gsub(/\s+/, ' ')
|
85
|
+
when :remove
|
86
|
+
value.gsub(/\s+/, '')
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end # Modifier
|
93
|
+
|
94
|
+
end # Question
|
95
|
+
end # Shell
|
96
|
+
end # TTY
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Shell
|
5
|
+
class Question
|
6
|
+
|
7
|
+
# A class representing question validation.
|
8
|
+
class Validation
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
attr_reader :validation
|
12
|
+
private :validation
|
13
|
+
|
14
|
+
# Initialize a Validation
|
15
|
+
#
|
16
|
+
# @param [Object] validation
|
17
|
+
#
|
18
|
+
# @return [undefined]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
def initialize(validation=nil)
|
22
|
+
@validation = validation ? coerce(validation) : validation
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert validation into known type.
|
26
|
+
#
|
27
|
+
# @param [Object] validation
|
28
|
+
#
|
29
|
+
# @raise [TTY::ValidationCoercion] failed to convert validation
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def coerce(validation)
|
33
|
+
case validation
|
34
|
+
when Proc
|
35
|
+
validation
|
36
|
+
when Regexp, String
|
37
|
+
Regexp.new(validation.to_s)
|
38
|
+
else
|
39
|
+
raise TTY::ValidationCoercion, "Wrong type, got #{validation.class}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check if validation is required
|
44
|
+
#
|
45
|
+
# @return [Boolean]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def validate?
|
49
|
+
!!validation
|
50
|
+
end
|
51
|
+
|
52
|
+
# Test if the value matches the validation
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# validation.valid_value?(value) # => true or false
|
56
|
+
#
|
57
|
+
# @param [Object] value
|
58
|
+
# the value to validate
|
59
|
+
#
|
60
|
+
# @return [undefined]
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
def valid_value?(value)
|
64
|
+
check_validation(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Check if provided value passes validation
|
70
|
+
#
|
71
|
+
# @param [String] value
|
72
|
+
#
|
73
|
+
# @raise [TTY::InvalidArgument] unkown type of argument
|
74
|
+
#
|
75
|
+
# @return [undefined]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def check_validation(value)
|
79
|
+
if validate? && value
|
80
|
+
value = value.to_s
|
81
|
+
if validation.is_a?(Regexp) && validation =~ value
|
82
|
+
elsif validation.is_a?(Proc) && validation.call(value)
|
83
|
+
else raise TTY::InvalidArgument, "Invalid input for #{value}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end # Validation
|
89
|
+
end # Question
|
90
|
+
end # Shell
|
91
|
+
end # TTY
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Shell
|
5
|
+
|
6
|
+
# A class representing a question.
|
7
|
+
class Question
|
8
|
+
|
9
|
+
PREFIX = " + "
|
10
|
+
MULTIPLE_PREFIX = " * "
|
11
|
+
ERROR_PREFIX = " ERROR:"
|
12
|
+
|
13
|
+
VALID_TYPES = [:boolean, :string, :symbol, :integer, :float, :date, :datetime]
|
14
|
+
|
15
|
+
# Store question.
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
attr_accessor :question
|
19
|
+
|
20
|
+
# Store default value.
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
attr_reader :default_value
|
24
|
+
|
25
|
+
attr_reader :required
|
26
|
+
private :required
|
27
|
+
|
28
|
+
attr_reader :validation
|
29
|
+
|
30
|
+
# Controls character processing to the answer
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
attr_reader :modifier
|
34
|
+
|
35
|
+
attr_reader :valid_values
|
36
|
+
|
37
|
+
attr_reader :error
|
38
|
+
|
39
|
+
attr_reader :statement
|
40
|
+
|
41
|
+
# Expected answer type
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
attr_reader :type
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
attr_reader :shell
|
48
|
+
private :shell
|
49
|
+
|
50
|
+
def initialize(shell, statement, options={})
|
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.
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def required?
|
63
|
+
required
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set a new prompt
|
67
|
+
#
|
68
|
+
# @param [String] message
|
69
|
+
#
|
70
|
+
# @return [self]
|
71
|
+
#
|
72
|
+
def prompt(message)
|
73
|
+
self.question = message
|
74
|
+
shell.say question
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set default value.
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def default(value)
|
82
|
+
return self if value == ""
|
83
|
+
@default_value = value
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def default?
|
88
|
+
!!@default_value
|
89
|
+
end
|
90
|
+
|
91
|
+
# Ensure that passed argument is present if required option
|
92
|
+
#
|
93
|
+
# @return [Question]
|
94
|
+
#
|
95
|
+
# @api public
|
96
|
+
def argument(value)
|
97
|
+
case value
|
98
|
+
when :required
|
99
|
+
@required = true
|
100
|
+
when :optional
|
101
|
+
@required = false
|
102
|
+
end
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Set validation rule for an argument
|
107
|
+
#
|
108
|
+
# @param [Object] value
|
109
|
+
#
|
110
|
+
# @return [Question]
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
def validate(value=nil, &block)
|
114
|
+
@validation = Validation.new(value || block)
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
# @api public
|
119
|
+
def valid(value)
|
120
|
+
@valid_values = value
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
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
|
+
|
132
|
+
# Reset question object.
|
133
|
+
#
|
134
|
+
# @api public
|
135
|
+
def clean
|
136
|
+
@question = nil
|
137
|
+
@type = nil
|
138
|
+
@default_value = nil
|
139
|
+
@required = false
|
140
|
+
@modifier = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# Modify string according to the rule given.
|
144
|
+
#
|
145
|
+
# @param [Symbol] rule
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
def modify(*rules)
|
149
|
+
@modifier = Modifier.new *rules
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# @api public
|
154
|
+
def on_error(action=nil)
|
155
|
+
@error = action
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# @api private
|
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
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def read_string(error=nil)
|
179
|
+
String(read)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Read multiple line answer and cast to String type
|
183
|
+
def read_text
|
184
|
+
String(read)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Read ansewr and cast to Symbol type
|
188
|
+
def read_symbol(error=nil)
|
189
|
+
read.to_sym
|
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)
|
202
|
+
end
|
203
|
+
|
204
|
+
def read_date
|
205
|
+
Date.parse(read)
|
206
|
+
end
|
207
|
+
|
208
|
+
def read_datetime
|
209
|
+
DateTime.parse(read)
|
210
|
+
end
|
211
|
+
|
212
|
+
def read_bool(error=nil)
|
213
|
+
parse_boolean read
|
214
|
+
end
|
215
|
+
|
216
|
+
def read_choice(type=nil)
|
217
|
+
@required = true unless default?
|
218
|
+
check_valid read
|
219
|
+
end
|
220
|
+
|
221
|
+
def read_file(error=nil)
|
222
|
+
File.open(File.join(directory, read))
|
223
|
+
end
|
224
|
+
|
225
|
+
# Ignore exception
|
226
|
+
#
|
227
|
+
# @api private
|
228
|
+
def with_exception(&block)
|
229
|
+
yield
|
230
|
+
rescue
|
231
|
+
block.call
|
232
|
+
end
|
233
|
+
|
234
|
+
# Reads string answer and validates against email regex
|
235
|
+
#
|
236
|
+
# @return [String]
|
237
|
+
#
|
238
|
+
# @api public
|
239
|
+
def read_email
|
240
|
+
validate(/^[a-z0-9._%+-]+@([a-z0-9-]+\.)+[a-z]{2,6}$/i)
|
241
|
+
if error
|
242
|
+
self.prompt statement
|
243
|
+
with_exception { read_string }
|
244
|
+
else
|
245
|
+
read_string
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Read answer provided on multiple lines
|
250
|
+
#
|
251
|
+
# @api public
|
252
|
+
def read_multiple
|
253
|
+
response = ""
|
254
|
+
loop do
|
255
|
+
value = read
|
256
|
+
break if !value || value == ""
|
257
|
+
response << value
|
258
|
+
end
|
259
|
+
response
|
260
|
+
end
|
261
|
+
|
262
|
+
protected
|
263
|
+
|
264
|
+
# @param [Symbol] type
|
265
|
+
# :boolean, :string, :numeric, :array
|
266
|
+
#
|
267
|
+
# @api private
|
268
|
+
def read_type(type)
|
269
|
+
raise TypeError, "Type #{type} is not valid" if type && !valid_type?(type)
|
270
|
+
case type
|
271
|
+
when :string
|
272
|
+
read_string
|
273
|
+
when :symbol
|
274
|
+
read_symbol
|
275
|
+
when :float
|
276
|
+
read_float
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def valid_type?(type)
|
281
|
+
self.class::VALID_TYPES.include? type.to_sym
|
282
|
+
end
|
283
|
+
|
284
|
+
# Convert message into boolean type
|
285
|
+
#
|
286
|
+
# @param [String] message
|
287
|
+
#
|
288
|
+
# @return [Boolean]
|
289
|
+
#
|
290
|
+
# @api private
|
291
|
+
def parse_boolean(message)
|
292
|
+
case message.to_s
|
293
|
+
when %r/^(yes|y)$/i
|
294
|
+
return true
|
295
|
+
when %r/^(no|n)$/i
|
296
|
+
return false
|
297
|
+
else
|
298
|
+
raise TypeError, "Expected boolean type, got #{message}"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
end # Question
|
303
|
+
end # Shell
|
304
|
+
end # TTY
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Shell
|
5
|
+
|
6
|
+
# A class representing a statement output to shell.
|
7
|
+
class Statement
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
attr_reader :shell
|
11
|
+
private :shell
|
12
|
+
|
13
|
+
attr_reader :newline
|
14
|
+
|
15
|
+
attr_reader :color
|
16
|
+
|
17
|
+
# Initialize a Statement
|
18
|
+
#
|
19
|
+
# @param [TTY::Shell] shell
|
20
|
+
#
|
21
|
+
# @param [Hash] options
|
22
|
+
#
|
23
|
+
# @option options [Symbol] :newline
|
24
|
+
# force a newline break after the message
|
25
|
+
#
|
26
|
+
# @option options [Symbol] :color
|
27
|
+
# change the message display to color
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def initialize(shell=nil, options={})
|
31
|
+
@shell = shell || Shell.new
|
32
|
+
@newline = options.fetch :newline, true
|
33
|
+
@color = options.fetch :color, nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Output the message to the shell
|
37
|
+
#
|
38
|
+
# @param [String] message
|
39
|
+
# the message to be printed to stdout
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
def declare(message)
|
43
|
+
message = TTY::terminal.color.set message, *color if color
|
44
|
+
|
45
|
+
if newline && /( |\t)(\e\[\d+(;\d+)*m)?\Z/ !~ message
|
46
|
+
shell.output.puts message
|
47
|
+
else
|
48
|
+
shell.output.print message
|
49
|
+
shell.output.flush
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # Statement
|
54
|
+
end # Shell
|
55
|
+
end # TTY
|