ruby-charts 0.1.0 → 0.1.2a
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/LICENSE +21 -0
- data/README.md +0 -0
- data/lib/ruby_charts/builder.rb +66 -0
- data/lib/ruby_charts/charts/bar_chart.rb +46 -55
- data/lib/ruby_charts/charts/base_chart.rb +98 -50
- data/lib/ruby_charts/charts/line_chart.rb +46 -38
- data/lib/ruby_charts/charts/pie_chart.rb +42 -29
- data/lib/ruby_charts/config.rb +38 -0
- data/lib/ruby_charts/data_loader.rb +0 -49
- data/lib/ruby_charts/loaders/csv_loader.rb +24 -0
- data/lib/ruby_charts/loaders/xlsx_loader.rb +34 -0
- data/lib/ruby_charts/loaders/yaml_loader.rb +40 -0
- data/lib/ruby_charts/version.rb +5 -0
- data/lib/ruby_charts.rb +35 -86
- data/spec/ruby_charts_spec.rb +0 -0
- metadata +43 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9173b29f6224a11f6a5e757fb3addf9e4676ebcbd5412764640ee16d735dc179
|
|
4
|
+
data.tar.gz: ad0a799a726e55c4dab6cb33e7e2236ebfe24b2b7f947779f59f320b7b077ca2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7cfbb418630b403d324b4309efb5e693a0b78d9955d12761a9226c8d7c344d710c6917e0d203f6848919a666dff36873fd921740acd46f3b1dfe5638927dd029
|
|
7
|
+
data.tar.gz: 18ad9c8e29bff49c5532ad1587eb466e17fe3a275f50e37f2e761c608ad0318abfee226e026aabc73bc793049f4786281c39b19a4866b51591fd9219c2aeadb8
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Giménez Silva Germán Alberto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# lib/ruby_charts/builder.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
class Builder
|
|
5
|
+
def initialize(data)
|
|
6
|
+
@data = data
|
|
7
|
+
@chart_type = :bar
|
|
8
|
+
@title = 'Chart'
|
|
9
|
+
@subtitle = nil
|
|
10
|
+
@colors = nil
|
|
11
|
+
@legend = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def type(chart_type)
|
|
15
|
+
@chart_type = chart_type
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def title(text)
|
|
20
|
+
@title = text
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def subtitle(text)
|
|
25
|
+
@subtitle = text
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def colors(*color_array)
|
|
30
|
+
@colors = color_array.flatten
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def legend(position: :right)
|
|
35
|
+
@legend = position
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def save(filename)
|
|
40
|
+
chart = build_chart
|
|
41
|
+
chart.title(@title)
|
|
42
|
+
chart.subtitle(@subtitle)
|
|
43
|
+
chart.colors(*@colors) if @colors
|
|
44
|
+
chart.legend(position: @legend) if @legend
|
|
45
|
+
|
|
46
|
+
chart.save(filename)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def build_chart
|
|
52
|
+
case @chart_type
|
|
53
|
+
when :pie
|
|
54
|
+
Charts::PieChart.new(@data)
|
|
55
|
+
when :bar, :vertical_bar
|
|
56
|
+
Charts::BarChart.new(@data, :vertical)
|
|
57
|
+
when :horizontal_bar
|
|
58
|
+
Charts::BarChart.new(@data, :horizontal)
|
|
59
|
+
when :line
|
|
60
|
+
Charts::LineChart.new(@data)
|
|
61
|
+
else
|
|
62
|
+
raise "Unknown chart type: #{@chart_type}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib/ruby_charts/charts/bar_chart.rb
|
|
2
2
|
|
|
3
3
|
module RubyCharts
|
|
4
4
|
module Charts
|
|
@@ -18,7 +18,7 @@ module RubyCharts
|
|
|
18
18
|
self
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
protected
|
|
22
22
|
|
|
23
23
|
def draw_chart_content
|
|
24
24
|
if @orientation == :horizontal
|
|
@@ -28,96 +28,74 @@ module RubyCharts
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
private
|
|
32
|
+
|
|
31
33
|
def draw_vertical_bars
|
|
32
34
|
chart_width = @options[:width] - (2 * @options[:padding])
|
|
33
35
|
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
34
36
|
|
|
35
|
-
max_value = @data[:values].max
|
|
36
|
-
bar_width = chart_width / @data[:labels].length * 0.
|
|
37
|
+
max_value = @data[:values].max.to_f
|
|
38
|
+
bar_width = (chart_width / @data[:labels].length) * 0.6
|
|
37
39
|
spacing = chart_width / @data[:labels].length
|
|
38
40
|
|
|
39
41
|
@data[:labels].each_with_index do |label, index|
|
|
40
42
|
value = @data[:values][index]
|
|
41
|
-
bar_height = (value / max_value
|
|
43
|
+
bar_height = (value / max_value) * chart_height
|
|
42
44
|
|
|
43
45
|
x1 = @options[:padding] + (index * spacing) + ((spacing - bar_width) / 2)
|
|
44
46
|
y1 = @options[:height] - @options[:padding] - bar_height.to_i
|
|
45
47
|
x2 = x1 + bar_width.to_i
|
|
46
48
|
y2 = @options[:height] - @options[:padding]
|
|
47
49
|
|
|
48
|
-
color =
|
|
50
|
+
color = get_color(index)
|
|
49
51
|
|
|
50
52
|
# Draw bar
|
|
51
53
|
@img.filled_rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
|
54
|
+
@img.rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, [100, 100, 100])
|
|
52
55
|
|
|
53
|
-
# Draw label
|
|
54
|
-
|
|
55
|
-
x: x1.to_i + 5,
|
|
56
|
-
y: y2 + 10,
|
|
57
|
-
size: 10,
|
|
58
|
-
color: [50, 50, 50]
|
|
59
|
-
})
|
|
56
|
+
# Draw value label on top of bar
|
|
57
|
+
draw_text(value.to_i.to_s, x1.to_i + 5, y1 - 10, 10, [50, 50, 50])
|
|
60
58
|
|
|
61
|
-
# Draw
|
|
62
|
-
|
|
63
|
-
label_text = "#{(value / @data[:values].sum.to_f * 100).round(1)}%"
|
|
64
|
-
else
|
|
65
|
-
label_text = value.to_s
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
@img.text(label_text, {
|
|
69
|
-
x: x1.to_i + 10,
|
|
70
|
-
y: y1 - 15,
|
|
71
|
-
size: 10,
|
|
72
|
-
color: [50, 50, 50]
|
|
73
|
-
})
|
|
59
|
+
# Draw label below bar
|
|
60
|
+
draw_text(label, x1.to_i, y2 + 20, 10, [50, 50, 50])
|
|
74
61
|
end
|
|
75
62
|
|
|
76
63
|
# Draw axes
|
|
77
|
-
draw_axes
|
|
64
|
+
draw_axes(max_value)
|
|
78
65
|
end
|
|
79
66
|
|
|
80
67
|
def draw_horizontal_bars
|
|
81
|
-
chart_width = @options[:width] - (2 * @options[:padding])
|
|
68
|
+
chart_width = @options[:width] - (2 * @options[:padding]) - 150
|
|
82
69
|
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
83
70
|
|
|
84
|
-
max_value = @data[:values].max
|
|
85
|
-
bar_height = chart_height / @data[:labels].length * 0.
|
|
71
|
+
max_value = @data[:values].max.to_f
|
|
72
|
+
bar_height = (chart_height / @data[:labels].length) * 0.6
|
|
86
73
|
spacing = chart_height / @data[:labels].length
|
|
87
74
|
|
|
88
75
|
@data[:labels].each_with_index do |label, index|
|
|
89
76
|
value = @data[:values][index]
|
|
90
|
-
bar_width = (value / max_value
|
|
77
|
+
bar_width = (value / max_value) * chart_width
|
|
91
78
|
|
|
92
|
-
y1 = @options[:padding] + (index * spacing) + ((spacing - bar_height) / 2)
|
|
93
|
-
x1 = @options[:padding]
|
|
79
|
+
y1 = @options[:padding] + 50 + (index * spacing) + ((spacing - bar_height) / 2)
|
|
80
|
+
x1 = @options[:padding] + 150
|
|
94
81
|
y2 = y1 + bar_height.to_i
|
|
95
82
|
x2 = x1 + bar_width.to_i
|
|
96
83
|
|
|
97
|
-
color =
|
|
84
|
+
color = get_color(index)
|
|
98
85
|
|
|
99
86
|
# Draw bar
|
|
100
87
|
@img.filled_rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
|
88
|
+
@img.rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, [100, 100, 100])
|
|
101
89
|
|
|
102
|
-
# Draw label
|
|
103
|
-
|
|
104
|
-
x: @options[:padding] - 70,
|
|
105
|
-
y: y1.to_i + 5,
|
|
106
|
-
size: 10,
|
|
107
|
-
color: [50, 50, 50]
|
|
108
|
-
})
|
|
90
|
+
# Draw label on left
|
|
91
|
+
draw_text(label, 20, y1.to_i + 15, 10, [50, 50, 50])
|
|
109
92
|
|
|
110
|
-
# Draw value
|
|
111
|
-
|
|
112
|
-
x: x2 + 5,
|
|
113
|
-
y: y1.to_i + 5,
|
|
114
|
-
size: 10,
|
|
115
|
-
color: [50, 50, 50]
|
|
116
|
-
})
|
|
93
|
+
# Draw value on right
|
|
94
|
+
draw_text(value.to_i.to_s, x2 + 10, y1.to_i + 15, 10, [50, 50, 50])
|
|
117
95
|
end
|
|
118
96
|
end
|
|
119
97
|
|
|
120
|
-
def draw_axes
|
|
98
|
+
def draw_axes(max_val)
|
|
121
99
|
# X axis
|
|
122
100
|
@img.line(
|
|
123
101
|
@options[:padding],
|
|
@@ -130,16 +108,29 @@ module RubyCharts
|
|
|
130
108
|
# Y axis
|
|
131
109
|
@img.line(
|
|
132
110
|
@options[:padding],
|
|
133
|
-
@options[:padding],
|
|
111
|
+
@options[:padding] + 70,
|
|
134
112
|
@options[:padding],
|
|
135
113
|
@options[:height] - @options[:padding],
|
|
136
114
|
[0, 0, 0]
|
|
137
115
|
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
116
|
+
|
|
117
|
+
# Grid lines and Y labels
|
|
118
|
+
5.times do |i|
|
|
119
|
+
value = (max_val * i / 4).to_i
|
|
120
|
+
y = @options[:height] - @options[:padding] - (i * (@options[:height] - 2 * @options[:padding] - 100) / 4)
|
|
121
|
+
|
|
122
|
+
# Grid line
|
|
123
|
+
@img.line(
|
|
124
|
+
@options[:padding],
|
|
125
|
+
y.to_i,
|
|
126
|
+
@options[:width] - @options[:padding],
|
|
127
|
+
y.to_i,
|
|
128
|
+
[230, 230, 230]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Y label
|
|
132
|
+
draw_text(value.to_s, 20, y.to_i + 5, 9, [100, 100, 100])
|
|
133
|
+
end
|
|
143
134
|
end
|
|
144
135
|
end
|
|
145
136
|
end
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib/ruby_charts/charts/base_chart.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../config'
|
|
2
4
|
|
|
3
5
|
module RubyCharts
|
|
4
6
|
module Charts
|
|
5
7
|
class BaseChart
|
|
8
|
+
COLORS = [
|
|
9
|
+
[255, 107, 107], # Red
|
|
10
|
+
[78, 205, 196], # Teal
|
|
11
|
+
[69, 183, 209], # Blue
|
|
12
|
+
[255, 160, 122], # Orange
|
|
13
|
+
[154, 216, 200], # Mint
|
|
14
|
+
[247, 220, 111], # Yellow
|
|
15
|
+
[187, 143, 206], # Purple
|
|
16
|
+
[133, 193, 226], # Light Blue
|
|
17
|
+
[248, 184, 139], # Peach
|
|
18
|
+
[170, 190, 198] # Gray
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
6
21
|
attr_accessor :data, :options
|
|
7
22
|
|
|
8
23
|
def initialize(data)
|
|
@@ -12,10 +27,10 @@ module RubyCharts
|
|
|
12
27
|
height: 700,
|
|
13
28
|
title: 'Chart',
|
|
14
29
|
subtitle: nil,
|
|
15
|
-
|
|
16
|
-
colors: default_colors,
|
|
30
|
+
colors: COLORS,
|
|
17
31
|
padding: 80,
|
|
18
|
-
|
|
32
|
+
font: Config.font(:regular),
|
|
33
|
+
font_bold: Config.font(:bold)
|
|
19
34
|
}
|
|
20
35
|
end
|
|
21
36
|
|
|
@@ -29,23 +44,13 @@ module RubyCharts
|
|
|
29
44
|
self
|
|
30
45
|
end
|
|
31
46
|
|
|
32
|
-
def
|
|
33
|
-
@options[:
|
|
34
|
-
self
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def height(h)
|
|
38
|
-
@options[:height] = h
|
|
39
|
-
self
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def colors(color_array)
|
|
43
|
-
@options[:colors] = color_array
|
|
47
|
+
def colors(*color_array)
|
|
48
|
+
@options[:colors] = color_array.flatten
|
|
44
49
|
self
|
|
45
50
|
end
|
|
46
51
|
|
|
47
|
-
def
|
|
48
|
-
@options[:
|
|
52
|
+
def font(path)
|
|
53
|
+
@options[:font] = path
|
|
49
54
|
self
|
|
50
55
|
end
|
|
51
56
|
|
|
@@ -61,59 +66,102 @@ module RubyCharts
|
|
|
61
66
|
draw_background
|
|
62
67
|
draw_title
|
|
63
68
|
draw_chart_content
|
|
64
|
-
draw_legend if @options[:legend]
|
|
65
69
|
|
|
66
70
|
@img
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
def
|
|
70
|
-
render.
|
|
73
|
+
def save(filename)
|
|
74
|
+
render.save(filename)
|
|
71
75
|
puts "✓ Chart saved: #{filename}"
|
|
76
|
+
filename
|
|
72
77
|
end
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
# Requires ruby-pdf integration
|
|
76
|
-
render_to_pdf(filename)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
private
|
|
80
|
-
|
|
81
|
-
def default_colors
|
|
82
|
-
[
|
|
83
|
-
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8',
|
|
84
|
-
'#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B88B', '#ABEBC6'
|
|
85
|
-
]
|
|
86
|
-
end
|
|
79
|
+
protected
|
|
87
80
|
|
|
88
81
|
def draw_background
|
|
89
|
-
@img.filled_rectangle(
|
|
82
|
+
@img.filled_rectangle(
|
|
83
|
+
0, 0,
|
|
84
|
+
@options[:width] - 1,
|
|
85
|
+
@options[:height] - 1,
|
|
86
|
+
[255, 255, 255]
|
|
87
|
+
)
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
@img.rectangle(
|
|
90
|
+
0, 0,
|
|
91
|
+
@options[:width] - 1,
|
|
92
|
+
@options[:height] - 1,
|
|
93
|
+
[200, 200, 200]
|
|
94
|
+
)
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
def draw_title
|
|
96
|
-
#
|
|
97
|
-
@img.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
# Title background
|
|
99
|
+
@img.filled_rectangle(
|
|
100
|
+
0, 0,
|
|
101
|
+
@options[:width] - 1,
|
|
102
|
+
70,
|
|
103
|
+
[250, 250, 250]
|
|
104
|
+
)
|
|
103
105
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
# Title border
|
|
107
|
+
@img.line(0, 70, @options[:width] - 1, 70, [200, 200, 200])
|
|
108
|
+
|
|
109
|
+
# Title text
|
|
110
|
+
if File.exist?(@options[:font_bold])
|
|
111
|
+
@img.text_ft(
|
|
112
|
+
@options[:title],
|
|
107
113
|
x: @options[:padding],
|
|
108
114
|
y: 50,
|
|
109
|
-
|
|
115
|
+
font: @options[:font_bold],
|
|
116
|
+
size: 28,
|
|
117
|
+
color: [30, 30, 30]
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Subtitle text
|
|
122
|
+
if @options[:subtitle] && File.exist?(@options[:font])
|
|
123
|
+
@img.text_ft(
|
|
124
|
+
@options[:subtitle],
|
|
125
|
+
x: @options[:padding],
|
|
126
|
+
y: 65,
|
|
127
|
+
font: @options[:font],
|
|
128
|
+
size: 12,
|
|
110
129
|
color: [100, 100, 100]
|
|
111
|
-
|
|
130
|
+
)
|
|
112
131
|
end
|
|
113
132
|
end
|
|
114
133
|
|
|
115
134
|
def draw_chart_content
|
|
116
|
-
raise NotImplementedError,
|
|
135
|
+
raise NotImplementedError, 'Subclasses must implement draw_chart_content'
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def get_color(index)
|
|
139
|
+
@options[:colors][index % @options[:colors].length]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def draw_text(text, x, y, size, color)
|
|
143
|
+
if File.exist?(@options[:font])
|
|
144
|
+
@img.text_ft(
|
|
145
|
+
text,
|
|
146
|
+
x: x,
|
|
147
|
+
y: y,
|
|
148
|
+
font: @options[:font],
|
|
149
|
+
size: size,
|
|
150
|
+
color: color
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def measure_text(text, size)
|
|
156
|
+
if File.exist?(@options[:font])
|
|
157
|
+
@img.text_bbox(
|
|
158
|
+
text,
|
|
159
|
+
font: @options[:font],
|
|
160
|
+
size: size
|
|
161
|
+
)
|
|
162
|
+
else
|
|
163
|
+
[text.length * size / 2, size]
|
|
164
|
+
end
|
|
117
165
|
end
|
|
118
166
|
end
|
|
119
167
|
end
|
|
@@ -1,84 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib/ruby_charts/charts/line_chart.rb
|
|
2
2
|
|
|
3
3
|
module RubyCharts
|
|
4
4
|
module Charts
|
|
5
5
|
class LineChart < BaseChart
|
|
6
|
-
|
|
6
|
+
protected
|
|
7
7
|
|
|
8
8
|
def draw_chart_content
|
|
9
9
|
chart_width = @options[:width] - (2 * @options[:padding])
|
|
10
10
|
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
11
11
|
|
|
12
|
-
max_value = @data[:values].max
|
|
13
|
-
min_value = @data[:values].min
|
|
12
|
+
max_value = @data[:values].max.to_f
|
|
13
|
+
min_value = @data[:values].min.to_f
|
|
14
14
|
range = max_value - min_value
|
|
15
|
+
range = 1 if range == 0
|
|
15
16
|
|
|
16
17
|
# Draw grid
|
|
17
|
-
draw_grid(chart_width, chart_height
|
|
18
|
+
draw_grid(chart_width, chart_height)
|
|
18
19
|
|
|
19
|
-
#
|
|
20
|
+
# Calculate points
|
|
20
21
|
points = @data[:values].each_with_index.map do |value, index|
|
|
21
|
-
x = @options[:padding] + (index * chart_width / (@data[:values].length - 1))
|
|
22
|
-
y = @options[:height] - @options[:padding] - ((value - min_value) / range
|
|
22
|
+
x = @options[:padding] + (index * chart_width / ([@data[:values].length - 1, 1].max))
|
|
23
|
+
y = @options[:height] - @options[:padding] - ((value - min_value) / range * chart_height)
|
|
23
24
|
[x.to_i, y.to_i]
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
color =
|
|
27
|
+
color = get_color(0)
|
|
27
28
|
|
|
28
29
|
# Draw line segments
|
|
29
30
|
points.each_cons(2) do |p1, p2|
|
|
30
31
|
@img.line(p1[0], p1[1], p2[0], p2[1], color)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
# Draw points
|
|
34
|
+
# Draw points as circles
|
|
34
35
|
points.each_with_index do |point, index|
|
|
35
|
-
@img.filled_circle(point[0], point[1],
|
|
36
|
+
@img.filled_circle(point[0], point[1], 5, color)
|
|
37
|
+
@img.circle(point[0], point[1], 5, [100, 100, 100])
|
|
36
38
|
|
|
37
39
|
# Draw label
|
|
38
|
-
|
|
39
|
-
x: point[0] - 20,
|
|
40
|
-
y: point[1] - 20,
|
|
41
|
-
size: 9,
|
|
42
|
-
color: [50, 50, 50]
|
|
43
|
-
})
|
|
40
|
+
draw_text(@data[:labels][index], point[0] - 20, point[1] - 20, 9, [50, 50, 50])
|
|
44
41
|
end
|
|
45
42
|
|
|
46
43
|
# Draw axes
|
|
47
44
|
draw_axes(max_value, min_value)
|
|
48
45
|
end
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def draw_grid(width, height)
|
|
51
50
|
# Horizontal grid lines
|
|
52
51
|
5.times do |i|
|
|
53
|
-
y = @options[:padding] +
|
|
54
|
-
@img.line(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
y = @options[:padding] + 70 + (i * height / 4)
|
|
53
|
+
@img.line(
|
|
54
|
+
@options[:padding],
|
|
55
|
+
y,
|
|
56
|
+
@options[:width] - @options[:padding],
|
|
57
|
+
y,
|
|
58
|
+
[230, 230, 230]
|
|
59
|
+
)
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def draw_axes(max_val, min_val)
|
|
64
|
+
# X axis
|
|
65
|
+
@img.line(
|
|
66
|
+
@options[:padding],
|
|
67
|
+
@options[:height] - @options[:padding],
|
|
68
|
+
@options[:width] - @options[:padding],
|
|
69
|
+
@options[:height] - @options[:padding],
|
|
70
|
+
[0, 0, 0]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Y axis
|
|
74
|
+
@img.line(
|
|
75
|
+
@options[:padding],
|
|
76
|
+
@options[:padding] + 70,
|
|
77
|
+
@options[:padding],
|
|
78
|
+
@options[:height] - @options[:padding],
|
|
79
|
+
[0, 0, 0]
|
|
80
|
+
)
|
|
81
|
+
|
|
65
82
|
# Y axis labels
|
|
66
83
|
5.times do |i|
|
|
67
|
-
value = min_val + ((max_val - min_val) * i / 4)
|
|
84
|
+
value = (min_val + ((max_val - min_val) * i / 4)).to_i
|
|
68
85
|
y = @options[:height] - @options[:padding] - (i * (@options[:height] - 2 * @options[:padding] - 100) / 4)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
y: y.to_i - 7,
|
|
72
|
-
size: 9,
|
|
73
|
-
color: [100, 100, 100]
|
|
74
|
-
})
|
|
86
|
+
|
|
87
|
+
draw_text(value.to_s, 20, y.to_i + 5, 9, [100, 100, 100])
|
|
75
88
|
end
|
|
76
89
|
end
|
|
77
|
-
|
|
78
|
-
def hex_to_rgb(hex)
|
|
79
|
-
hex = hex.delete('#')
|
|
80
|
-
[hex[0..1], hex[2..3], hex[4..5]].map { |x| x.to_i(16) }
|
|
81
|
-
end
|
|
82
90
|
end
|
|
83
91
|
end
|
|
84
92
|
end
|
|
@@ -1,62 +1,65 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib/ruby_charts/charts/pie_chart.rb
|
|
2
2
|
|
|
3
3
|
module RubyCharts
|
|
4
4
|
module Charts
|
|
5
5
|
class PieChart < BaseChart
|
|
6
|
-
|
|
6
|
+
protected
|
|
7
7
|
|
|
8
8
|
def draw_chart_content
|
|
9
9
|
chart_area_width = @options[:width] - (2 * @options[:padding])
|
|
10
|
-
chart_area_height = @options[:height] - (2 * @options[:padding]) -
|
|
10
|
+
chart_area_height = @options[:height] - (2 * @options[:padding]) - 120
|
|
11
11
|
|
|
12
12
|
center_x = @options[:padding] + (chart_area_width / 2)
|
|
13
|
-
center_y = @options[:padding] +
|
|
13
|
+
center_y = @options[:padding] + 70 + (chart_area_height / 2)
|
|
14
14
|
radius = [chart_area_width, chart_area_height].min / 3
|
|
15
15
|
|
|
16
|
-
total = @data[:values].sum
|
|
17
|
-
current_angle =
|
|
16
|
+
total = @data[:values].sum.to_f
|
|
17
|
+
current_angle = -90 # Start from top
|
|
18
18
|
|
|
19
19
|
@data[:labels].each_with_index do |label, index|
|
|
20
20
|
value = @data[:values][index]
|
|
21
|
-
percentage = (value / total
|
|
21
|
+
percentage = (value / total) * 100
|
|
22
22
|
angle = (percentage / 100.0) * 360
|
|
23
23
|
|
|
24
|
-
color =
|
|
24
|
+
color = get_color(index)
|
|
25
25
|
|
|
26
|
-
# Draw pie slice
|
|
26
|
+
# Draw pie slice using polygon
|
|
27
27
|
draw_pie_slice(center_x, center_y, radius, current_angle, angle, color)
|
|
28
28
|
|
|
29
|
-
# Draw label
|
|
30
|
-
|
|
31
|
-
label_text = "#{percentage.round(1)}%"
|
|
32
|
-
else
|
|
33
|
-
label_text = "#{label}: #{value}"
|
|
34
|
-
end
|
|
29
|
+
# Draw percentage label
|
|
30
|
+
draw_slice_label(center_x, center_y, radius, current_angle, angle, percentage)
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
# Draw legend
|
|
33
|
+
draw_legend_item(index, label, percentage, color)
|
|
37
34
|
|
|
38
35
|
current_angle += angle
|
|
39
36
|
end
|
|
40
37
|
end
|
|
41
38
|
|
|
39
|
+
private
|
|
40
|
+
|
|
42
41
|
def draw_pie_slice(cx, cy, radius, start_angle, angle, color)
|
|
43
|
-
#
|
|
44
|
-
# Using ruby-libgd polygon
|
|
42
|
+
# Build polygon points for pie slice
|
|
45
43
|
points = [[cx, cy]]
|
|
46
44
|
|
|
47
|
-
steps = (angle / 5).ceil
|
|
45
|
+
steps = (angle / 5).ceil
|
|
48
46
|
(0..steps).each do |i|
|
|
49
47
|
current_angle = start_angle + (angle * i / steps)
|
|
50
48
|
rad = (current_angle * Math::PI / 180)
|
|
49
|
+
|
|
51
50
|
x = (cx + radius * Math.cos(rad)).to_i
|
|
52
51
|
y = (cy + radius * Math.sin(rad)).to_i
|
|
53
52
|
points << [x, y]
|
|
54
53
|
end
|
|
55
54
|
|
|
55
|
+
# Draw filled polygon (pie slice)
|
|
56
56
|
@img.filled_polygon(points, color)
|
|
57
|
+
|
|
58
|
+
# Draw border
|
|
59
|
+
@img.polygon(points, [100, 100, 100])
|
|
57
60
|
end
|
|
58
61
|
|
|
59
|
-
def
|
|
62
|
+
def draw_slice_label(cx, cy, radius, start_angle, angle, percentage)
|
|
60
63
|
label_angle = start_angle + (angle / 2)
|
|
61
64
|
label_radius = radius * 0.65
|
|
62
65
|
|
|
@@ -64,17 +67,27 @@ module RubyCharts
|
|
|
64
67
|
label_x = (cx + label_radius * Math.cos(rad)).to_i
|
|
65
68
|
label_y = (cy + label_radius * Math.sin(rad)).to_i
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
size: 11,
|
|
71
|
-
color: [255, 255, 255]
|
|
72
|
-
})
|
|
70
|
+
text = "#{percentage.round(1)}%"
|
|
71
|
+
|
|
72
|
+
draw_text(text, label_x - 25, label_y, 11, [255, 255, 255])
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
def draw_legend_item(index, label, percentage, color)
|
|
76
|
+
# Legend at bottom
|
|
77
|
+
items_per_row = 3
|
|
78
|
+
row = index / items_per_row
|
|
79
|
+
col = index % items_per_row
|
|
80
|
+
|
|
81
|
+
legend_x = @options[:padding] + (col * (@options[:width] - 2 * @options[:padding]) / 3)
|
|
82
|
+
legend_y = @options[:height] - 50 + (row * 25)
|
|
83
|
+
|
|
84
|
+
# Legend color box
|
|
85
|
+
@img.filled_rectangle(legend_x, legend_y, legend_x + 15, legend_y + 15, color)
|
|
86
|
+
@img.rectangle(legend_x, legend_y, legend_x + 15, legend_y + 15, [100, 100, 100])
|
|
87
|
+
|
|
88
|
+
# Legend text
|
|
89
|
+
text = "#{label}: #{percentage.round(1)}%"
|
|
90
|
+
draw_text(text, legend_x + 25, legend_y + 13, 10, [50, 50, 50])
|
|
78
91
|
end
|
|
79
92
|
end
|
|
80
93
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# lib/ruby_charts/config.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
class Config
|
|
5
|
+
# Default system fonts (adjust for your OS)
|
|
6
|
+
DEFAULT_FONTS = {
|
|
7
|
+
regular: '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
|
8
|
+
bold: '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
# macOS alternatives
|
|
12
|
+
MACOS_FONTS = {
|
|
13
|
+
regular: '/Library/Fonts/Arial.ttf',
|
|
14
|
+
bold: '/Library/Fonts/Arial Bold.ttf'
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# Windows alternatives
|
|
18
|
+
WINDOWS_FONTS = {
|
|
19
|
+
regular: 'C:\\Windows\\Fonts\\arial.ttf',
|
|
20
|
+
bold: 'C:\\Windows\\Fonts\\arialbd.ttf'
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def self.font(style = :regular)
|
|
24
|
+
case RUBY_PLATFORM
|
|
25
|
+
when /darwin/
|
|
26
|
+
MACOS_FONTS[style]
|
|
27
|
+
when /mswin|mingw/
|
|
28
|
+
WINDOWS_FONTS[style]
|
|
29
|
+
else
|
|
30
|
+
DEFAULT_FONTS[style]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.font_exists?(path)
|
|
35
|
+
File.exist?(path)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# app/lib/ruby_charts/data_loader.rb
|
|
2
|
-
|
|
3
|
-
module RubyCharts
|
|
4
|
-
class DataLoader
|
|
5
|
-
def self.from_csv(filepath, options = {})
|
|
6
|
-
data = CSV.read(filepath, headers: true)
|
|
7
|
-
parse_data(data, options)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def self.from_xlsx(filepath, options = {})
|
|
11
|
-
require 'roo'
|
|
12
|
-
workbook = Roo::Excelx.new(filepath)
|
|
13
|
-
sheet = options[:sheet] || workbook.default_sheet
|
|
14
|
-
workbook.sheet(sheet)
|
|
15
|
-
|
|
16
|
-
headers = workbook.row(1)
|
|
17
|
-
rows = (2..workbook.last_row).map { |i| workbook.row(i) }
|
|
18
|
-
|
|
19
|
-
parse_rows(headers, rows, options)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def self.from_yaml(filepath, options = {})
|
|
23
|
-
data = YAML.safe_load_file(filepath)
|
|
24
|
-
parse_data(data, options)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def self.parse_data(data, options)
|
|
30
|
-
{
|
|
31
|
-
labels: data.map { |row| row[0] },
|
|
32
|
-
values: data.map { |row| row[1] },
|
|
33
|
-
series: extract_series(data, options)
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def self.extract_series(data, options)
|
|
38
|
-
# Para multi-series charts
|
|
39
|
-
if data.first.is_a?(Hash) && data.first.keys.length > 2
|
|
40
|
-
data.first.keys.map do |key|
|
|
41
|
-
{
|
|
42
|
-
name: key,
|
|
43
|
-
values: data.map { |row| row[key] }
|
|
44
|
-
}
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# lib/ruby_charts/loaders/csv_loader.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Loaders
|
|
5
|
+
class CSVLoader
|
|
6
|
+
def self.load(filepath, options = {})
|
|
7
|
+
raise "File not found: #{filepath}" unless File.exist?(filepath)
|
|
8
|
+
|
|
9
|
+
rows = CSV.read(filepath)
|
|
10
|
+
headers = rows[0]
|
|
11
|
+
data_rows = rows[1..-1]
|
|
12
|
+
|
|
13
|
+
labels = data_rows.map { |row| row[0] }
|
|
14
|
+
values = data_rows.map { |row| row[1].to_f }
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
labels: labels,
|
|
18
|
+
values: values,
|
|
19
|
+
type: options[:type] || :bar
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# lib/ruby_charts/loaders/xlsx_loader.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Loaders
|
|
5
|
+
class XLSXLoader
|
|
6
|
+
def self.load(filepath, options = {})
|
|
7
|
+
raise "File not found: #{filepath}" unless File.exist?(filepath)
|
|
8
|
+
|
|
9
|
+
workbook = Roo::Excelx.new(filepath)
|
|
10
|
+
sheet = options[:sheet] || workbook.default_sheet
|
|
11
|
+
workbook.sheet(sheet)
|
|
12
|
+
|
|
13
|
+
labels = []
|
|
14
|
+
values = []
|
|
15
|
+
|
|
16
|
+
(2..workbook.last_row).each do |row|
|
|
17
|
+
label = workbook.cell(row, 1)
|
|
18
|
+
value = workbook.cell(row, 2)
|
|
19
|
+
|
|
20
|
+
next if label.nil? || value.nil?
|
|
21
|
+
|
|
22
|
+
labels << label.to_s
|
|
23
|
+
values << value.to_f
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
labels: labels,
|
|
28
|
+
values: values,
|
|
29
|
+
type: options[:type] || :bar
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# lib/ruby_charts/loaders/yaml_loader.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Loaders
|
|
5
|
+
class YAMLLoader
|
|
6
|
+
def self.load(filepath, options = {})
|
|
7
|
+
raise "File not found: #{filepath}" unless File.exist?(filepath)
|
|
8
|
+
|
|
9
|
+
data = YAML.safe_load_file(filepath)
|
|
10
|
+
|
|
11
|
+
if data.is_a?(Hash) && data['data']
|
|
12
|
+
data = data['data']
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
labels = []
|
|
16
|
+
values = []
|
|
17
|
+
|
|
18
|
+
if data.is_a?(Array)
|
|
19
|
+
data.each do |item|
|
|
20
|
+
if item.is_a?(Hash)
|
|
21
|
+
labels << item['name'] || item['label']
|
|
22
|
+
values << (item['value'] || item['amount']).to_f
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
elsif data.is_a?(Hash)
|
|
26
|
+
data.each do |key, value|
|
|
27
|
+
labels << key
|
|
28
|
+
values << value.to_f
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
labels: labels,
|
|
34
|
+
values: values,
|
|
35
|
+
type: options[:type] || :bar
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/ruby_charts.rb
CHANGED
|
@@ -1,90 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib/ruby_charts.rb
|
|
2
|
+
|
|
3
|
+
require 'gd'
|
|
4
|
+
require 'csv'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require 'roo'
|
|
7
|
+
|
|
8
|
+
require_relative 'ruby_charts/version'
|
|
9
|
+
require_relative 'ruby_charts/loaders/csv_loader'
|
|
10
|
+
require_relative 'ruby_charts/loaders/xlsx_loader'
|
|
11
|
+
require_relative 'ruby_charts/loaders/yaml_loader'
|
|
12
|
+
require_relative 'ruby_charts/charts/base_chart'
|
|
13
|
+
require_relative 'ruby_charts/charts/pie_chart'
|
|
14
|
+
require_relative 'ruby_charts/charts/bar_chart'
|
|
15
|
+
require_relative 'ruby_charts/charts/line_chart'
|
|
16
|
+
require_relative 'ruby_charts/builder'
|
|
2
17
|
|
|
3
18
|
module RubyCharts
|
|
4
|
-
class
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def self.from_hash(data_hash)
|
|
25
|
-
new(data_hash)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def type(chart_type)
|
|
29
|
-
@chart_type = chart_type
|
|
30
|
-
self
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def title(text)
|
|
34
|
-
@title = text
|
|
35
|
-
self
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def subtitle(text)
|
|
39
|
-
@subtitle = text
|
|
40
|
-
self
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def colors(*color_array)
|
|
44
|
-
@colors = color_array.flatten
|
|
45
|
-
self
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def labels(format: :text)
|
|
49
|
-
@label_format = format
|
|
50
|
-
self
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def legend(position: :right)
|
|
54
|
-
@legend = position
|
|
55
|
-
self
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def save_png(filename)
|
|
59
|
-
chart = build_chart
|
|
60
|
-
chart.title(@title) if @title
|
|
61
|
-
chart.subtitle(@subtitle) if @subtitle
|
|
62
|
-
chart.colors(@colors) if @colors
|
|
63
|
-
chart.labels(format: @label_format) if @label_format
|
|
64
|
-
chart.legend(position: @legend) if @legend
|
|
65
|
-
chart.save_png(filename)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def save_pdf(filename)
|
|
69
|
-
chart = build_chart
|
|
70
|
-
chart.save_pdf(filename)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
def build_chart
|
|
76
|
-
case @chart_type
|
|
77
|
-
when :pie
|
|
78
|
-
Charts::PieChart.new(@data)
|
|
79
|
-
when :bar, :vertical_bar
|
|
80
|
-
Charts::BarChart.new(@data, :vertical)
|
|
81
|
-
when :horizontal_bar
|
|
82
|
-
Charts::BarChart.new(@data, :horizontal)
|
|
83
|
-
when :line
|
|
84
|
-
Charts::LineChart.new(@data)
|
|
85
|
-
else
|
|
86
|
-
raise "Unknown chart type: #{@chart_type}"
|
|
87
|
-
end
|
|
88
|
-
end
|
|
19
|
+
class Error < StandardError; end
|
|
20
|
+
|
|
21
|
+
def self.from_csv(filepath, options = {})
|
|
22
|
+
data = Loaders::CSVLoader.load(filepath, options)
|
|
23
|
+
Builder.new(data)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.from_xlsx(filepath, options = {})
|
|
27
|
+
data = Loaders::XLSXLoader.load(filepath, options)
|
|
28
|
+
Builder.new(data)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.from_yaml(filepath, options = {})
|
|
32
|
+
data = Loaders::YAMLLoader.load(filepath, options)
|
|
33
|
+
Builder.new(data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.from_hash(data_hash)
|
|
37
|
+
Builder.new(data_hash)
|
|
89
38
|
end
|
|
90
39
|
end
|
|
File without changes
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-charts
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2a
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Germán Silva
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.3.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: csv
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: roo
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -37,19 +51,42 @@ dependencies:
|
|
|
37
51
|
- - ">="
|
|
38
52
|
- !ruby/object:Gem::Version
|
|
39
53
|
version: 2.10.0
|
|
40
|
-
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: yaml
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
description: Generate pie, bar, and line charts from CSV/XLSX/YAML
|
|
41
69
|
email:
|
|
42
70
|
- ggerman@gmail.com
|
|
43
71
|
executables: []
|
|
44
72
|
extensions: []
|
|
45
73
|
extra_rdoc_files: []
|
|
46
74
|
files:
|
|
75
|
+
- LICENSE
|
|
76
|
+
- README.md
|
|
47
77
|
- lib/ruby_charts.rb
|
|
78
|
+
- lib/ruby_charts/builder.rb
|
|
48
79
|
- lib/ruby_charts/charts/bar_chart.rb
|
|
49
80
|
- lib/ruby_charts/charts/base_chart.rb
|
|
50
81
|
- lib/ruby_charts/charts/line_chart.rb
|
|
51
82
|
- lib/ruby_charts/charts/pie_chart.rb
|
|
83
|
+
- lib/ruby_charts/config.rb
|
|
52
84
|
- lib/ruby_charts/data_loader.rb
|
|
85
|
+
- lib/ruby_charts/loaders/csv_loader.rb
|
|
86
|
+
- lib/ruby_charts/loaders/xlsx_loader.rb
|
|
87
|
+
- lib/ruby_charts/loaders/yaml_loader.rb
|
|
88
|
+
- lib/ruby_charts/version.rb
|
|
89
|
+
- spec/ruby_charts_spec.rb
|
|
53
90
|
homepage: https://github.com/ggerman/ruby-charts
|
|
54
91
|
licenses:
|
|
55
92
|
- MIT
|
|
@@ -68,7 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
68
105
|
- !ruby/object:Gem::Version
|
|
69
106
|
version: '0'
|
|
70
107
|
requirements: []
|
|
71
|
-
rubygems_version: 4.0.
|
|
108
|
+
rubygems_version: 4.0.11
|
|
72
109
|
specification_version: 4
|
|
73
|
-
summary: Professional charts from
|
|
74
|
-
test_files:
|
|
110
|
+
summary: Professional charts from data files using ruby-libgd
|
|
111
|
+
test_files:
|
|
112
|
+
- spec/ruby_charts_spec.rb
|