text_chart 0.1.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 +7 -0
- data/lib/text_chart/designer.rb +140 -0
- data/lib/text_chart/size_calculator.rb +76 -0
- data/lib/text_chart/version.rb +5 -0
- data/lib/text_chart.rb +72 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6bcc9a9d069cb1fc77252328c76b956bd69024bde31ae55bde6ce6d99d995be0
|
4
|
+
data.tar.gz: e281cc003c4f57706e66a321e3e107b10d334dabe89a108c09c53d61f570e357
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c4147b7db9d1d4e4006afb8726ec8054b3308fa805e7ae8b57fa70fae0967e73e572303050a05ef756bccc19b6fd861a217f55b637686a99c85acfc7df2a32b
|
7
|
+
data.tar.gz: d42511067db1a0a4e1c459f47bd28fd26f07368c54fa43e3e9ada5b7c09eefb06d28bcfb47b98bc5a6a8fdab7a46218d950b72d1c0d1249ba9f3fc2bda5b990f
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TextChart::Designer
|
4
|
+
# @param [TextChart] text_chart
|
5
|
+
# @param [TextChart::SizeCalculator] size_calc
|
6
|
+
# @param [Array<String>] chart_canvas
|
7
|
+
def initialize(text_chart, size_calc)
|
8
|
+
@text_chart = text_chart
|
9
|
+
@size_calc = size_calc
|
10
|
+
@chart_canvas = build_empty_chart
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Array<String>]
|
14
|
+
def draw_header
|
15
|
+
header = []
|
16
|
+
header << "#{@text_chart.title}\n"
|
17
|
+
header << "Goal: #{@text_chart.goal}\n"
|
18
|
+
header << "\n"
|
19
|
+
|
20
|
+
@chart_canvas.prepend(*header)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<String>]
|
24
|
+
def draw_axis
|
25
|
+
draw_x_axis
|
26
|
+
draw_y_axis
|
27
|
+
draw_references
|
28
|
+
|
29
|
+
@chart_canvas
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<String>]
|
33
|
+
def draw_bars
|
34
|
+
last_line_index = @chart_canvas.size - 1
|
35
|
+
zero_line = last_line_index - @text_chart.size_config(:x_axis_height)
|
36
|
+
chart_line = 0
|
37
|
+
ref_width = @size_calc.calculate_reference_width
|
38
|
+
y_axis_width = @text_chart.size_config(:y_axis_width)
|
39
|
+
first_bar_margin = y_axis_width + @text_chart.size_config(:bar_area_left_margin)
|
40
|
+
middle_bar_margin = @text_chart.size_config(:bar_spacing)
|
41
|
+
bar_row = "###"
|
42
|
+
bar_width = @text_chart.size_config(:bar_width)
|
43
|
+
bar_height = bar_start = bar_end = bar_top = 0
|
44
|
+
|
45
|
+
@text_chart.data.each do |d|
|
46
|
+
# + 1 to guarantee that the bar will always be rendered
|
47
|
+
bar_height = d + 1
|
48
|
+
|
49
|
+
bar_start = if bar_start == 0
|
50
|
+
ref_width + first_bar_margin
|
51
|
+
else
|
52
|
+
# + 1 because bar_end put us on the bar last column
|
53
|
+
bar_end + 1 + middle_bar_margin
|
54
|
+
end
|
55
|
+
|
56
|
+
# - 1 because bar_start already put us on the bar first column
|
57
|
+
bar_end = bar_start + bar_width - 1
|
58
|
+
|
59
|
+
chart_line = zero_line
|
60
|
+
bar_top = bar_height - 1
|
61
|
+
bar_height.times do |t|
|
62
|
+
@chart_canvas[chart_line][bar_start..bar_end] = bar_row
|
63
|
+
|
64
|
+
chart_line -= 1 unless t == bar_top
|
65
|
+
end
|
66
|
+
|
67
|
+
draw_reference_line(chart_line, ref_width + y_axis_width, bar_start)
|
68
|
+
end
|
69
|
+
|
70
|
+
@chart_canvas
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def build_empty_chart
|
76
|
+
chart = []
|
77
|
+
|
78
|
+
number_of_columns = @size_calc.calculate_number_of_columns
|
79
|
+
number_of_rows = @size_calc.calculate_number_of_rows
|
80
|
+
|
81
|
+
empty_row = ""
|
82
|
+
number_of_columns.times { empty_row += " " }
|
83
|
+
empty_row += "\n"
|
84
|
+
|
85
|
+
number_of_rows.times { chart << empty_row.dup }
|
86
|
+
|
87
|
+
chart
|
88
|
+
end
|
89
|
+
|
90
|
+
def draw_x_axis
|
91
|
+
position = @size_calc.calculate_reference_width
|
92
|
+
size = @size_calc.calculate_x_axis_size
|
93
|
+
size.times do
|
94
|
+
@chart_canvas.last[position] = "-"
|
95
|
+
position += 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def draw_y_axis
|
100
|
+
position = 0
|
101
|
+
margin = @size_calc.calculate_reference_width
|
102
|
+
size = @size_calc.calculate_y_axis_size
|
103
|
+
size.times do
|
104
|
+
@chart_canvas[position][margin] = "|"
|
105
|
+
position += 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def draw_references
|
110
|
+
references = @text_chart.refs
|
111
|
+
width = @size_calc.calculate_reference_width
|
112
|
+
number_of_references = references.size
|
113
|
+
ref_size = ref_start = ref_end = nil
|
114
|
+
margin_size = @text_chart.size_config(:reference_and_y_axis_margin)
|
115
|
+
|
116
|
+
number_of_references.times do |i|
|
117
|
+
ref_size = references[i].digits.size
|
118
|
+
|
119
|
+
if ref_size == (width - margin_size)
|
120
|
+
ref_start = 0
|
121
|
+
ref_end = ref_size - 1
|
122
|
+
@chart_canvas[i][ref_start..ref_end] = references[i].to_s
|
123
|
+
else
|
124
|
+
ref_start = ref_size
|
125
|
+
@chart_canvas[i][ref_start] = references[i].to_s
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def draw_reference_line(chart_line, line_start, line_end)
|
131
|
+
current_position = line_start
|
132
|
+
until current_position == line_end
|
133
|
+
if @chart_canvas[chart_line][current_position] != "#"
|
134
|
+
@chart_canvas[chart_line][current_position] = "'"
|
135
|
+
end
|
136
|
+
|
137
|
+
current_position += 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TextChart::SizeCalculator
|
4
|
+
# @param [TextChart] text_chart
|
5
|
+
def initialize(text_chart)
|
6
|
+
@text_chart = text_chart
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [Integer]
|
10
|
+
def calculate_number_of_columns
|
11
|
+
@number_of_columns ||=
|
12
|
+
begin
|
13
|
+
result = 0
|
14
|
+
|
15
|
+
result += calculate_reference_width
|
16
|
+
|
17
|
+
y_axis_width = @text_chart.size_config(:y_axis_width)
|
18
|
+
result += y_axis_width
|
19
|
+
|
20
|
+
left_margin = @text_chart.size_config(:bar_area_left_margin)
|
21
|
+
right_margin = @text_chart.size_config(:bar_area_right_margin)
|
22
|
+
result += left_margin + right_margin
|
23
|
+
|
24
|
+
bar_width = @text_chart.size_config(:bar_width)
|
25
|
+
result += @text_chart.data.size * bar_width
|
26
|
+
|
27
|
+
bar_spacing = @text_chart.size_config(:bar_spacing)
|
28
|
+
# -1 to avoid adding spacing after the last bar.
|
29
|
+
result += (@text_chart.data.size - 1) * bar_spacing
|
30
|
+
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Integer]
|
36
|
+
def calculate_reference_width
|
37
|
+
@reference_width ||=
|
38
|
+
begin
|
39
|
+
biggest_item = @text_chart.data.max
|
40
|
+
biggest_item_width = biggest_item.digits.count
|
41
|
+
reference_margin = @text_chart.size_config(:reference_and_y_axis_margin)
|
42
|
+
biggest_item_width + reference_margin
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Integer]
|
47
|
+
def calculate_number_of_rows
|
48
|
+
@number_of_rows ||=
|
49
|
+
begin
|
50
|
+
result = 0
|
51
|
+
|
52
|
+
x_axis_row = @text_chart.size_config(:x_axis_height)
|
53
|
+
result += x_axis_row
|
54
|
+
|
55
|
+
reference_row = @text_chart.size_config(:reference_row_height)
|
56
|
+
number_of_references = @text_chart.refs.size
|
57
|
+
number_of_references.times { result += reference_row }
|
58
|
+
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Integer]
|
64
|
+
def calculate_x_axis_size
|
65
|
+
@x_axis_size ||= calculate_number_of_columns - calculate_reference_width
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Integer]
|
69
|
+
def calculate_y_axis_size
|
70
|
+
@y_axis_size ||=
|
71
|
+
begin
|
72
|
+
x_axis_row = @text_chart.size_config(:x_axis_height)
|
73
|
+
calculate_number_of_rows - x_axis_row
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/text_chart.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "text_chart/version"
|
4
|
+
require_relative "text_chart/size_calculator"
|
5
|
+
require_relative "text_chart/designer"
|
6
|
+
|
7
|
+
# This is the main class of the text_chart. After calling the `#to_s` method you'll receive a string
|
8
|
+
# representing the chart, just like this one:
|
9
|
+
#
|
10
|
+
# text_chart demonstration
|
11
|
+
# Goal: Show you how cool this is
|
12
|
+
#
|
13
|
+
# 9 |'''''''''''''''''''''''''''''''''''''''''''''''''''''###
|
14
|
+
# 8 | ###
|
15
|
+
# 7 | ###
|
16
|
+
# 6 |'''''''''''''### ###
|
17
|
+
# 5 |'''''''''''''###'''''''''''''''''''''''''''### ###
|
18
|
+
# 4 |'''''''''''''###'''''''''''''''''''''''''''###'''''''###'''''''###
|
19
|
+
# 3 |'''###'''''''###'''''''''''''''''### ### ### ###
|
20
|
+
# 2 | ### ### ### ### ### ###
|
21
|
+
# 1 | ### ### ### ### ### ###
|
22
|
+
# 0 |'''###'''''''###'''''''### ### ### ### ###
|
23
|
+
# ----------------------------------------------------------------------
|
24
|
+
class TextChart
|
25
|
+
class Error < StandardError; end
|
26
|
+
|
27
|
+
# @param [String] title
|
28
|
+
# @param [String] goal
|
29
|
+
# @param [Array] data
|
30
|
+
def initialize(title, goal, data)
|
31
|
+
@title = title
|
32
|
+
@goal = goal
|
33
|
+
@data = data.empty? ? [0] : data
|
34
|
+
@refs = define_references
|
35
|
+
@size_calculator = SizeCalculator.new(self)
|
36
|
+
@designer = Designer.new(self, @size_calculator)
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :title, :goal, :refs, :data, :size_calculator, :designer
|
40
|
+
|
41
|
+
# @return [String]
|
42
|
+
def to_s
|
43
|
+
@designer.draw_axis
|
44
|
+
@designer.draw_bars
|
45
|
+
@designer.draw_header.join
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Symbol] key
|
49
|
+
# @return [Integer]
|
50
|
+
def size_config(key)
|
51
|
+
SIZE_CONFIG[key]
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
SIZE_CONFIG = {
|
57
|
+
y_axis_width: 1,
|
58
|
+
bar_area_left_margin: 3,
|
59
|
+
bar_area_right_margin: 3,
|
60
|
+
bar_width: 3,
|
61
|
+
bar_spacing: 7,
|
62
|
+
reference_and_y_axis_margin: 1,
|
63
|
+
x_axis_height: 1,
|
64
|
+
reference_row_height: 1
|
65
|
+
}
|
66
|
+
|
67
|
+
def define_references
|
68
|
+
r = [*@data.min..@data.max].reverse
|
69
|
+
r << 0 unless r.include?(0)
|
70
|
+
r
|
71
|
+
end
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: text_chart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gustavo Ribeiro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-12-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- grdev@tutanota.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/text_chart.rb
|
21
|
+
- lib/text_chart/designer.rb
|
22
|
+
- lib/text_chart/size_calculator.rb
|
23
|
+
- lib/text_chart/version.rb
|
24
|
+
homepage: https://github.com/gustavothecoder/text_chart
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata:
|
28
|
+
homepage_uri: https://github.com/gustavothecoder/text_chart
|
29
|
+
source_code_uri: https://github.com/gustavothecoder/text_chart
|
30
|
+
changelog_uri: https://github.com/gustavothecoder/text_chart/blob/main/CHANGELOG.md
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.2.33
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Generate text-based charts.
|
50
|
+
test_files: []
|