command_line_reporter 2.1 → 3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +29 -7
- data/examples/nested.rb +2 -2
- data/examples/progress.rb +10 -8
- data/examples/quiet.rb +1 -1
- data/examples/simple.rb +2 -1
- data/examples/table.rb +38 -16
- data/lib/column.rb +52 -35
- data/lib/command_line_reporter.rb +39 -25
- data/lib/nested_formatter.rb +20 -5
- data/lib/progress_formatter.rb +17 -2
- data/lib/row.rb +64 -46
- data/lib/table.rb +42 -28
- data/lib/version.rb +1 -1
- data/spec/column_spec.rb +342 -49
- data/spec/command_line_reporter_spec.rb +104 -3
- data/spec/nested_formatter_spec.rb +44 -3
- data/spec/progress_formatter_spec.rb +26 -0
- data/spec/row_spec.rb +47 -10
- data/spec/table_spec.rb +73 -19
- metadata +14 -3
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
## Command Line Reporter
|
2
2
|
|
3
|
-
This gem provides a DSL that makes it easy to write reports of various types in ruby. It
|
3
|
+
This gem provides a DSL that makes it easy to write reports of various types in ruby. It eliminates
|
4
4
|
the need to litter your source with *puts* statements instead providing a more readable, expressive
|
5
5
|
interface to your application. Some of the best features include:
|
6
6
|
|
@@ -9,12 +9,15 @@ interface to your application. Some of the best features include:
|
|
9
9
|
* Easily created headers and footers for your report
|
10
10
|
* Output suppression that makes it easy for your script to support a _quiet_ flag
|
11
11
|
|
12
|
+
The latest release also supports colors allowing you to distinguish data in new ways including bold
|
13
|
+
if your terminal supports it.
|
14
|
+
|
12
15
|
### Installation
|
13
16
|
|
14
|
-
It is up on rubygems.org so add it to your bundle
|
17
|
+
It is up on rubygems.org so add it to your bundle in the Gemfile
|
15
18
|
|
16
19
|
```bash
|
17
|
-
gem 'command_line_reporter', '>=
|
20
|
+
gem 'command_line_reporter', '>=3.0'
|
18
21
|
```
|
19
22
|
|
20
23
|
or do it the old fashioned way:
|
@@ -55,6 +58,8 @@ There are several methods the mixin provides that do not depend on the formatter
|
|
55
58
|
Either true|false. _Default: false_
|
56
59
|
* _:rule_ - true|false indicates whether to include a horizontal rule below|above the
|
57
60
|
header|footer. _Default: false_
|
61
|
+
* _:color_ - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
62
|
+
* _:bold_ - true|false to boldface the font
|
58
63
|
* _report(hash) {block}_
|
59
64
|
* The first argument is a hash that defines the options for the method. See the details in the
|
60
65
|
formatter section for allowed values.
|
@@ -69,34 +74,51 @@ There are several methods the mixin provides that do not depend on the formatter
|
|
69
74
|
* _horizontal_rule(hash)_
|
70
75
|
* _:char_ - The character used to build the rule. _Default: '-'_
|
71
76
|
* _:width_ - The width in characters of the rule. _Default: 100_
|
77
|
+
* _:color_ - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
78
|
+
* _:bold_ - true|false to boldface the font
|
72
79
|
* _vertical_spacing(int)_
|
73
80
|
* Number of blank lines to output. _Default: 1_
|
74
81
|
* _datetime(hash)_
|
75
82
|
* _:align_ - 'left'|'center'|'right' alignment of the timestamp. _Default: 'left'_
|
76
83
|
* _:width_ - The width of the string in characters. _Default: 100_
|
77
84
|
* _:format_ - Any allowed format from #strftime#. _Default: %Y-%m-%d %H:%I:%S%p_
|
85
|
+
* _:color_ - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
86
|
+
* _:bold_ - true|false to boldface the font
|
78
87
|
* _aligned(string, hash)_
|
79
88
|
* _text_ - String to display
|
80
89
|
* _:align_ - 'left'|'right'|'center' align the string text. _Default: 'left'_
|
81
90
|
* _:width_ - The width in characters of the string text. _Default: 100_
|
91
|
+
* _:color_ - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
92
|
+
* _:bold_ - true|false to boldface the font
|
82
93
|
* _table(hash) {block}_
|
83
94
|
* The first argument is a hash that defines properties of the table.
|
84
95
|
* _:border_ - true|false indicates whether to include borders around the table cells
|
85
96
|
* The second argument is a block which includes calls the to the _row_ method
|
86
97
|
* _row {block}_
|
87
|
-
*
|
98
|
+
* _:header_ - Set to true to indicate if this is a header row in the table.
|
99
|
+
* _:color_ - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
100
|
+
* _:bold_ - true|false to boldface the font
|
88
101
|
* _column(string, hash)_
|
89
102
|
* _text_ - String to display in the table cell
|
90
103
|
* _options_ - The options to define the column
|
91
104
|
* :width - defines the width of the column
|
92
105
|
* :padding - The number of spaces to put on both the left and right of the text.
|
93
106
|
* :align - Allowed values are left|right|center
|
107
|
+
* :color - The color to use for the terminal output i.e. 'red' or 'blue' or 'green'
|
108
|
+
* :bold - true|false to boldface the font
|
94
109
|
|
95
110
|
### To Do
|
96
111
|
|
97
|
-
*
|
98
|
-
*
|
99
|
-
|
112
|
+
* Refactor the table structure to use a formatter that produces the current ascii output
|
113
|
+
* After the formatter is added to a table then create one for html output
|
114
|
+
* Add the ability for a column to span across others in a table
|
115
|
+
|
116
|
+
### Contributors
|
117
|
+
|
118
|
+
* Thanks to [Mike Gunderloy](https://github.com/ffmike) for suggesting the need for suppressing
|
119
|
+
output and putting together a fantastic pull request and discussion
|
120
|
+
* Thanks to [Jason Rogers](https://github.com/jacaetevha) and [Peter
|
121
|
+
Suschlik](https://github.com/splattael) for their contributions as well on items I missed
|
100
122
|
|
101
123
|
### License
|
102
124
|
|
data/examples/nested.rb
CHANGED
data/examples/progress.rb
CHANGED
@@ -16,12 +16,14 @@ class Example
|
|
16
16
|
10.times do
|
17
17
|
x += 1
|
18
18
|
sleep 0.1
|
19
|
-
formatter.progress
|
19
|
+
# formatter.progress
|
20
|
+
progress
|
20
21
|
|
21
22
|
10.times do
|
22
23
|
x += 1
|
23
24
|
sleep 0.1
|
24
|
-
formatter.progress
|
25
|
+
# formatter.progress
|
26
|
+
progress
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
@@ -32,24 +34,24 @@ class Example
|
|
32
34
|
10.times do
|
33
35
|
y += 1
|
34
36
|
sleep 0.1
|
35
|
-
|
37
|
+
progress("#{y*10}%")
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
41
|
report do
|
40
42
|
3.times do
|
41
|
-
|
43
|
+
progress("\\")
|
42
44
|
sleep 0.1
|
43
|
-
|
45
|
+
progress("/")
|
44
46
|
sleep 0.1
|
45
|
-
|
47
|
+
progress("-")
|
46
48
|
sleep 0.1
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
report do
|
52
|
+
report(:color => 'red') do
|
51
53
|
100.times do
|
52
|
-
|
54
|
+
progress(erase_chars + NYAN_CHARS)
|
53
55
|
sleep 0.1
|
54
56
|
end
|
55
57
|
end
|
data/examples/quiet.rb
CHANGED
data/examples/simple.rb
CHANGED
data/examples/table.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# recoil <co>mmand_<li>ne_<re>porter
|
1
2
|
require 'command_line_reporter'
|
2
3
|
|
3
4
|
class Example
|
@@ -7,40 +8,61 @@ class Example
|
|
7
8
|
header(:title => 'TABLE EXAMPLES - Borders, Wrapping, Alignment and Padding', :align => 'center', :width => 70)
|
8
9
|
|
9
10
|
2.times do |j|
|
10
|
-
header
|
11
|
+
header :title => "Table #{j}", :align => 'center', :width => 65
|
11
12
|
|
12
|
-
table
|
13
|
+
table :border => j % 2 == 0 do
|
13
14
|
3.times do
|
14
15
|
row do
|
15
16
|
i = 0
|
16
17
|
3.times do
|
17
18
|
i += 10
|
18
|
-
column
|
19
|
+
column 'x' * (0 + rand(50)), :align => %w[left right center][rand(3)], :width => i, :padding => rand(5)
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
vertical_spacing
|
25
|
+
vertical_spacing 2
|
25
26
|
end
|
26
27
|
|
27
|
-
header
|
28
|
+
header :title => 'An example of a table with a header row. The color and border properties are not inherited'
|
28
29
|
|
29
|
-
table
|
30
|
-
row do
|
31
|
-
column
|
32
|
-
column
|
33
|
-
column
|
30
|
+
table :border => true do
|
31
|
+
row :header => true, :color => 'red' do
|
32
|
+
column 'MY NAME IS REALLY LONG AND WILL WRAP AND HOPE', :width => 20, :align => 'center', :color => 'blue'
|
33
|
+
column 'ADDRESS', :width => 30, :padding => 5
|
34
|
+
column 'CITY', :width => 15
|
35
|
+
end
|
36
|
+
row :color => 'green', :bold => true do
|
37
|
+
column 'Ceaser'
|
38
|
+
column '1 Appian Way'
|
39
|
+
column 'Rome'
|
34
40
|
end
|
35
41
|
row do
|
36
|
-
column
|
37
|
-
column
|
38
|
-
column
|
42
|
+
column 'Richard Feynman'
|
43
|
+
column '1 Golden Gate'
|
44
|
+
column 'Quantum Field'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
vertical_spacing 2
|
49
|
+
header :title => 'The same table with the properties inherited from the first row'
|
50
|
+
|
51
|
+
table :border => true do
|
52
|
+
row :color => 'red' do
|
53
|
+
column 'MY NAME IS REALLY LONG AND WILL WRAP AND HOPE', :width => 20, :align => 'center', :color => 'blue'
|
54
|
+
column 'ADDRESS', :width => 30, :padding => 5
|
55
|
+
column 'CITY', :width => 15
|
56
|
+
end
|
57
|
+
row :color => 'green', :bold => true do
|
58
|
+
column 'Ceaser'
|
59
|
+
column '1 Appian Way'
|
60
|
+
column 'Rome'
|
39
61
|
end
|
40
62
|
row do
|
41
|
-
column
|
42
|
-
column
|
43
|
-
column
|
63
|
+
column 'Richard Feynman'
|
64
|
+
column '1 Golden Gate'
|
65
|
+
column 'Quantum Field'
|
44
66
|
end
|
45
67
|
end
|
46
68
|
end
|
data/lib/column.rb
CHANGED
@@ -1,50 +1,67 @@
|
|
1
1
|
require 'options_validator'
|
2
|
+
require 'colored'
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
module CommandLineReporter
|
5
|
+
class Column
|
6
|
+
include OptionsValidator
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
+
VALID_OPTIONS = [:width, :padding, :align, :color, :bold, :underline, :reversed]
|
9
|
+
attr_accessor :text, :size, *VALID_OPTIONS
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
def initialize(text = nil, options = {})
|
12
|
+
self.validate_options(options, *VALID_OPTIONS)
|
11
13
|
|
12
|
-
|
14
|
+
self.text = text
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
self.width = options[:width] || 10
|
17
|
+
self.align = options[:align] || 'left'
|
18
|
+
self.padding = options[:padding] || 0
|
19
|
+
self.color = options[:color] || nil
|
20
|
+
self.bold = options[:bold] || false
|
21
|
+
self.underline = options[:underline] || false
|
22
|
+
self.reversed = options[:reversed] || false
|
17
23
|
|
18
|
-
|
19
|
-
|
24
|
+
raise ArgumentError unless self.width > 0
|
25
|
+
raise ArgumentError unless self.padding.to_s.match(/^\d+$/)
|
20
26
|
|
21
|
-
|
22
|
-
|
27
|
+
self.size = self.width - 2 * self.padding
|
28
|
+
end
|
23
29
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
def screen_rows
|
31
|
+
if self.text.nil? || self.text.empty?
|
32
|
+
[' ' * self.width]
|
33
|
+
else
|
34
|
+
self.text.scan(/.{1,#{self.size}}/m).map {|s| to_cell(s)}
|
35
|
+
end
|
29
36
|
end
|
30
|
-
end
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
private
|
39
|
+
|
40
|
+
def to_cell(str)
|
41
|
+
# NOTE: For making underline and reversed work Change so that based on the
|
42
|
+
# unformatted text it determines how much spacing to add left and right
|
43
|
+
# then colorize the cell text
|
44
|
+
cell = if str.empty?
|
45
|
+
' ' * self.size
|
46
|
+
else
|
47
|
+
case self.align
|
48
|
+
when 'left'
|
49
|
+
str.ljust(self.size)
|
50
|
+
when 'right'
|
51
|
+
str.rjust(self.size)
|
52
|
+
when 'center'
|
53
|
+
str.ljust((self.size - str.size)/2.0 + str.size).rjust(self.size)
|
54
|
+
end
|
45
55
|
end
|
46
|
-
end
|
47
56
|
|
48
|
-
|
57
|
+
padding_str = ' ' * self.padding
|
58
|
+
padding_str + colorize(cell) + padding_str
|
59
|
+
end
|
60
|
+
|
61
|
+
def colorize(str)
|
62
|
+
str = str.send(color) if self.color
|
63
|
+
str = str.send('bold') if self.bold
|
64
|
+
str
|
65
|
+
end
|
49
66
|
end
|
50
67
|
end
|
@@ -11,6 +11,7 @@ module CommandLineReporter
|
|
11
11
|
DEFAULTS = {
|
12
12
|
:width => 100,
|
13
13
|
:align => 'left',
|
14
|
+
:formatter => 'nested',
|
14
15
|
}
|
15
16
|
|
16
17
|
def suppress_output
|
@@ -36,21 +37,25 @@ module CommandLineReporter
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def report(options = {}, &block)
|
39
|
-
self.formatter ||=
|
40
|
+
self.formatter ||= DEFAULTS[:formatter]
|
40
41
|
self.formatter.format(options, block)
|
41
42
|
end
|
42
43
|
|
44
|
+
def progress(override = nil)
|
45
|
+
self.formatter.progress(override)
|
46
|
+
end
|
47
|
+
|
43
48
|
def footer(options = {})
|
44
49
|
section(:footer, options)
|
45
50
|
end
|
46
51
|
|
47
52
|
def horizontal_rule(options = {})
|
48
|
-
validate_options(options, :char, :width)
|
53
|
+
validate_options(options, :char, :width, :color, :bold)
|
49
54
|
|
50
55
|
char = options[:char].is_a?(String) ? options[:char] : '-'
|
51
56
|
width = options[:width] || DEFAULTS[:width]
|
52
57
|
|
53
|
-
|
58
|
+
aligned(char * width, :width => width, :color => options[:color], :bold => options[:bold])
|
54
59
|
end
|
55
60
|
|
56
61
|
def vertical_spacing(lines = 1)
|
@@ -60,7 +65,7 @@ module CommandLineReporter
|
|
60
65
|
end
|
61
66
|
|
62
67
|
def datetime(options = {})
|
63
|
-
validate_options(options, :align, :width, :format)
|
68
|
+
validate_options(options, :align, :width, :format, :color, :bold)
|
64
69
|
|
65
70
|
format = options[:format] || '%Y-%m-%d - %l:%M:%S%p'
|
66
71
|
align = options[:align] || DEFAULTS[:align]
|
@@ -70,67 +75,76 @@ module CommandLineReporter
|
|
70
75
|
|
71
76
|
raise Exception if text.size > width
|
72
77
|
|
73
|
-
aligned(text,
|
78
|
+
aligned(text, :align => align, :width => width, :color => options[:color], :bold => options[:bold])
|
74
79
|
end
|
75
80
|
|
76
81
|
def aligned(text, options = {})
|
77
|
-
validate_options(options, :align, :width)
|
82
|
+
validate_options(options, :align, :width, :color, :bold)
|
78
83
|
|
79
84
|
align = options[:align] || DEFAULTS[:align]
|
80
85
|
width = options[:width] || DEFAULTS[:width]
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
color = options[:color]
|
87
|
+
bold = options[:bold] || false
|
88
|
+
|
89
|
+
line = case align
|
90
|
+
when 'left'
|
91
|
+
text
|
92
|
+
when 'right'
|
93
|
+
text.rjust(width)
|
94
|
+
when 'center'
|
95
|
+
text.rjust((width - text.size)/2 + text.size)
|
96
|
+
else
|
97
|
+
raise ArgumentError
|
98
|
+
end
|
99
|
+
|
100
|
+
line = line.send(color) if color
|
101
|
+
line = line.send('bold') if bold
|
102
|
+
|
103
|
+
puts line
|
92
104
|
end
|
93
105
|
|
94
106
|
def table(options = {})
|
95
|
-
@table = Table.new(options)
|
107
|
+
@table = CommandLineReporter::Table.new(options)
|
96
108
|
yield
|
97
109
|
@table.output
|
98
110
|
end
|
99
111
|
|
100
112
|
def row(options = {})
|
101
|
-
@row = Row.new
|
113
|
+
@row = CommandLineReporter::Row.new(options)
|
102
114
|
yield
|
103
115
|
@table.add(@row)
|
104
116
|
end
|
105
117
|
|
106
118
|
def column(text, options = {})
|
107
|
-
col = Column.new(text, options)
|
119
|
+
col = CommandLineReporter::Column.new(text, options)
|
108
120
|
@row.add(col)
|
109
121
|
end
|
110
122
|
|
111
123
|
private
|
112
124
|
|
113
125
|
def section(type, options)
|
114
|
-
validate_options(options, :title, :width, :align, :spacing, :timestamp, :rule)
|
126
|
+
validate_options(options, :title, :width, :align, :spacing, :timestamp, :rule, :color, :bold)
|
115
127
|
|
116
128
|
title = options[:title] || 'Report'
|
117
129
|
width = options[:width] || DEFAULTS[:width]
|
118
130
|
align = options[:align] || DEFAULTS[:align]
|
119
131
|
lines = options[:spacing] || 1
|
132
|
+
color = options[:color]
|
133
|
+
bold = options[:bold] || false
|
120
134
|
|
121
135
|
# This also ensures that width is a Fixnum
|
122
136
|
raise ArgumentError if title.size > width
|
123
137
|
|
124
138
|
if type == :footer
|
125
139
|
vertical_spacing(lines)
|
126
|
-
horizontal_rule(:char => options[:rule], :width => width) if options[:rule]
|
140
|
+
horizontal_rule(:char => options[:rule], :width => width, :color => color, :bold => bold) if options[:rule]
|
127
141
|
end
|
128
142
|
|
129
|
-
aligned(title,
|
130
|
-
datetime(:align => align, :width => width) if options[:timestamp]
|
143
|
+
aligned(title, :align => align, :width => width, :color => color, :bold => bold)
|
144
|
+
datetime(:align => align, :width => width, :color => color, :bold => bold) if options[:timestamp]
|
131
145
|
|
132
146
|
if type == :header
|
133
|
-
horizontal_rule(:char => options[:rule], :width => width) if options[:rule]
|
147
|
+
horizontal_rule(:char => options[:rule], :width => width, :color => color, :bold => bold) if options[:rule]
|
134
148
|
vertical_spacing(lines)
|
135
149
|
end
|
136
150
|
end
|
data/lib/nested_formatter.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'options_validator'
|
3
|
+
require 'colored'
|
2
4
|
|
3
5
|
module CommandLineReporter
|
4
6
|
class NestedFormatter
|
5
7
|
include Singleton
|
8
|
+
include OptionsValidator
|
6
9
|
|
7
|
-
|
10
|
+
VALID_OPTIONS = [:message, :type, :complete, :indent_size, :color, :bold]
|
11
|
+
attr_accessor :indent_size, :complete_string, :message_string, :color, :bold
|
8
12
|
|
9
13
|
def format(options, block)
|
10
|
-
|
14
|
+
self.validate_options(options, *VALID_OPTIONS)
|
11
15
|
|
12
16
|
indent_level :incr
|
13
17
|
|
@@ -17,15 +21,15 @@ module CommandLineReporter
|
|
17
21
|
complete_str = options[:complete] || self.complete_string
|
18
22
|
|
19
23
|
if options[:type] == 'inline'
|
20
|
-
|
24
|
+
colorize("#{message_str}...", true, options)
|
21
25
|
else
|
22
|
-
|
26
|
+
colorize(message_str, false, options)
|
23
27
|
complete_str = padding + complete_str
|
24
28
|
end
|
25
29
|
|
26
30
|
block.call
|
27
31
|
|
28
|
-
|
32
|
+
colorize(complete_str, false, options)
|
29
33
|
|
30
34
|
indent_level :decr
|
31
35
|
end
|
@@ -44,6 +48,17 @@ module CommandLineReporter
|
|
44
48
|
|
45
49
|
private
|
46
50
|
|
51
|
+
def colorize(str, inline, options)
|
52
|
+
str = str.send(options[:color]) if options[:color]
|
53
|
+
str = str.bold if options[:bold]
|
54
|
+
|
55
|
+
if inline
|
56
|
+
print str
|
57
|
+
else
|
58
|
+
puts str
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
47
62
|
def indent_level(value)
|
48
63
|
case value
|
49
64
|
when :incr
|
data/lib/progress_formatter.rb
CHANGED
@@ -1,19 +1,34 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'options_validator'
|
3
|
+
require 'colored'
|
2
4
|
|
3
5
|
module CommandLineReporter
|
4
6
|
class ProgressFormatter
|
5
7
|
include Singleton
|
8
|
+
include OptionsValidator
|
6
9
|
|
7
|
-
|
10
|
+
VALID_OPTIONS = [:indicator, :color, :bold]
|
11
|
+
attr_accessor *VALID_OPTIONS
|
8
12
|
|
9
13
|
def format(options, block)
|
14
|
+
self.validate_options(options, *VALID_OPTIONS)
|
15
|
+
|
10
16
|
self.indicator = options[:indicator] if options[:indicator]
|
17
|
+
self.color = options[:color]
|
18
|
+
self.bold = options[:bold] || false
|
19
|
+
|
11
20
|
block.call
|
21
|
+
|
12
22
|
puts
|
13
23
|
end
|
14
24
|
|
15
25
|
def progress(override = nil)
|
16
|
-
|
26
|
+
str = override || self.indicator
|
27
|
+
|
28
|
+
str = str.send(self.color) if self.color
|
29
|
+
str = str.send('bold') if self.bold
|
30
|
+
|
31
|
+
print str
|
17
32
|
end
|
18
33
|
|
19
34
|
def indicator
|