teejayvanslyke-gruff 0.3.6
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.
- data/History.txt +117 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +81 -0
- data/README.txt +40 -0
- data/Rakefile +55 -0
- data/assets/bubble.png +0 -0
- data/assets/city_scene/background/0000.png +0 -0
- data/assets/city_scene/background/0600.png +0 -0
- data/assets/city_scene/background/2000.png +0 -0
- data/assets/city_scene/clouds/cloudy.png +0 -0
- data/assets/city_scene/clouds/partly_cloudy.png +0 -0
- data/assets/city_scene/clouds/stormy.png +0 -0
- data/assets/city_scene/grass/default.png +0 -0
- data/assets/city_scene/haze/true.png +0 -0
- data/assets/city_scene/number_sample/1.png +0 -0
- data/assets/city_scene/number_sample/2.png +0 -0
- data/assets/city_scene/number_sample/default.png +0 -0
- data/assets/city_scene/sky/0000.png +0 -0
- data/assets/city_scene/sky/0200.png +0 -0
- data/assets/city_scene/sky/0400.png +0 -0
- data/assets/city_scene/sky/0600.png +0 -0
- data/assets/city_scene/sky/0800.png +0 -0
- data/assets/city_scene/sky/1000.png +0 -0
- data/assets/city_scene/sky/1200.png +0 -0
- data/assets/city_scene/sky/1400.png +0 -0
- data/assets/city_scene/sky/1500.png +0 -0
- data/assets/city_scene/sky/1700.png +0 -0
- data/assets/city_scene/sky/2000.png +0 -0
- data/assets/pc306715.jpg +0 -0
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/init.rb +2 -0
- data/lib/gruff/accumulator_bar.rb +27 -0
- data/lib/gruff/area.rb +58 -0
- data/lib/gruff/bar.rb +87 -0
- data/lib/gruff/bar_conversion.rb +46 -0
- data/lib/gruff/base.rb +1123 -0
- data/lib/gruff/bullet.rb +109 -0
- data/lib/gruff/deprecated.rb +39 -0
- data/lib/gruff/dot.rb +113 -0
- data/lib/gruff/line.rb +135 -0
- data/lib/gruff/mini/bar.rb +37 -0
- data/lib/gruff/mini/legend.rb +82 -0
- data/lib/gruff/mini/pie.rb +36 -0
- data/lib/gruff/mini/side_bar.rb +35 -0
- data/lib/gruff/net.rb +140 -0
- data/lib/gruff/photo_bar.rb +100 -0
- data/lib/gruff/pie.rb +126 -0
- data/lib/gruff/scene.rb +209 -0
- data/lib/gruff/side_bar.rb +118 -0
- data/lib/gruff/side_stacked_bar.rb +77 -0
- data/lib/gruff/spider.rb +130 -0
- data/lib/gruff/stacked_area.rb +67 -0
- data/lib/gruff/stacked_bar.rb +57 -0
- data/lib/gruff/stacked_mixin.rb +23 -0
- data/lib/gruff.rb +28 -0
- data/rails_generators/gruff/gruff_generator.rb +63 -0
- data/rails_generators/gruff/templates/controller.rb +32 -0
- data/rails_generators/gruff/templates/functional_test.rb +24 -0
- data/test/gruff_test_case.rb +123 -0
- data/test/test_accumulator_bar.rb +50 -0
- data/test/test_area.rb +134 -0
- data/test/test_bar.rb +321 -0
- data/test/test_base.rb +8 -0
- data/test/test_bullet.rb +26 -0
- data/test/test_dot.rb +273 -0
- data/test/test_legend.rb +68 -0
- data/test/test_line.rb +556 -0
- data/test/test_mini_bar.rb +33 -0
- data/test/test_mini_pie.rb +20 -0
- data/test/test_mini_side_bar.rb +37 -0
- data/test/test_net.rb +230 -0
- data/test/test_photo.rb +41 -0
- data/test/test_pie.rb +154 -0
- data/test/test_scene.rb +100 -0
- data/test/test_side_bar.rb +12 -0
- data/test/test_sidestacked_bar.rb +89 -0
- data/test/test_sidestacked_bar_percentile.rb +29 -0
- data/test/test_spider.rb +216 -0
- data/test/test_stacked_area.rb +52 -0
- data/test/test_stacked_bar.rb +52 -0
- metadata +167 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require File.dirname(__FILE__) + '/side_bar'
|
3
|
+
require File.dirname(__FILE__) + '/stacked_mixin'
|
4
|
+
|
5
|
+
##
|
6
|
+
# New gruff graph type added to enable sideways stacking bar charts
|
7
|
+
# (basically looks like a x/y flip of a standard stacking bar chart)
|
8
|
+
#
|
9
|
+
# alun.eyre@googlemail.com
|
10
|
+
|
11
|
+
class Gruff::SideStackedBar < Gruff::SideBar
|
12
|
+
include StackedMixin
|
13
|
+
|
14
|
+
# Spacing factor applied between bars
|
15
|
+
attr_accessor :bar_spacing
|
16
|
+
|
17
|
+
def draw
|
18
|
+
@has_left_labels = true
|
19
|
+
get_maximum_by_stack
|
20
|
+
super
|
21
|
+
|
22
|
+
return unless @has_data
|
23
|
+
|
24
|
+
# Setup spacing.
|
25
|
+
#
|
26
|
+
# Columns sit stacked.
|
27
|
+
@bar_spacing ||= 0.9
|
28
|
+
|
29
|
+
@bar_width = @graph_height / @column_count.to_f
|
30
|
+
@d = @d.stroke_opacity 0.0
|
31
|
+
height = Array.new(@column_count, 0)
|
32
|
+
length = Array.new(@column_count, @graph_left)
|
33
|
+
padding = (@bar_width * (1 - @bar_spacing)) / 2
|
34
|
+
|
35
|
+
@norm_data.each_with_index do |data_row, row_index|
|
36
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
37
|
+
|
38
|
+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
|
39
|
+
|
40
|
+
## using the original calcs from the stacked bar chart to get the difference between
|
41
|
+
## part of the bart chart we wish to stack.
|
42
|
+
temp1 = @graph_left + (@graph_width -
|
43
|
+
data_point * @graph_width -
|
44
|
+
height[point_index])
|
45
|
+
temp2 = @graph_left + @graph_width - height[point_index]
|
46
|
+
difference = temp2 - temp1
|
47
|
+
|
48
|
+
left_x = length[point_index] #+ 1
|
49
|
+
left_y = @graph_top + (@bar_width * point_index) + padding
|
50
|
+
right_x = left_x + difference
|
51
|
+
right_y = left_y + @bar_width * @bar_spacing
|
52
|
+
length[point_index] += difference
|
53
|
+
height[point_index] += (data_point * @graph_width - 2)
|
54
|
+
|
55
|
+
@d = @d.rectangle(left_x, left_y, right_x, right_y)
|
56
|
+
|
57
|
+
# Calculate center based on bar_width and current row
|
58
|
+
label_center = @graph_top + (@bar_width * point_index) + (@bar_width * @bar_spacing / 2.0)
|
59
|
+
draw_label(label_center, point_index)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
@d.draw(@base_image)
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def larger_than_max?(data_point, index=0)
|
70
|
+
max(data_point, index) > @maximum_value
|
71
|
+
end
|
72
|
+
|
73
|
+
def max(data_point, index)
|
74
|
+
@data.inject(0) {|sum, item| sum + item[DATA_VALUES_INDEX][index]}
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/lib/gruff/spider.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
# Experimental!!! See also the Net graph.
|
5
|
+
#
|
6
|
+
# Submitted by Kevin Clark http://glu.ttono.us/
|
7
|
+
class Gruff::Spider < Gruff::Base
|
8
|
+
|
9
|
+
# Hide all text
|
10
|
+
attr_reader :hide_text
|
11
|
+
attr_accessor :hide_axes
|
12
|
+
attr_reader :transparent_background
|
13
|
+
|
14
|
+
def transparent_background=(value)
|
15
|
+
@transparent_background = value
|
16
|
+
@base_image = render_transparent_background if value
|
17
|
+
end
|
18
|
+
|
19
|
+
def hide_text=(value)
|
20
|
+
@hide_title = @hide_text = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(max_value, target_width = 800)
|
24
|
+
super(target_width)
|
25
|
+
@max_value = max_value
|
26
|
+
@hide_legend = true;
|
27
|
+
end
|
28
|
+
|
29
|
+
def draw
|
30
|
+
@hide_line_markers = true
|
31
|
+
|
32
|
+
super
|
33
|
+
|
34
|
+
return unless @has_data
|
35
|
+
|
36
|
+
# Setup basic positioning
|
37
|
+
diameter = @graph_height
|
38
|
+
radius = @graph_height / 2.0
|
39
|
+
top_x = @graph_left + (@graph_width - diameter) / 2.0
|
40
|
+
center_x = @graph_left + (@graph_width / 2.0)
|
41
|
+
center_y = @graph_top + (@graph_height / 2.0) - 25 # Move graph up a bit
|
42
|
+
|
43
|
+
@unit_length = radius / @max_value
|
44
|
+
|
45
|
+
|
46
|
+
total_sum = sums_for_spider
|
47
|
+
prev_degrees = 0.0
|
48
|
+
additive_angle = (2 * Math::PI)/ @data.size
|
49
|
+
|
50
|
+
current_angle = 0.0
|
51
|
+
|
52
|
+
# Draw axes
|
53
|
+
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
54
|
+
|
55
|
+
# Draw polygon
|
56
|
+
draw_polygon(center_x, center_y, additive_angle)
|
57
|
+
|
58
|
+
|
59
|
+
@d.draw(@base_image)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def normalize_points(value)
|
65
|
+
value * @unit_length
|
66
|
+
end
|
67
|
+
|
68
|
+
def draw_label(center_x, center_y, angle, radius, amount)
|
69
|
+
r_offset = 50 # The distance out from the center of the pie to get point
|
70
|
+
x_offset = center_x # The label points need to be tweaked slightly
|
71
|
+
y_offset = center_y + 0 # This one doesn't though
|
72
|
+
x = x_offset + ((radius + r_offset) * Math.cos(angle))
|
73
|
+
y = y_offset + ((radius + r_offset) * Math.sin(angle))
|
74
|
+
|
75
|
+
# Draw label
|
76
|
+
@d.fill = @marker_color
|
77
|
+
@d.font = @font if @font
|
78
|
+
@d.pointsize = scale_fontsize(legend_font_size)
|
79
|
+
@d.stroke = 'transparent'
|
80
|
+
@d.font_weight = BoldWeight
|
81
|
+
@d.gravity = CenterGravity
|
82
|
+
@d.annotate_scaled( @base_image,
|
83
|
+
0, 0,
|
84
|
+
x, y,
|
85
|
+
amount, @scale)
|
86
|
+
end
|
87
|
+
|
88
|
+
def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
|
89
|
+
return if hide_axes
|
90
|
+
|
91
|
+
current_angle = 0.0
|
92
|
+
|
93
|
+
@data.each do |data_row|
|
94
|
+
@d.stroke(line_color || data_row[DATA_COLOR_INDEX])
|
95
|
+
@d.stroke_width 5.0
|
96
|
+
|
97
|
+
x_offset = radius * Math.cos(current_angle)
|
98
|
+
y_offset = radius * Math.sin(current_angle)
|
99
|
+
|
100
|
+
@d.line(center_x, center_y,
|
101
|
+
center_x + x_offset,
|
102
|
+
center_y + y_offset)
|
103
|
+
|
104
|
+
draw_label(center_x, center_y, current_angle, radius, data_row[DATA_LABEL_INDEX].to_s) unless hide_text
|
105
|
+
|
106
|
+
current_angle += additive_angle
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def draw_polygon(center_x, center_y, additive_angle, color = nil)
|
111
|
+
points = []
|
112
|
+
current_angle = 0.0
|
113
|
+
@data.each do |data_row|
|
114
|
+
points << center_x + normalize_points(data_row[DATA_VALUES_INDEX].first) * Math.cos(current_angle)
|
115
|
+
points << center_y + normalize_points(data_row[DATA_VALUES_INDEX].first) * Math.sin(current_angle)
|
116
|
+
current_angle += additive_angle
|
117
|
+
end
|
118
|
+
|
119
|
+
@d.stroke_width 1.0
|
120
|
+
@d.stroke(color || @marker_color)
|
121
|
+
@d.fill(color || @marker_color)
|
122
|
+
@d.fill_opacity 0.4
|
123
|
+
@d.polygon(*points)
|
124
|
+
end
|
125
|
+
|
126
|
+
def sums_for_spider
|
127
|
+
@data.inject(0.0) {|sum, data_row| sum += data_row[DATA_VALUES_INDEX].first}
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
require File.dirname(__FILE__) + '/stacked_mixin'
|
4
|
+
|
5
|
+
class Gruff::StackedArea < Gruff::Base
|
6
|
+
include StackedMixin
|
7
|
+
attr_accessor :last_series_goes_on_bottom
|
8
|
+
|
9
|
+
def draw
|
10
|
+
get_maximum_by_stack
|
11
|
+
super
|
12
|
+
|
13
|
+
return unless @has_data
|
14
|
+
|
15
|
+
@x_increment = @graph_width / (@column_count - 1).to_f
|
16
|
+
@d = @d.stroke 'transparent'
|
17
|
+
|
18
|
+
height = Array.new(@column_count, 0)
|
19
|
+
|
20
|
+
data_points = nil
|
21
|
+
iterator = last_series_goes_on_bottom ? :reverse_each : :each
|
22
|
+
@norm_data.send(iterator) do |data_row|
|
23
|
+
prev_data_points = data_points
|
24
|
+
data_points = Array.new
|
25
|
+
|
26
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
27
|
+
|
28
|
+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
|
29
|
+
# Use incremented x and scaled y
|
30
|
+
new_x = @graph_left + (@x_increment * index)
|
31
|
+
new_y = @graph_top + (@graph_height - data_point * @graph_height - height[index])
|
32
|
+
|
33
|
+
height[index] += (data_point * @graph_height)
|
34
|
+
|
35
|
+
data_points << new_x
|
36
|
+
data_points << new_y
|
37
|
+
|
38
|
+
draw_label(new_x, index)
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
if prev_data_points
|
43
|
+
poly_points = data_points.dup
|
44
|
+
(prev_data_points.length/2 - 1).downto(0) do |i|
|
45
|
+
poly_points << prev_data_points[2*i]
|
46
|
+
poly_points << prev_data_points[2*i+1]
|
47
|
+
end
|
48
|
+
poly_points << data_points[0]
|
49
|
+
poly_points << data_points[1]
|
50
|
+
else
|
51
|
+
poly_points = data_points.dup
|
52
|
+
poly_points << @graph_right
|
53
|
+
poly_points << @graph_bottom - 1
|
54
|
+
poly_points << @graph_left
|
55
|
+
poly_points << @graph_bottom - 1
|
56
|
+
poly_points << data_points[0]
|
57
|
+
poly_points << data_points[1]
|
58
|
+
end
|
59
|
+
@d = @d.polyline(*poly_points)
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
@d.draw(@base_image)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
require File.dirname(__FILE__) + '/stacked_mixin'
|
4
|
+
|
5
|
+
class Gruff::StackedBar < Gruff::Base
|
6
|
+
include StackedMixin
|
7
|
+
|
8
|
+
# Spacing factor applied between bars
|
9
|
+
attr_accessor :bar_spacing
|
10
|
+
|
11
|
+
# Draws a bar graph, but multiple sets are stacked on top of each other.
|
12
|
+
def draw
|
13
|
+
get_maximum_by_stack
|
14
|
+
super
|
15
|
+
return unless @has_data
|
16
|
+
|
17
|
+
# Setup spacing.
|
18
|
+
#
|
19
|
+
# Columns sit stacked.
|
20
|
+
@bar_spacing ||= 0.9
|
21
|
+
@bar_width = @graph_width / @column_count.to_f
|
22
|
+
padding = (@bar_width * (1 - @bar_spacing)) / 2
|
23
|
+
|
24
|
+
@d = @d.stroke_opacity 0.0
|
25
|
+
|
26
|
+
height = Array.new(@column_count, 0)
|
27
|
+
|
28
|
+
@norm_data.each_with_index do |data_row, row_index|
|
29
|
+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
|
30
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
31
|
+
|
32
|
+
# Calculate center based on bar_width and current row
|
33
|
+
label_center = @graph_left + (@bar_width * point_index) + (@bar_width * @bar_spacing / 2.0)
|
34
|
+
draw_label(label_center, point_index)
|
35
|
+
|
36
|
+
next if (data_point == 0)
|
37
|
+
# Use incremented x and scaled y
|
38
|
+
left_x = @graph_left + (@bar_width * point_index) + padding
|
39
|
+
left_y = @graph_top + (@graph_height -
|
40
|
+
data_point * @graph_height -
|
41
|
+
height[point_index]) + 1
|
42
|
+
right_x = left_x + @bar_width * @bar_spacing
|
43
|
+
right_y = @graph_top + @graph_height - height[point_index] - 1
|
44
|
+
|
45
|
+
# update the total height of the current stacked bar
|
46
|
+
height[point_index] += (data_point * @graph_height )
|
47
|
+
|
48
|
+
@d = @d.rectangle(left_x, left_y, right_x, right_y)
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
@d.draw(@base_image)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Gruff::Base::StackedMixin
|
3
|
+
# Used by StackedBar and child classes.
|
4
|
+
#
|
5
|
+
# tsal: moved from Base 03 FEB 2007
|
6
|
+
DATA_VALUES_INDEX = Gruff::Base::DATA_VALUES_INDEX
|
7
|
+
def get_maximum_by_stack
|
8
|
+
# Get sum of each stack
|
9
|
+
max_hash = {}
|
10
|
+
@data.each do |data_set|
|
11
|
+
data_set[DATA_VALUES_INDEX].each_with_index do |data_point, i|
|
12
|
+
max_hash[i] = 0.0 unless max_hash[i]
|
13
|
+
max_hash[i] += data_point.to_f
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @maximum_value = 0
|
18
|
+
max_hash.keys.each do |key|
|
19
|
+
@maximum_value = max_hash[key] if max_hash[key] > @maximum_value
|
20
|
+
end
|
21
|
+
@minimum_value = 0
|
22
|
+
end
|
23
|
+
end
|
data/lib/gruff.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Extra full path added to fix loading errors on some installations.
|
2
|
+
|
3
|
+
%w(
|
4
|
+
base
|
5
|
+
area
|
6
|
+
bar
|
7
|
+
line
|
8
|
+
dot
|
9
|
+
pie
|
10
|
+
spider
|
11
|
+
net
|
12
|
+
stacked_area
|
13
|
+
stacked_bar
|
14
|
+
side_stacked_bar
|
15
|
+
side_bar
|
16
|
+
accumulator_bar
|
17
|
+
|
18
|
+
scene
|
19
|
+
|
20
|
+
mini/legend
|
21
|
+
mini/bar
|
22
|
+
mini/pie
|
23
|
+
mini/side_bar
|
24
|
+
).each do |filename|
|
25
|
+
require File.dirname(__FILE__) + "/gruff/#{filename}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO bullet
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class GruffGenerator < Rails::Generator::NamedBase
|
2
|
+
|
3
|
+
attr_reader :controller_name,
|
4
|
+
:controller_class_path,
|
5
|
+
:controller_file_path,
|
6
|
+
:controller_class_nesting,
|
7
|
+
:controller_class_nesting_depth,
|
8
|
+
:controller_class_name,
|
9
|
+
:controller_singular_name,
|
10
|
+
:controller_plural_name,
|
11
|
+
:parent_folder_for_require
|
12
|
+
alias_method :controller_file_name, :controller_singular_name
|
13
|
+
alias_method :controller_table_name, :controller_plural_name
|
14
|
+
|
15
|
+
def initialize(runtime_args, runtime_options = {})
|
16
|
+
super
|
17
|
+
|
18
|
+
# Take controller name from the next argument.
|
19
|
+
@controller_name = runtime_args.shift
|
20
|
+
|
21
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
22
|
+
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
|
23
|
+
|
24
|
+
if @controller_class_nesting.empty?
|
25
|
+
@controller_class_name = @controller_class_name_without_nesting
|
26
|
+
else
|
27
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def manifest
|
32
|
+
record do |m|
|
33
|
+
# Check for class naming collisions.
|
34
|
+
m.class_collisions controller_class_path, "#{controller_class_name}Controller",
|
35
|
+
"#{controller_class_name}ControllerTest"
|
36
|
+
|
37
|
+
# Controller, helper, views, and test directories.
|
38
|
+
m.directory File.join('app/controllers', controller_class_path)
|
39
|
+
m.directory File.join('test/functional', controller_class_path)
|
40
|
+
|
41
|
+
m.template 'controller.rb',
|
42
|
+
File.join('app/controllers',
|
43
|
+
controller_class_path,
|
44
|
+
"#{controller_file_name}_controller.rb")
|
45
|
+
|
46
|
+
# For some reason this doesn't take effect if done in initialize()
|
47
|
+
@parent_folder_for_require = @controller_class_path.join('/').gsub(%r%app/controllers/?%, '')
|
48
|
+
@parent_folder_for_require += @parent_folder_for_require.blank? ? '' : '/'
|
49
|
+
|
50
|
+
m.template 'functional_test.rb',
|
51
|
+
File.join('test/functional',
|
52
|
+
controller_class_path,
|
53
|
+
"#{controller_file_name}_controller_test.rb")
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
# Override with your own usage banner.
|
60
|
+
def banner
|
61
|
+
"Usage: #{$0} gruff ControllerName"
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
2
|
+
|
3
|
+
# To make caching easier, add a line like this to config/routes.rb:
|
4
|
+
# map.graph "graph/:action/:id/image.png", :controller => "graph"
|
5
|
+
#
|
6
|
+
# Then reference it with the named route:
|
7
|
+
# image_tag graph_url(:action => 'show', :id => 42)
|
8
|
+
|
9
|
+
def show
|
10
|
+
g = Gruff::Line.new
|
11
|
+
# Uncomment to use your own theme or font
|
12
|
+
# See http://colourlovers.com or http://www.firewheeldesign.com/widgets/ for color ideas
|
13
|
+
# g.theme = {
|
14
|
+
# :colors => ['#663366', '#cccc99', '#cc6633', '#cc9966', '#99cc99'],
|
15
|
+
# :marker_color => 'white',
|
16
|
+
# :background_colors => ['black', '#333333']
|
17
|
+
# }
|
18
|
+
# g.font = File.expand_path('artwork/fonts/VeraBd.ttf', RAILS_ROOT)
|
19
|
+
|
20
|
+
g.title = "Gruff-o-Rama"
|
21
|
+
|
22
|
+
g.data("Apples", [1, 2, 3, 4, 4, 3])
|
23
|
+
g.data("Oranges", [4, 8, 7, 9, 8, 9])
|
24
|
+
g.data("Watermelon", [2, 3, 1, 5, 6, 8])
|
25
|
+
g.data("Peaches", [9, 9, 10, 8, 7, 9])
|
26
|
+
|
27
|
+
g.labels = {0 => '2004', 2 => '2005', 4 => '2006'}
|
28
|
+
|
29
|
+
send_data(g.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "gruff.png")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= '/..' * controller_class_name.split("::").length %>/test_helper'
|
2
|
+
require '<%= parent_folder_for_require %><%= controller_file_name %>_controller'
|
3
|
+
|
4
|
+
# Re-raise errors caught by the controller.
|
5
|
+
class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
|
6
|
+
|
7
|
+
class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
#fixtures :data
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@controller = <%= controller_class_name %>Controller.new
|
13
|
+
@request = ActionController::TestRequest.new
|
14
|
+
@response = ActionController::TestResponse.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO Replace this with your actual tests
|
18
|
+
def test_show
|
19
|
+
get :show
|
20
|
+
assert_response :success
|
21
|
+
assert_equal 'image/png', @response.headers['Content-Type']
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
2
|
+
# require 'rubygems'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'gruff'
|
6
|
+
require 'fileutils'
|
7
|
+
# require 'test_timer'
|
8
|
+
|
9
|
+
TEST_OUTPUT_DIR = File.dirname(__FILE__) + "/output"
|
10
|
+
FileUtils.mkdir_p(TEST_OUTPUT_DIR)
|
11
|
+
|
12
|
+
class GruffTestCase < Test::Unit::TestCase
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@datasets = [
|
16
|
+
[:Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]],
|
17
|
+
[:Charles, [80, 54, 67, 54, 68, 70, 90, 95]],
|
18
|
+
[:Julie, [22, 29, 35, 38, 36, 40, 46, 57]],
|
19
|
+
[:Jane, [95, 95, 95, 90, 85, 80, 88, 100]],
|
20
|
+
[:Philip, [90, 34, 23, 12, 78, 89, 98, 88]],
|
21
|
+
["Arthur", [5, 10, 13, 11, 6, 16, 22, 32]],
|
22
|
+
]
|
23
|
+
|
24
|
+
@labels = {
|
25
|
+
0 => '5/6',
|
26
|
+
1 => '5/15',
|
27
|
+
2 => '5/24',
|
28
|
+
3 => '5/30',
|
29
|
+
4 => '6/4',
|
30
|
+
5 => '6/12',
|
31
|
+
6 => '6/21',
|
32
|
+
7 => '6/28',
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup_single_dataset
|
37
|
+
@datasets = [
|
38
|
+
[:Jimmy, [25, 36, 86]]
|
39
|
+
]
|
40
|
+
|
41
|
+
@labels = {
|
42
|
+
0 => 'You',
|
43
|
+
1 => 'Average',
|
44
|
+
2 => 'Lifetime'
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_wide_dataset
|
49
|
+
@datasets = [
|
50
|
+
["Auto", 25],
|
51
|
+
["Food", 5],
|
52
|
+
["Entertainment", 15]
|
53
|
+
]
|
54
|
+
|
55
|
+
@labels = { 0 => 'This Month' }
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_dummy
|
59
|
+
assert true
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
# Generate graphs at several sizes.
|
65
|
+
#
|
66
|
+
# Also writes the graph to disk.
|
67
|
+
#
|
68
|
+
# graph_sized 'bar_basic' do |g|
|
69
|
+
# g.data('students', [1, 2, 3, 4])
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
def graph_sized(filename, sizes=['', 400])
|
73
|
+
class_name = self.class.name.gsub(/^TestGruff/, '')
|
74
|
+
Array(sizes).each do |size|
|
75
|
+
g = instance_eval("Gruff::#{class_name}.new #{size}")
|
76
|
+
g.title = "#{class_name} Graph"
|
77
|
+
yield g
|
78
|
+
write_test_file g, "#{filename}_#{size}.png"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_test_file(graph, filename)
|
83
|
+
graph.write([TEST_OUTPUT_DIR, filename].join("/"))
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Example:
|
88
|
+
#
|
89
|
+
# setup_basic_graph Gruff::Pie, 400
|
90
|
+
#
|
91
|
+
def setup_basic_graph(*args)
|
92
|
+
klass, size = Gruff::Bar, 400
|
93
|
+
# Allow args to be klass, size or just klass or just size.
|
94
|
+
#
|
95
|
+
# TODO Refactor
|
96
|
+
case args.length
|
97
|
+
when 1
|
98
|
+
case args[0]
|
99
|
+
when Fixnum
|
100
|
+
size = args[0]
|
101
|
+
klass = eval("Gruff::#{self.class.name.gsub(/^TestGruff/, '')}")
|
102
|
+
when String
|
103
|
+
size = args[0]
|
104
|
+
klass = eval("Gruff::#{self.class.name.gsub(/^TestGruff/, '')}")
|
105
|
+
else
|
106
|
+
klass = args[0]
|
107
|
+
end
|
108
|
+
when 2
|
109
|
+
klass, size = args[0], args[1]
|
110
|
+
end
|
111
|
+
|
112
|
+
g = klass.new(size)
|
113
|
+
g.title = "My Bar Graph"
|
114
|
+
g.labels = @labels
|
115
|
+
|
116
|
+
|
117
|
+
@datasets.each do |data|
|
118
|
+
g.data(data[0], data[1])
|
119
|
+
end
|
120
|
+
g
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/gruff_test_case"
|
2
|
+
|
3
|
+
class TestGruffAccumulatorBar < GruffTestCase
|
4
|
+
|
5
|
+
# TODO Delete old output files once when starting tests
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@datasets = [
|
9
|
+
(1..20).to_a.map { rand(10) }
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_accumulator
|
14
|
+
g = Gruff::AccumulatorBar.new 500
|
15
|
+
g.title = "Your Savings"
|
16
|
+
g.hide_legend = true
|
17
|
+
|
18
|
+
# g.font = File.expand_path(File.dirname(__FILE__) + "/../assets/fonts/ATMA____.TTF")
|
19
|
+
|
20
|
+
g.marker_font_size = 18
|
21
|
+
|
22
|
+
g.theme = {
|
23
|
+
:colors => ['#aedaa9', '#12a702'], # 3077a9 blue, aedaa9 light green
|
24
|
+
:marker_color => '#dddddd',
|
25
|
+
:font_color => 'black',
|
26
|
+
:background_colors => "white"
|
27
|
+
# :background_image => File.expand_path(File.dirname(__FILE__) + "/../assets/backgrounds/43things.png")
|
28
|
+
}
|
29
|
+
|
30
|
+
# Attempt at negative numbers
|
31
|
+
# g.data 'Savings', (1..20).to_a.map { rand(10) * (rand(2) > 0 ? 1 : -1) }
|
32
|
+
g.data 'Savings', (1..12).to_a.map { rand(100) }
|
33
|
+
g.labels = (0..11).to_a.inject({}) {|memo, index| {index => '12-26'}.merge(memo)}
|
34
|
+
|
35
|
+
g.maximum_value = 1000
|
36
|
+
g.minimum_value = 0
|
37
|
+
|
38
|
+
g.write("test/output/accum_bar.png")
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_too_many_args
|
42
|
+
assert_raise(Gruff::IncorrectNumberOfDatasetsException) {
|
43
|
+
g = Gruff::AccumulatorBar.new
|
44
|
+
g.data 'First', [1,1,1]
|
45
|
+
g.data 'Too Many', [1,1,1]
|
46
|
+
g.write("test/output/_SHOULD_NOT_ACTUALLY_BE_WRITTEN.png")
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|