Rails Bar Graph Helper Source Code
See Rails: Bar Graph Helper for usage instructions.
RUBY:
-
module BarGraphHelper
-
# Does all the heavy lifting
-
def bar_graph( data, bg_props = {}, bar_props = {}, label_props = {}, title_props = {}, legend_props = {} )
-
the_bg = GraphBackground.new( bg_props )
-
the_bars = GraphBar.new( bar_props )
-
the_labels = GraphLabel.new( label_props, the_bars )
-
the_title = GraphTitle.new( title_props )
-
the_legend = GraphLegend.new( legend_props )
-
-
html = "<style>"
-
html <<the_bg.define_css <<"\n"
-
html <<the_bars.define_css( the_bg ) <<"\n"
-
html <<the_labels.define_css( the_bg ) <<"\n"
-
html <<the_title.define_css( the_bg ) <<"\n"
-
html <<the_legend.define_css( the_bg ) <<"\n"
-
html <<'</style><div id="vertgraph-' + the_bg.graph_id + '" title="' + the_bg.title + '">' <<"\n"
-
html <<the_title.define_title <<"\n"
-
html <<the_legend.define_legend( the_bg ) <<"\n"
-
html <<"<dl>\n"
-
html <<the_bars.define_bars( data, the_bg, the_labels ) <<"\n"
-
html <<"</dl>\n</div>" <<"\n"
-
return html
-
end
-
-
-
# ---------------------------------------------------------------------------------------------------- #
-
-
-
class GraphComponent
-
def method_missing( method, *args )
-
@props[method] != nil ? the_val = @props[method] : the_val = ''
-
return the_val
-
end
-
end
-
-
-
class GraphBackground <GraphComponent
-
attr_accessor :graph_id
-
-
def initialize( props )
-
@props = props
-
@graph_id = random_alphanumeric
-
-
default_prop_array = { :position => 'relative', :width => '400px', :height => '150px', :background_color => '#fff', :border => '4px solid #aaa' }
-
default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
-
end
-
-
def define_css
-
prop_array = [ :position, :width, :height, :background_color, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style, :font_family, :background]
-
-
css = "#vertgraph-" + @graph_id + " {\n"
-
prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil }
-
css <<"}\n"
-
-
return css
-
end
-
-
private
-
-
def define_lines
-
-
end
-
-
def random_alphanumeric( size=5 )
-
# This function courtesy pope on Code Snippets (http://www.bigbold.com/snippets/user/pope)
-
(1..size).collect { (i = Kernel.rand(62); i += ((i <10) ? 48 : ((i <36) ? 55 : 61 ))).chr }.join
-
end
-
end
-
-
-
class GraphTitle <GraphComponent
-
def initialize( props )
-
@props = props
-
-
default_prop_array = { :position => 'absolute', :left => '5px', :top => '5px', :font_weight => 'bold' }
-
default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
-
end
-
-
def define_css( the_bg )
-
prop_array = [:position, :width, :left, :top, :color, :background_color, :text_align, :text_decoration, :font_size, :font_weight, :font_family, :font_style, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style, :padding, :margin ]
-
-
css = "#vertgraph-" + the_bg.graph_id + " #title {\n"
-
prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil }
-
css <<"}\n"
-
return css
-
end
-
-
def define_title
-
if @props[:title] != nil
-
title = "<div id='title'>"
-
title <<@props[:title]
-
title <<"</div>"
-
-
return title
-
else
-
return ''
-
end
-
end
-
end
-
-
-
class GraphBar <GraphComponent
-
def initialize( props )
-
@props = props
-
@props[:background_color] = Array.new(1, @props[:background_color]) if @props[:background_color].is_a? String
-
-
default_prop_array = { :position => 'absolute', :width => '40px', :bottom => '30px', :color => '#fff', :background_color => ['#00f'], :text_align => 'center', :top_offset => 5, :left_offset => 10, :increment => 50, :padding => '0px', :margin => '0px', :text_floor => 24 }
-
default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
-
end
-
-
def define_css( the_bg )
-
prop_array = [:position, :width, :bottom, :text_align, :font_weight, :color, :padding, :margin, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style]
-
-
css = "#vertgraph-" + the_bg.graph_id + " dl dd {\n"
-
prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil }
-
css <<"background-color: #{@props[:background_color][0]};" if @props[:background_color].size == 1
-
css <<"}\n"
-
return css
-
end
-
-
def define_bars( data, the_bg, the_labels )
-
data_max = data.inject(0) { |memo, array| array.last> memo ? array.last : memo }
-
scaled_offset = the_bg.height.to_i - @props[:bottom].to_i - @props[:top_offset]
-
-
bars = ''
-
data.each_with_index do |d, index|
-
bar_left = ( @props[:left_offset] + ( @props[:increment] * index ) )
-
label_left = ( bar_left - the_labels.h_offset )
-
scaled_value = ( ( d.last.to_f / data_max.to_f ) * scaled_offset ).round
-
-
# If the background color is an array> 1 then loop through it applying colors to bars
-
bar_background_text = ''
-
if @props[:background_color].size> 1
-
bar_background_text = "background-color: " + @props[:background_color][ index % @props[:background_color].size ] + ";"
-
end
-
-
bars <<"<dt style='left: #{label_left}px;'>#{d[0].to_s.humanize}</dt>\n" if d[0] != ''
-
bars <<"<dd style='left: #{bar_left}px; height: #{scaled_value}px; #{bar_background_text}' title='#{d[0]} - #{d[1]}'>#{scaled_value <@props[:text_floor] ? '' : d.last}</dt>\n"
-
end
-
return bars
-
end
-
end
-
-
-
class GraphLabel <GraphComponent
-
def initialize( props, the_bars )
-
@props = props
-
@props[:width] = ( ( the_bars.width.to_i + ( @props[:h_offset] * 2 ) ).to_s + 'px' ) if @props[:h_offset] != nil
-
-
default_prop_array = { :position => 'absolute', :width => '40px', :h_offset => 0, :height => '25px', :bottom => '0px', :font_size => '0.8em', :text_align => 'center', :background_color => '#fff', :padding => '0px', :margin => '0px' }
-
default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
-
end
-
-
def define_css( the_bg )
-
prop_array = [:position, :width, :height, :bottom, :text_align, :font_weight, :font_size, :color, :background_color, :padding, :margin, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style]
-
-
css = "#vertgraph-" + the_bg.graph_id + " dl dt {\n"
-
prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil }
-
css <<"}\n"
-
return css
-
end
-
end
-
-
-
class GraphLegend <GraphComponent
-
def initialize( props )
-
@props = props
-
-
default_prop_array = { :position => 'absolute', :list_style => 'none', :color => '#000', :border => '1px solid #000', :margin => '0px', :padding => '4px' }
-
default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
-
end
-
-
def define_css( the_bg )
-
if !@props[:data] then return '' end
-
-
prop_array = [:position, :list_style, :width, :height, :left, :top, :bottom, :font_family, :font_weight, :font_size, :color, :background_color, :padding, :margin, :border, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style]
-
-
css = "#vertgraph-" + the_bg.graph_id + " #legend {\n"
-
prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil }
-
css <<"}\n\n"
-
-
@props[:data].each_with_index do |d, index|
-
css <<"#legend_key#{index} {\n"
-
css <<"width: 12px; height: 12px; border: 1px solid #000; background-color: #{d[0]}; margin-right: 4px; float: left;\n"
-
css <<"}\n\n"
-
end
-
-
return css
-
end
-
-
def define_legend( the_bg )
-
if !@props[:data] then return '' end
-
-
legend = '<ul id="legend">'
-
@props[:data].each_with_index do |d, index|
-
legend <<"<li><div id='legend_key#{index}'></div>" + d[1] + "</li>"
-
end
-
legend <<"</ul>"
-
return legend
-
end
-
end
-
end
chris on February 16th 2007

AJ responded on 18 Jun 2007 at 9:56 am #
This is pretty cool. I’m thinking of possibly using it in a project; how is this licensed?
chris responded on 18 Jun 2007 at 11:18 am #
I suppose it’s licensed under the “wide open, do whatever you want, absolutely no restrictions or conditions of any sort” license. I really I ought to grab one of the official legal-like ones but in the meantime consider the previous sentence tacit agreement for whatever you’d like to do with it.
Of course it would be appreciated if you sent any improvements back so I could incorporate them and if you use it on a public site let me know so I can gaze upon it proudly.