command_kit 0.3.0 → 0.4.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +4 -6
- data/.rubocop.yml +13 -0
- data/ChangeLog.md +18 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +18 -9
- data/command_kit.gemspec +0 -1
- data/examples/printing/tables.rb +141 -0
- data/gemspec.yml +3 -3
- data/lib/command_kit/bug_report.rb +105 -0
- data/lib/command_kit/colors.rb +4 -4
- data/lib/command_kit/edit.rb +54 -0
- data/lib/command_kit/env.rb +1 -1
- data/lib/command_kit/options/option.rb +5 -1
- data/lib/command_kit/options/option_value.rb +2 -2
- data/lib/command_kit/options/parser.rb +1 -1
- data/lib/command_kit/options/quiet.rb +1 -1
- data/lib/command_kit/options/verbose.rb +2 -2
- data/lib/command_kit/options/version.rb +10 -0
- data/lib/command_kit/options.rb +1 -1
- data/lib/command_kit/os.rb +1 -1
- data/lib/command_kit/printing/fields.rb +56 -0
- data/lib/command_kit/printing/indent.rb +1 -1
- data/lib/command_kit/printing/lists.rb +91 -0
- data/lib/command_kit/printing/tables/border_style.rb +169 -0
- data/lib/command_kit/printing/tables/cell_builder.rb +93 -0
- data/lib/command_kit/printing/tables/row_builder.rb +111 -0
- data/lib/command_kit/printing/tables/style.rb +198 -0
- data/lib/command_kit/printing/tables/table_builder.rb +145 -0
- data/lib/command_kit/printing/tables/table_formatter.rb +254 -0
- data/lib/command_kit/printing/tables.rb +208 -0
- data/lib/command_kit/stdio.rb +5 -1
- data/lib/command_kit/version.rb +1 -1
- data/spec/bug_report_spec.rb +266 -0
- data/spec/colors_spec.rb +6 -0
- data/spec/command_name_spec.rb +1 -1
- data/spec/edit_spec.rb +72 -0
- data/spec/options/option_spec.rb +12 -2
- data/spec/options/quiet_spec.rb +51 -0
- data/spec/options/verbose_spec.rb +51 -0
- data/spec/options/version_spec.rb +146 -0
- data/spec/pager_spec.rb +1 -1
- data/spec/printing/fields_spec.rb +167 -0
- data/spec/printing/lists_spec.rb +99 -0
- data/spec/printing/tables/border_style.rb +43 -0
- data/spec/printing/tables/cell_builer_spec.rb +135 -0
- data/spec/printing/tables/row_builder_spec.rb +165 -0
- data/spec/printing/tables/style_spec.rb +377 -0
- data/spec/printing/tables/table_builder_spec.rb +252 -0
- data/spec/printing/tables/table_formatter_spec.rb +1180 -0
- data/spec/printing/tables_spec.rb +1069 -0
- metadata +33 -7
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/options/verbose'
|
3
|
+
|
4
|
+
describe CommandKit::Options::Verbose do
|
5
|
+
module TestOptionsVerbose
|
6
|
+
class TestCommand
|
7
|
+
include CommandKit::Options::Verbose
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:command_class) { TestOptionsVerbose::TestCommand }
|
12
|
+
|
13
|
+
describe ".included" do
|
14
|
+
subject { command_class }
|
15
|
+
|
16
|
+
it "must include CommandKit::Options" do
|
17
|
+
expect(subject).to include(CommandKit::Options)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "must define a verbose option" do
|
21
|
+
expect(subject.options[:verbose]).to_not be(nil)
|
22
|
+
expect(subject.options[:verbose].short).to eq('-v')
|
23
|
+
expect(subject.options[:verbose].long).to eq('--verbose')
|
24
|
+
expect(subject.options[:verbose].desc).to eq('Enables verbose output')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
subject { command_class.new }
|
29
|
+
|
30
|
+
describe "#verbose?" do
|
31
|
+
context "when @verbose is true" do
|
32
|
+
before do
|
33
|
+
subject.instance_variable_set('@verbose',true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "must return true" do
|
37
|
+
expect(subject.verbose?).to be(true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when @verbose is false" do
|
42
|
+
before do
|
43
|
+
subject.instance_variable_set('@verbose',false)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "must return false" do
|
47
|
+
expect(subject.verbose?).to be(false)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/options/version'
|
3
|
+
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
describe CommandKit::Options::Version do
|
7
|
+
module TestOptionsVersion
|
8
|
+
class TestCommandWithoutVersion
|
9
|
+
|
10
|
+
include CommandKit::Options::Version
|
11
|
+
|
12
|
+
command_name 'test'
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestCommandWithVersion
|
17
|
+
|
18
|
+
include CommandKit::Options::Version
|
19
|
+
|
20
|
+
command_name 'test'
|
21
|
+
version '0.1.0'
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".included" do
|
27
|
+
subject { TestOptionsVersion::TestCommandWithVersion }
|
28
|
+
|
29
|
+
it "must include CommandKit::Options" do
|
30
|
+
expect(subject).to include(CommandKit::Options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ".version" do
|
35
|
+
context "when no .version has been previously set" do
|
36
|
+
subject { TestOptionsVersion::TestCommandWithoutVersion }
|
37
|
+
|
38
|
+
it "must return nil" do
|
39
|
+
expect(subject.version).to be(nil)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when a .version has been set" do
|
44
|
+
subject { TestOptionsVersion::TestCommandWithVersion }
|
45
|
+
|
46
|
+
it "must return the set version" do
|
47
|
+
expect(subject.version).to eq('0.1.0')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the command class inherites from another class" do
|
52
|
+
context "but no version are defined" do
|
53
|
+
module TestOptionsVersion
|
54
|
+
class InheritedCommandWithoutVersion < TestCommandWithoutVersion
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
subject { TestOptionsVersion::InheritedCommandWithoutVersion }
|
59
|
+
|
60
|
+
it "must return nil" do
|
61
|
+
expect(subject.version).to be(nil)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the superclass defines the version" do
|
66
|
+
module TestOptionsVersion
|
67
|
+
class InheritedCommandWithInheritedVersion < TestCommandWithVersion
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:super_class) { TestOptionsVersion::TestCommandWithVersion }
|
72
|
+
subject { TestOptionsVersion::InheritedCommandWithInheritedVersion }
|
73
|
+
|
74
|
+
it "must return the version defined in the superclass" do
|
75
|
+
expect(subject.version).to eq(super_class.version)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when the subclass defines the version" do
|
80
|
+
module TestOptionsVersion
|
81
|
+
class InheritedCommandWithOwnVersion < TestCommandWithoutVersion
|
82
|
+
|
83
|
+
version '0.1.0'
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:super_subject) { TestOptionsVersion::TestCommandWithoutVersion }
|
89
|
+
subject { TestOptionsVersion::InheritedCommandWithOwnVersion }
|
90
|
+
|
91
|
+
it "must return the version set in the subclass" do
|
92
|
+
expect(subject.version).to eq('0.1.0')
|
93
|
+
end
|
94
|
+
|
95
|
+
it "must not change the superclass'es version" do
|
96
|
+
expect(super_subject.version).to be(nil)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when subclass overrides the superclass's version" do
|
101
|
+
module TestOptionsVersion
|
102
|
+
class InheritedCommandThatOverridesVersion < TestCommandWithVersion
|
103
|
+
|
104
|
+
version '0.2.0'
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:super_subject) { TestOptionsVersion::TestCommandWithVersion }
|
110
|
+
subject { TestOptionsVersion::InheritedCommandThatOverridesVersion }
|
111
|
+
|
112
|
+
it "must return the version set in the subclass" do
|
113
|
+
expect(subject.version).to eq('0.2.0')
|
114
|
+
end
|
115
|
+
|
116
|
+
it "must not change the superclass'es version" do
|
117
|
+
expect(super_subject.version).to eq('0.1.0')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:command_class) { TestOptionsVersion::TestCommandWithVersion }
|
124
|
+
|
125
|
+
subject { command_class.new }
|
126
|
+
|
127
|
+
describe "#version" do
|
128
|
+
it "must return the class'es .version value" do
|
129
|
+
expect(subject.version).to eq(command_class.version)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#print_version" do
|
134
|
+
let(:stdout) { StringIO.new }
|
135
|
+
|
136
|
+
subject { command_class.new(stdout: stdout) }
|
137
|
+
|
138
|
+
it "must print the #command_name and #version" do
|
139
|
+
subject.print_version
|
140
|
+
|
141
|
+
expect(stdout.string).to eq(
|
142
|
+
"#{subject.command_name} #{subject.version}#{$/}"
|
143
|
+
)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/spec/pager_spec.rb
CHANGED
@@ -26,7 +26,7 @@ describe CommandKit::Pager do
|
|
26
26
|
|
27
27
|
context "when the PAGER env variable is not set" do
|
28
28
|
context "but the PATH env variable is" do
|
29
|
-
subject { command_class.new(env: {'PATH' => ENV
|
29
|
+
subject { command_class.new(env: {'PATH' => ENV.fetch('PATH')}) }
|
30
30
|
|
31
31
|
it "must search PATH for one of the pagers" do
|
32
32
|
expect(subject.instance_variable_get('@pager_command')).to eq("less -r")
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/printing/fields'
|
3
|
+
|
4
|
+
describe CommandKit::Printing::Fields do
|
5
|
+
module TestPrintingFields
|
6
|
+
class TestCmd
|
7
|
+
|
8
|
+
include CommandKit::Printing::Fields
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:command_class) { TestPrintingFields::TestCmd }
|
14
|
+
subject { command_class.new }
|
15
|
+
|
16
|
+
let(:nl) { $/ }
|
17
|
+
|
18
|
+
describe "#print_fields" do
|
19
|
+
context "when given a Hash" do
|
20
|
+
context "and all key values are the same length" do
|
21
|
+
let(:name1) { 'A' }
|
22
|
+
let(:value1) { 'foo' }
|
23
|
+
let(:name2) { 'B' }
|
24
|
+
let(:value2) { 'bar' }
|
25
|
+
|
26
|
+
let(:hash) do
|
27
|
+
{
|
28
|
+
name1 => value1,
|
29
|
+
name2 => value2
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must not left-justify the Hash keys" do
|
34
|
+
expect {
|
35
|
+
subject.print_fields(hash)
|
36
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "but key values have different lengths" do
|
41
|
+
let(:name1) { 'A' }
|
42
|
+
let(:value1) { 'foo' }
|
43
|
+
let(:name2) { 'Bar' }
|
44
|
+
let(:value2) { 'bar' }
|
45
|
+
|
46
|
+
let(:hash) do
|
47
|
+
{
|
48
|
+
name1 => value1,
|
49
|
+
name2 => value2
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it "must left-justify the Hash keys" do
|
54
|
+
expect {
|
55
|
+
subject.print_fields(hash)
|
56
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "but the key values are not Strings" do
|
61
|
+
let(:name1) { 1 }
|
62
|
+
let(:value1) { 'foo' }
|
63
|
+
let(:name2) { 100 }
|
64
|
+
let(:value2) { 'bar' }
|
65
|
+
|
66
|
+
let(:hash) do
|
67
|
+
{
|
68
|
+
name1 => value1,
|
69
|
+
name2 => value2
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
it "must convert them to Strings before calculating justification" do
|
74
|
+
expect {
|
75
|
+
subject.print_fields(hash)
|
76
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when given an Array of tuples" do
|
82
|
+
context "and all first tuple values are the same length" do
|
83
|
+
let(:name1) { 'A' }
|
84
|
+
let(:value1) { 'foo' }
|
85
|
+
let(:name2) { 'B' }
|
86
|
+
let(:value2) { 'bar' }
|
87
|
+
|
88
|
+
let(:array) do
|
89
|
+
[
|
90
|
+
[name1, value1],
|
91
|
+
[name2, value2]
|
92
|
+
]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "must not left-justify the tuples" do
|
96
|
+
expect {
|
97
|
+
subject.print_fields(array)
|
98
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "but first tuple values have different lengths" do
|
103
|
+
let(:name1) { 'A' }
|
104
|
+
let(:value1) { 'foo' }
|
105
|
+
let(:name2) { 'Bar' }
|
106
|
+
let(:value2) { 'bar' }
|
107
|
+
|
108
|
+
let(:array) do
|
109
|
+
[
|
110
|
+
[name1, value1],
|
111
|
+
[name2, value2]
|
112
|
+
]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "must left-justify the tuples" do
|
116
|
+
expect {
|
117
|
+
subject.print_fields(array)
|
118
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "but the first tuple values are not Strings" do
|
123
|
+
let(:name1) { 1 }
|
124
|
+
let(:value1) { 'foo' }
|
125
|
+
let(:name2) { 100 }
|
126
|
+
let(:value2) { 'bar' }
|
127
|
+
|
128
|
+
let(:array) do
|
129
|
+
[
|
130
|
+
[name1, value1],
|
131
|
+
[name2, value2]
|
132
|
+
]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "must convert them to Strings before calculating justification" do
|
136
|
+
expect {
|
137
|
+
subject.print_fields(array)
|
138
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{value2}#{nl}").to_stdout
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "but the values contain multiple lines" do
|
144
|
+
let(:name1) { 'A' }
|
145
|
+
let(:value1) { 'foo' }
|
146
|
+
let(:name2) { 'Bar' }
|
147
|
+
let(:line1) { 'bar' }
|
148
|
+
let(:line2) { 'baz' }
|
149
|
+
let(:value2) do
|
150
|
+
[line1, line2].join($/)
|
151
|
+
end
|
152
|
+
|
153
|
+
let(:hash) do
|
154
|
+
{
|
155
|
+
name1 => value1,
|
156
|
+
name2 => value2
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
it "must print the header with the first line and then indent the other lines" do
|
161
|
+
expect {
|
162
|
+
subject.print_fields(hash)
|
163
|
+
}.to output("#{name1}: #{value1}#{nl}#{name2}: #{line1}#{nl} #{line2}#{nl}").to_stdout
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/printing/lists'
|
3
|
+
|
4
|
+
describe CommandKit::Printing::Lists do
|
5
|
+
module TestPrintingLists
|
6
|
+
class TestCmd
|
7
|
+
|
8
|
+
include CommandKit::Printing::Lists
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:command_class) { TestPrintingLists::TestCmd }
|
14
|
+
subject { command_class.new }
|
15
|
+
|
16
|
+
describe "#print_list" do
|
17
|
+
let(:list) { %w[foo bar baz] }
|
18
|
+
|
19
|
+
it "must print each item in the list with a '*' bullet" do
|
20
|
+
expect {
|
21
|
+
subject.print_list(list)
|
22
|
+
}.to output(
|
23
|
+
list.map { |item| "* #{item}" }.join($/) + $/
|
24
|
+
).to_stdout
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when the list contins multi-line Strings" do
|
28
|
+
let(:item1) { "foo" }
|
29
|
+
let(:item2) do
|
30
|
+
[
|
31
|
+
"line 1",
|
32
|
+
"line 2",
|
33
|
+
"line 3"
|
34
|
+
].join($/)
|
35
|
+
end
|
36
|
+
let(:item3) { "bar" }
|
37
|
+
let(:list) { [item1, item2, item3] }
|
38
|
+
|
39
|
+
it "must print the bullet with the first line and then indent the other lines" do
|
40
|
+
expect {
|
41
|
+
subject.print_list(list)
|
42
|
+
}.to output(
|
43
|
+
[
|
44
|
+
"* #{item1}",
|
45
|
+
"* #{item2.lines[0].chomp}",
|
46
|
+
" #{item2.lines[1].chomp}",
|
47
|
+
" #{item2.lines[2].chomp}",
|
48
|
+
"* #{item3}",
|
49
|
+
''
|
50
|
+
].join($/)
|
51
|
+
).to_stdout
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when the list contains nested-lists" do
|
56
|
+
let(:item1) { 'item 1' }
|
57
|
+
let(:sub_item1) { 'sub-item 1' }
|
58
|
+
let(:sub_item2) { 'sub-item 2' }
|
59
|
+
let(:item2) { 'item 2' }
|
60
|
+
|
61
|
+
let(:list) do
|
62
|
+
[
|
63
|
+
'item 1',
|
64
|
+
[
|
65
|
+
'sub-item 1',
|
66
|
+
'sub-item 2'
|
67
|
+
],
|
68
|
+
'item 2'
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "must indent and print each sub-list" do
|
73
|
+
expect {
|
74
|
+
subject.print_list(list)
|
75
|
+
}.to output(
|
76
|
+
[
|
77
|
+
"* #{item1}",
|
78
|
+
" * #{sub_item1}",
|
79
|
+
" * #{sub_item2}",
|
80
|
+
"* #{item2}",
|
81
|
+
''
|
82
|
+
].join($/)
|
83
|
+
).to_stdout
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when the bullet: keyowrd argument is given" do
|
88
|
+
let(:bullet) { '-' }
|
89
|
+
|
90
|
+
it "must print each item in the list with the bullet character" do
|
91
|
+
expect {
|
92
|
+
subject.print_list(list, bullet: bullet)
|
93
|
+
}.to output(
|
94
|
+
list.map { |item| "#{bullet} #{item}" }.join($/) + $/
|
95
|
+
).to_stdout
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/printing/tables/border_style'
|
3
|
+
|
4
|
+
describe CommandKit::Printing::Tables::BorderStyle do
|
5
|
+
describe "#initialize" do
|
6
|
+
[
|
7
|
+
:top_left_corner,
|
8
|
+
:top_border,
|
9
|
+
:top_joined_border,
|
10
|
+
:top_right_corner,
|
11
|
+
:left_border,
|
12
|
+
:left_joined_border,
|
13
|
+
:horizontal_separator,
|
14
|
+
:vertical_separator,
|
15
|
+
:inner_joined_border,
|
16
|
+
:right_border,
|
17
|
+
:right_joined_border,
|
18
|
+
:bottom_border,
|
19
|
+
:bottom_left_corner,
|
20
|
+
:bottom_joined_border,
|
21
|
+
:bottom_right_corner
|
22
|
+
].each do |keyword|
|
23
|
+
context "when #{keyword}: keyword is given" do
|
24
|
+
let(:keyword) { keyword }
|
25
|
+
let(:value) { 'x' }
|
26
|
+
|
27
|
+
subject { described_class.new(**{keyword => value}) }
|
28
|
+
|
29
|
+
it "must set ##{keyword}" do
|
30
|
+
expect(subject.send(keyword)).to eq(value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when #{keyword}: keyword is not given" do
|
35
|
+
let(:keyword) { keyword }
|
36
|
+
|
37
|
+
it "must default ##{keyword} to ' '" do
|
38
|
+
expect(subject.send(keyword)).to eq(' ')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_kit/printing/tables/cell_builder'
|
3
|
+
|
4
|
+
describe CommandKit::Printing::Tables::CellBuilder do
|
5
|
+
describe "#initialize" do
|
6
|
+
it "must initialize #lines to an empty Array" do
|
7
|
+
expect(subject.lines).to eq([])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "must default #height to 0" do
|
11
|
+
expect(subject.height).to eq(0)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "must default #width to 0" do
|
15
|
+
expect(subject.width).to eq(0)
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when an initial value is given" do
|
19
|
+
subject { described_class.new(value) }
|
20
|
+
|
21
|
+
context "and it's a String" do
|
22
|
+
let(:value) do
|
23
|
+
<<~EOS
|
24
|
+
foo bar
|
25
|
+
baz qux
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
|
29
|
+
it "must split the value into separate lines and populate #lines" do
|
30
|
+
expect(subject.lines).to eq(value.lines(chomp: true))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "but it's not a String" do
|
35
|
+
let(:value) { 42 }
|
36
|
+
|
37
|
+
it "must convert it to a String before adding it to #lines" do
|
38
|
+
expect(subject.lines).to eq([value.to_s])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".line_width" do
|
45
|
+
subject { described_class }
|
46
|
+
|
47
|
+
context "when given a plain-text String" do
|
48
|
+
let(:line) { "foo bar baz" }
|
49
|
+
|
50
|
+
it "must return the line length" do
|
51
|
+
expect(subject.line_width(line)).to eq(line.length)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when given a String containing ANSI control sequences" do
|
56
|
+
let(:line_without_ansi) { "foo bar baz" }
|
57
|
+
let(:line) { "\e[1m\e[32m#{line_without_ansi}\e[0m" }
|
58
|
+
|
59
|
+
it "must return the length of the line without the ANSI control sequences" do
|
60
|
+
expect(subject.line_width(line)).to eq(line_without_ansi.length)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#<<" do
|
66
|
+
let(:line) { "foo bar baz" }
|
67
|
+
|
68
|
+
it "must increment #height by 1" do
|
69
|
+
expect(subject.height).to eq(0)
|
70
|
+
|
71
|
+
subject << line
|
72
|
+
expect(subject.height).to eq(1)
|
73
|
+
|
74
|
+
subject << line
|
75
|
+
expect(subject.height).to eq(2)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "must append the line to #lines" do
|
79
|
+
subject << line
|
80
|
+
|
81
|
+
expect(subject.lines.last).to eq(line)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "must return self" do
|
85
|
+
expect(subject << line).to be(subject)
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when the line's line-width is greater than #width" do
|
89
|
+
let(:previous_line) { "foo" }
|
90
|
+
|
91
|
+
it "must update #width" do
|
92
|
+
subject << previous_line
|
93
|
+
expect(subject.width).to eq(previous_line.length)
|
94
|
+
|
95
|
+
subject << line
|
96
|
+
expect(subject.width).to eq(line.length)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when the line's line-width is not greater than #width" do
|
101
|
+
let(:previous_line) { "foo bar baz" }
|
102
|
+
let(:line) { "foo" }
|
103
|
+
|
104
|
+
it "must update #width" do
|
105
|
+
subject << previous_line
|
106
|
+
expect(subject.width).to eq(previous_line.length)
|
107
|
+
|
108
|
+
subject << line
|
109
|
+
expect(subject.width).to eq(previous_line.length)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#[]" do
|
115
|
+
let(:line1) { "foo bar" }
|
116
|
+
let(:line2) { "baz qux" }
|
117
|
+
|
118
|
+
before do
|
119
|
+
subject << line1
|
120
|
+
subject << line2
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when the index is within the bounds of #lines" do
|
124
|
+
it "must return the line at the given index" do
|
125
|
+
expect(subject[1]).to eq(line2)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when the index is out of the bounds of #lines" do
|
130
|
+
it "must return ''" do
|
131
|
+
expect(subject[2]).to eq('')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|