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: []
|