tty-table 0.11.0 → 0.12.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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +104 -98
  5. data/lib/tty-table.rb +1 -1
  6. data/lib/tty/table.rb +70 -34
  7. data/lib/tty/table/alignment_set.rb +1 -3
  8. data/lib/tty/table/border.rb +50 -57
  9. data/lib/tty/table/border/ascii.rb +16 -16
  10. data/lib/tty/table/border/null.rb +19 -22
  11. data/lib/tty/table/border/row_line.rb +3 -2
  12. data/lib/tty/table/border/unicode.rb +16 -16
  13. data/lib/tty/table/border_dsl.rb +24 -22
  14. data/lib/tty/table/border_options.rb +32 -27
  15. data/lib/tty/table/column_constraint.rb +12 -9
  16. data/lib/tty/table/columns.rb +6 -6
  17. data/lib/tty/table/field.rb +46 -28
  18. data/lib/tty/table/header.rb +5 -5
  19. data/lib/tty/table/indentation.rb +7 -4
  20. data/lib/tty/table/operation/alignment.rb +1 -1
  21. data/lib/tty/table/operation/escape.rb +1 -2
  22. data/lib/tty/table/operation/padding.rb +1 -1
  23. data/lib/tty/table/operation/truncation.rb +1 -1
  24. data/lib/tty/table/operation/wrapped.rb +1 -1
  25. data/lib/tty/table/options.rb +1 -1
  26. data/lib/tty/table/orientation.rb +4 -4
  27. data/lib/tty/table/renderer.rb +8 -8
  28. data/lib/tty/table/renderer/ascii.rb +2 -2
  29. data/lib/tty/table/renderer/basic.rb +37 -40
  30. data/lib/tty/table/renderer/unicode.rb +1 -1
  31. data/lib/tty/table/row.rb +7 -7
  32. data/lib/tty/table/validatable.rb +23 -7
  33. data/lib/tty/table/version.rb +1 -1
  34. metadata +23 -272
  35. data/Rakefile +0 -10
  36. data/examples/alignment.rb +0 -10
  37. data/examples/basic.rb +0 -6
  38. data/examples/orientation.rb +0 -13
  39. data/examples/padding.rb +0 -15
  40. data/examples/resize.rb +0 -15
  41. data/spec/spec_helper.rb +0 -50
  42. data/spec/unit/access_spec.rb +0 -84
  43. data/spec/unit/add_row_spec.rb +0 -26
  44. data/spec/unit/alignment_set/each_spec.rb +0 -15
  45. data/spec/unit/alignment_set/new_spec.rb +0 -25
  46. data/spec/unit/alignment_set/to_ary_spec.rb +0 -12
  47. data/spec/unit/alignment_spec.rb +0 -69
  48. data/spec/unit/border/ascii/rendering_spec.rb +0 -88
  49. data/spec/unit/border/new_spec.rb +0 -25
  50. data/spec/unit/border/null/rendering_spec.rb +0 -128
  51. data/spec/unit/border/options/from_spec.rb +0 -36
  52. data/spec/unit/border/options/new_spec.rb +0 -12
  53. data/spec/unit/border/unicode/rendering_spec.rb +0 -61
  54. data/spec/unit/border_options/new_spec.rb +0 -18
  55. data/spec/unit/border_options/update_spec.rb +0 -16
  56. data/spec/unit/column_constraint/enforce_spec.rb +0 -68
  57. data/spec/unit/column_constraint/widths_spec.rb +0 -33
  58. data/spec/unit/columns/extract_widths_spec.rb +0 -41
  59. data/spec/unit/columns/total_width_spec.rb +0 -11
  60. data/spec/unit/columns/widths_from_spec.rb +0 -49
  61. data/spec/unit/data_spec.rb +0 -10
  62. data/spec/unit/each_spec.rb +0 -24
  63. data/spec/unit/each_with_index_spec.rb +0 -49
  64. data/spec/unit/empty_spec.rb +0 -21
  65. data/spec/unit/eql_spec.rb +0 -32
  66. data/spec/unit/field/equality_spec.rb +0 -49
  67. data/spec/unit/field/length_spec.rb +0 -33
  68. data/spec/unit/field/lines_spec.rb +0 -19
  69. data/spec/unit/field/new_spec.rb +0 -27
  70. data/spec/unit/field/width_spec.rb +0 -21
  71. data/spec/unit/filter_spec.rb +0 -20
  72. data/spec/unit/header/call_spec.rb +0 -28
  73. data/spec/unit/header/color_spec.rb +0 -17
  74. data/spec/unit/header/equality_spec.rb +0 -49
  75. data/spec/unit/header/height_spec.rb +0 -25
  76. data/spec/unit/header/new_spec.rb +0 -13
  77. data/spec/unit/header/set_spec.rb +0 -18
  78. data/spec/unit/header/to_ary_spec.rb +0 -12
  79. data/spec/unit/header_spec.rb +0 -11
  80. data/spec/unit/indentation/indent_spec.rb +0 -15
  81. data/spec/unit/new_spec.rb +0 -71
  82. data/spec/unit/operation/alignment/call_spec.rb +0 -37
  83. data/spec/unit/operation/escape/call_spec.rb +0 -13
  84. data/spec/unit/operation/filter/call_spec.rb +0 -14
  85. data/spec/unit/operation/truncation/call_spec.rb +0 -28
  86. data/spec/unit/operation/wrapped/call_spec.rb +0 -38
  87. data/spec/unit/operations/new_spec.rb +0 -28
  88. data/spec/unit/options/access_spec.rb +0 -12
  89. data/spec/unit/options_spec.rb +0 -23
  90. data/spec/unit/orientation_spec.rb +0 -143
  91. data/spec/unit/padding_spec.rb +0 -115
  92. data/spec/unit/properties_spec.rb +0 -23
  93. data/spec/unit/render_repeat_spec.rb +0 -39
  94. data/spec/unit/render_spec.rb +0 -61
  95. data/spec/unit/render_with_spec.rb +0 -104
  96. data/spec/unit/renderer/ascii/coloring_spec.rb +0 -83
  97. data/spec/unit/renderer/ascii/indentation_spec.rb +0 -39
  98. data/spec/unit/renderer/ascii/multiline_spec.rb +0 -99
  99. data/spec/unit/renderer/ascii/padding_spec.rb +0 -115
  100. data/spec/unit/renderer/ascii/render_spec.rb +0 -66
  101. data/spec/unit/renderer/ascii/resizing_spec.rb +0 -112
  102. data/spec/unit/renderer/ascii/separator_spec.rb +0 -38
  103. data/spec/unit/renderer/basic/alignment_spec.rb +0 -86
  104. data/spec/unit/renderer/basic/coloring_spec.rb +0 -59
  105. data/spec/unit/renderer/basic/extract_column_widths_spec.rb +0 -26
  106. data/spec/unit/renderer/basic/filter_spec.rb +0 -51
  107. data/spec/unit/renderer/basic/indentation_spec.rb +0 -46
  108. data/spec/unit/renderer/basic/multiline_spec.rb +0 -72
  109. data/spec/unit/renderer/basic/new_spec.rb +0 -24
  110. data/spec/unit/renderer/basic/options_spec.rb +0 -50
  111. data/spec/unit/renderer/basic/padding_spec.rb +0 -74
  112. data/spec/unit/renderer/basic/render_spec.rb +0 -55
  113. data/spec/unit/renderer/basic/resizing_spec.rb +0 -94
  114. data/spec/unit/renderer/basic/separator_spec.rb +0 -41
  115. data/spec/unit/renderer/basic/single_row_separator_spec.rb +0 -80
  116. data/spec/unit/renderer/basic/truncation_spec.rb +0 -33
  117. data/spec/unit/renderer/basic/wrapping_spec.rb +0 -38
  118. data/spec/unit/renderer/border_spec.rb +0 -102
  119. data/spec/unit/renderer/render_spec.rb +0 -34
  120. data/spec/unit/renderer/select_spec.rb +0 -20
  121. data/spec/unit/renderer/unicode/coloring_spec.rb +0 -68
  122. data/spec/unit/renderer/unicode/indentation_spec.rb +0 -39
  123. data/spec/unit/renderer/unicode/padding_spec.rb +0 -59
  124. data/spec/unit/renderer/unicode/render_spec.rb +0 -66
  125. data/spec/unit/renderer/unicode/separator_spec.rb +0 -36
  126. data/spec/unit/renderer_spec.rb +0 -17
  127. data/spec/unit/rotate_spec.rb +0 -84
  128. data/spec/unit/row/access_spec.rb +0 -23
  129. data/spec/unit/row/call_spec.rb +0 -43
  130. data/spec/unit/row/data_spec.rb +0 -24
  131. data/spec/unit/row/each_spec.rb +0 -29
  132. data/spec/unit/row/equality_spec.rb +0 -71
  133. data/spec/unit/row/height_spec.rb +0 -25
  134. data/spec/unit/row/new_spec.rb +0 -39
  135. data/spec/unit/row/to_ary_spec.rb +0 -12
  136. data/spec/unit/to_s_spec.rb +0 -56
  137. data/spec/unit/transformation/extract_tuples_spec.rb +0 -33
  138. data/spec/unit/utf_spec.rb +0 -31
  139. data/spec/unit/validatable/validate_options_spec.rb +0 -31
  140. data/spec/unit/validatable_spec.rb +0 -30
  141. data/tasks/console.rake +0 -11
  142. data/tasks/coverage.rake +0 -11
  143. data/tasks/spec.rake +0 -29
  144. data/tty-table.gemspec +0 -41
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "bundler/gem_tasks"
4
-
5
- FileList['tasks/**/*.rake'].each(&method(:import))
6
-
7
- desc 'Run all specs'
8
- task ci: %w[ spec ]
9
-
10
- task default: :spec
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../lib/tty-table'
4
-
5
- table = TTY::Table.new header: ['right align', 'center align', 'left align']
6
- table << ['a1', 'a2', 'a3']
7
- table << ['b1','b2', 'b3']
8
- table << ['c1', 'c2', 'c3']
9
-
10
- puts table.render(:ascii, alignments: [:right, :center, :left])
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../lib/tty-table'
4
-
5
- table = TTY::Table.new ['header1','header2'], [['a1', 'a2'], ['b1', 'b2']]
6
- puts table.render(:basic)
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../lib/tty-table"
4
-
5
- table = TTY::Table.new(header: ["Column 1", "Column 2", "Column 3"]) do |t|
6
- t << [ "r1 c1", "r1 c2", "r1 c3" ]
7
- t << [ "r2 c1", "r2 c2", "r2 c3" ]
8
- t << [ "r3 c1", "r3 c2", "r3 c3" ]
9
- end
10
-
11
- table.orientation = :vertical
12
-
13
- puts table.render(:ascii)
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pastel'
4
- require_relative "../lib/tty-table"
5
-
6
- pastel = Pastel.new
7
- yellow = pastel.yellow.detach
8
-
9
- table = TTY::Table.new(header: [ yellow.("Column 1"), yellow.("Column 2"), yellow.("Column 3")]) do |t|
10
- t << [ "11", "12", "13" ]
11
- t << [ "21", "22", "23" ]
12
- t << [ "31", "32", "33" ]
13
- end
14
-
15
- puts table.render(:ascii, padding: [1, 2, 1, 2])
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pastel'
4
- require_relative "../lib/tty-table"
5
-
6
- pastel = Pastel.new
7
- yellow = pastel.yellow.detach
8
-
9
- table = TTY::Table.new(header: [ yellow.("Column 1"), yellow.("Column 2"), yellow.("Column 3")]) do |t|
10
- t << [ "11", "12", "13" ]
11
- t << [ "21", "22", "23" ]
12
- t << [ "31", "32", "33" ]
13
- end
14
-
15
- puts table.render(:ascii, resize: true, padding: [1, 2, 1, 2])
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if ENV['COVERAGE'] || ENV['TRAVIS']
4
- require 'simplecov'
5
- require 'coveralls'
6
-
7
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
- SimpleCov::Formatter::HTMLFormatter,
9
- Coveralls::SimpleCov::Formatter
10
- ]
11
-
12
- SimpleCov.start do
13
- command_name 'spec'
14
- add_filter 'spec'
15
- end
16
- end
17
-
18
- require 'tty-table'
19
-
20
- RSpec.configure do |config|
21
- config.expect_with :rspec do |expectations|
22
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
23
- end
24
-
25
- config.mock_with :rspec do |mocks|
26
- mocks.verify_partial_doubles = true
27
- end
28
-
29
- # Limits the available syntax to the non-monkey patched syntax that is recommended.
30
- config.disable_monkey_patching!
31
-
32
- # This setting enables warnings. It's recommended, but in some cases may
33
- # be too noisy due to issues in dependencies.
34
- config.warnings = true
35
-
36
- if config.files_to_run.one?
37
- config.default_formatter = 'doc'
38
- end
39
-
40
- config.profile_examples = 2
41
-
42
- config.order = :random
43
-
44
- Kernel.srand config.seed
45
- end
46
-
47
- def unindent(string)
48
- prefix = string.scan(/^[ \t]+(?=\S)/).min
49
- string.gsub(/^#{prefix}/, '').chomp
50
- end
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table, 'access' do
4
- let(:header) { [:head1, :head2] }
5
- let(:rows) { [['a1', 'a2'], ['b1', 'b2']] }
6
-
7
- subject(:table) { TTY::Table.new rows: rows, header: header }
8
-
9
- it { is_expected.to respond_to(:element) }
10
-
11
- it { is_expected.to respond_to(:component) }
12
-
13
- it { is_expected.to respond_to(:at) }
14
-
15
- context 'when array like access' do
16
- it { expect(table[0,0]).to eq('a1') }
17
-
18
- it { expect(table[0]).to eq(['a1','a2']) }
19
-
20
- it { expect(table[5]).to eq(nil) }
21
-
22
- it { expect(table[-1]).to eq(['b1','b2']) }
23
-
24
- it { expect(table[5,5]).to eq(nil) }
25
-
26
- it 'raises error for negative indices' do
27
- expect { table[-5,-5] }.to raise_error(IndexError)
28
- end
29
- end
30
-
31
- context '#row' do
32
- it 'returns nil for wrong index' do
33
- expect(table.row(11)).to be_nil
34
- end
35
-
36
- it 'gets row at index' do
37
- expect(table.row(1)).to eq(rows[1])
38
- end
39
-
40
- it 'yields self for wrong index' do
41
- block = lambda { |el| [] << el }
42
- expect(table.row(11, &block)).to eq(table)
43
- end
44
-
45
- it 'yields row at index' do
46
- yields = []
47
- expect {
48
- table.row(1).each { |el| yields << el }
49
- }.to change { yields }.from( [] ).to( rows[1] )
50
- end
51
- end
52
-
53
- context '#column' do
54
- it "gets based on header name" do
55
- expect(table.column(:head1)).to eq(['a1', 'b1'])
56
- end
57
-
58
- it "yields based on header name" do
59
- yielded = []
60
- table.column(:head1) { |el| yielded << el }
61
- expect(yielded).to eql(['a1', 'b1'])
62
- end
63
-
64
- it 'returns nil for wrong index' do
65
- expect(table.column(11)).to be_nil
66
- end
67
-
68
- it 'gets column at index' do
69
- expect(table.column(0)).to eq(['a1', 'b1'])
70
- end
71
-
72
- it 'yields self for wrong index' do
73
- block = lambda { |el| [] << el }
74
- expect(table.column(11, &block)).to eq(table)
75
- end
76
-
77
- it 'yields column at index' do
78
- yields = []
79
- expect {
80
- table.column(1).each { |el| yields << el }
81
- }.to change { yields }.from( [] ).to( ['a2', 'b2'])
82
- end
83
- end
84
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table, '#<<' do
4
- let(:rows) { ['a', 'b', 'c'] }
5
- let(:object) { described_class }
6
-
7
- subject(:table) { object[rows] }
8
-
9
- context 'with primitive values' do
10
- let(:row) { [1, 2, 3] }
11
-
12
- it 'extracts values correctly' do
13
- table << row
14
- expect(table.to_a.last).to eql(row)
15
- end
16
- end
17
-
18
- context 'with complex values' do
19
- let(:row) { [1, { value: 2 }, 3] }
20
-
21
- it 'extracts values correctly' do
22
- table << row
23
- expect(table.to_a.last).to eql([1,2,3])
24
- end
25
- end
26
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table::AlignmentSet, '#each' do
4
- let(:alignments) { [:left, :center, :right] }
5
- let(:yields) { [] }
6
- let(:object) { described_class.new alignments }
7
-
8
- subject { object.each { |alignment| yields << alignment } }
9
-
10
- it 'yields each alignment' do
11
- expect { subject }.to change { yields.dup }.
12
- from([]).
13
- to(alignments)
14
- end
15
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table::AlignmentSet, '#new' do
4
- let(:object) { described_class }
5
-
6
- subject(:alignment_set) { object.new(argument) }
7
-
8
- context 'with no argument' do
9
- let(:argument) { [] }
10
-
11
- it { is_expected.to be_kind_of(Enumerable) }
12
-
13
- it { is_expected.to be_instance_of(object) }
14
-
15
- it { expect(alignment_set.to_a).to eq([]) }
16
- end
17
-
18
- context 'with argument' do
19
- let(:argument) { [:center, :left] }
20
-
21
- it { is_expected.to be_instance_of(object) }
22
-
23
- it { expect(alignment_set.to_a).to eq(argument) }
24
- end
25
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table::AlignmentSet, '#to_ary' do
4
- let(:argument) { [:center, :left] }
5
- let(:object) { described_class.new argument }
6
-
7
- subject { object.to_ary }
8
-
9
- it { is_expected.to be_instance_of(Array) }
10
-
11
- it { is_expected.to eq(argument) }
12
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table, 'alignment' do
4
- it "aligns table columns when rendering" do
5
- table = TTY::Table.new ['header1', 'header2'], [['a1','a2'],['b1','b2']]
6
- expect(table.render(alignments: [:right, :center])).to eql([
7
- "header1 header2",
8
- " a1 a2 ",
9
- " b1 b2 "
10
- ].join("\n"))
11
- end
12
-
13
- it "uses default alignment when too few alignments provided" do
14
- table = TTY::Table.new ['header1', 'header2'], [['a1','a2'],['b1','b2']]
15
- expect(table.render(alignments: [:right])).to eql([
16
- "header1 header2",
17
- " a1 a2 ",
18
- " b1 b2 "
19
- ].join("\n"))
20
- end
21
-
22
- it "aligns table columns without header when rendering" do
23
- table = TTY::Table.new [['aaaaa1','a2'],['b1','bbbbb2']]
24
- expect(table.render(alignments: [:right, :center])).to eql([
25
- "aaaaa1 a2 ",
26
- " b1 bbbbb2"
27
- ].join("\n"))
28
- end
29
-
30
- it "aligns individual fields when rendering" do
31
- table = TTY::Table.new header: ['header1', 'header2']
32
- table << ['a1', {value: 'a2', alignment: :center}]
33
- table << [{value: 'b1', alignment: :right}, 'b2']
34
- expect(table.render).to eql([
35
- "header1 header2",
36
- "a1 a2 ",
37
- " b1 b2 "
38
- ].join("\n"))
39
- end
40
-
41
- it "prioritizes individual field options over table rendering options" do
42
- table = TTY::Table.new header: ['header1', 'header2']
43
- table << [{value: 'a1', alignment: :center},'a2']
44
- table << ['b1','b2']
45
- expect(table.render(alignments: [:right, :center])).to eql([
46
- "header1 header2",
47
- " a1 a2 ",
48
- " b1 b2 "
49
- ].join("\n"))
50
- end
51
-
52
- it "allows to align all columns at once" do
53
- table = TTY::Table.new ['header1', 'header2'], [['a1','a2'],['b1','b2']]
54
- expect(table.render(alignment: [:center])).to eql([
55
- "header1 header2",
56
- " a1 a2 ",
57
- " b1 b2 "
58
- ].join("\n"))
59
- end
60
-
61
- xit "aligns specific column" do
62
- table = TTY::Table.new ['header1', 'header2'], [['a1','a2'],['b1','b2']]
63
- expect(table.render(column_alignment: [1, :center])).to eql([
64
- "header1 header2",
65
- "a1 a2 ",
66
- "b1 b2 "
67
- ].join("\n"))
68
- end
69
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Table::Border::ASCII, '#rendering' do
4
-
5
- subject(:border) { described_class.new(column_widths) }
6
-
7
- context 'with empty row' do
8
- let(:row) { TTY::Table::Row.new([]) }
9
- let(:column_widths) { [] }
10
-
11
- it 'draws top line' do
12
- expect(border.top_line).to eq("++")
13
- end
14
-
15
- it 'draws middle line' do
16
- expect(border.separator).to eq("++")
17
- end
18
-
19
- it 'draw bottom line' do
20
- expect(border.bottom_line).to eq("++")
21
- end
22
-
23
- it 'draws row line' do
24
- expect(border.row_line(row)).to eq("||")
25
- end
26
- end
27
-
28
- context 'with row' do
29
- let(:column_widths) { [2,2,2] }
30
- let(:row) { TTY::Table::Row.new(['a1', 'a2', 'a3']) }
31
-
32
- it 'draws top line' do
33
- expect(border.top_line).to eq("+--+--+--+")
34
- end
35
-
36
- it 'draw middle line' do
37
- expect(border.separator).to eq("+--+--+--+")
38
- end
39
-
40
- it 'draw bottom line' do
41
- expect(border.bottom_line).to eq("+--+--+--+")
42
- end
43
-
44
- it 'draws row line' do
45
- expect(border.row_line(row)).to eq("|a1|a2|a3|")
46
- end
47
- end
48
-
49
- context 'with multiline row' do
50
- let(:column_widths) { [2,2,2]}
51
-
52
- context 'with mixed data' do
53
- let(:row) { TTY::Table::Row.new(["a1\nb1\nc1", 'a2', 'a3']) }
54
-
55
- it 'draws row line' do
56
- expect(border.row_line(row)).to eq unindent(<<-EOS)
57
- |a1|a2|a3|
58
- |b1| | |
59
- |c1| | |
60
- EOS
61
- end
62
- end
63
-
64
- context 'with sparse data' do
65
- let(:row) { TTY::Table::Row.new(["a1\n\n", "\na2\n", "\n\na3"]) }
66
-
67
- it 'draws row line' do
68
- expect(border.row_line(row)).to eq unindent(<<-EOS)
69
- |a1| | |
70
- | |a2| |
71
- | | |a3|
72
- EOS
73
- end
74
- end
75
-
76
- context 'with empty data' do
77
- let(:row) { TTY::Table::Row.new(["\na1\n", "\na2\n", "\na3\n"]) }
78
-
79
- it 'draws row line' do
80
- expect(border.row_line(row)).to eq unindent(<<-EOS)
81
- | | | |
82
- |a1|a2|a3|
83
- | | | |
84
- EOS
85
- end
86
- end
87
- end
88
- end