Rails Bar Graph Helper Source Code

See Rails: Bar Graph Helper for usage instructions.

RUBY:
  1. module BarGraphHelper
  2.   # Does all the heavy lifting
  3.   def bar_graph( data, bg_props = {}, bar_props = {}, label_props = {}, title_props = {}, legend_props = {} )
  4.     the_bg = GraphBackground.new( bg_props )
  5.     the_bars = GraphBar.new( bar_props )
  6.     the_labels = GraphLabel.new( label_props, the_bars )
  7.     the_title = GraphTitle.new( title_props )
  8.     the_legend = GraphLegend.new( legend_props )
  9.  
  10.     html = "<style>"
  11.     html <<the_bg.define_css <<"\n"
  12.     html <<the_bars.define_css( the_bg ) <<"\n"
  13.     html <<the_labels.define_css( the_bg ) <<"\n"
  14.     html <<the_title.define_css( the_bg ) <<"\n"
  15.     html <<the_legend.define_css( the_bg ) <<"\n"
  16.     html <<'</style><div id="vertgraph-' + the_bg.graph_id + '" title="' + the_bg.title + '">' <<"\n"
  17.     html <<the_title.define_title <<"\n"
  18.     html <<the_legend.define_legend( the_bg ) <<"\n"
  19.     html <<"<dl>\n"
  20.     html <<the_bars.define_bars( data, the_bg, the_labels ) <<"\n"
  21.     html <<"</dl>\n</div>" <<"\n"
  22.     return html
  23.   end
  24.  
  25.  
  26.   # ---------------------------------------------------------------------------------------------------- #
  27.  
  28.  
  29.   class GraphComponent
  30.     def method_missing( method, *args )
  31.       @props[method] != nil ? the_val = @props[method] : the_val = ''
  32.       return the_val
  33.     end
  34.   end
  35.  
  36.  
  37.   class GraphBackground <GraphComponent
  38.     attr_accessor :graph_id
  39.    
  40.     def initialize( props )
  41.       @props = props
  42.       @graph_id = random_alphanumeric
  43.      
  44.       default_prop_array = { :position => 'relative', :width => '400px', :height => '150px', :background_color => '#fff', :border => '4px solid #aaa' }
  45.       default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
  46.     end
  47.    
  48.     def define_css
  49.       prop_array = [ :position, :width, :height, :background_color, :border, :border_top, :border_right, :border_bottom, :border_left, :border_color, :border_style, :font_family, :background]
  50.      
  51.       css = "#vertgraph-" + @graph_id + " {\n"
  52.       prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil  }
  53.       css <<"}\n"
  54.  
  55.       return css
  56.     end
  57.  
  58.   private
  59.  
  60.     def define_lines
  61.      
  62.     end
  63.  
  64.     def random_alphanumeric( size=5 )
  65.       # This function courtesy pope on Code Snippets (http://www.bigbold.com/snippets/user/pope)
  66.       (1..size).collect { (i = Kernel.rand(62); i += ((i <10) ? 48 : ((i <36) ? 55 : 61 ))).chr }.join
  67.     end
  68.   end
  69.  
  70.  
  71.   class GraphTitle <GraphComponent
  72.     def initialize( props )
  73.       @props = props
  74.      
  75.       default_prop_array = { :position => 'absolute', :left => '5px', :top => '5px', :font_weight => 'bold' }
  76.       default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
  77.     end
  78.    
  79.     def define_css( the_bg )
  80.       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 ]
  81.      
  82.       css = "#vertgraph-" + the_bg.graph_id + " #title {\n"
  83.       prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil  }
  84.       css <<"}\n"
  85.       return css
  86.     end
  87.    
  88.     def define_title
  89.       if @props[:title] != nil
  90.         title = "<div id='title'>"
  91.         title <<@props[:title]
  92.         title <<"</div>"
  93.      
  94.         return title
  95.       else
  96.         return ''
  97.       end
  98.     end
  99.   end
  100.  
  101.  
  102.   class GraphBar <GraphComponent
  103.     def initialize( props )
  104.       @props = props
  105.       @props[:background_color] = Array.new(1, @props[:background_color]) if @props[:background_color].is_a? String
  106.    
  107.       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 }
  108.       default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
  109.     end
  110.    
  111.     def define_css( the_bg )
  112.       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]
  113.      
  114.       css = "#vertgraph-" + the_bg.graph_id + " dl dd {\n"
  115.       prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil  }
  116.       css <<"background-color: #{@props[:background_color][0]};" if @props[:background_color].size == 1
  117.       css <<"}\n"
  118.       return css
  119.     end
  120.    
  121.     def define_bars( data, the_bg, the_labels )
  122.       data_max = data.inject(0) { |memo, array| array.last> memo ? array.last : memo }
  123.       scaled_offset = the_bg.height.to_i - @props[:bottom].to_i - @props[:top_offset]
  124.      
  125.       bars = ''
  126.       data.each_with_index do |d, index|
  127.         bar_left = ( @props[:left_offset] + ( @props[:increment] * index ) )
  128.         label_left = ( bar_left - the_labels.h_offset )
  129.         scaled_value = ( ( d.last.to_f / data_max.to_f ) * scaled_offset ).round
  130.  
  131.         # If the background color is an array> 1 then loop through it applying colors to bars
  132.         bar_background_text = ''
  133.         if @props[:background_color].size> 1
  134.           bar_background_text = "background-color: " + @props[:background_color][ index % @props[:background_color].size ] + ";"
  135.         end
  136.        
  137.         bars <<"<dt style='left: #{label_left}px;'>#{d[0].to_s.humanize}</dt>\n" if d[0] != ''
  138.         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"
  139.       end
  140.       return bars
  141.     end
  142.   end
  143.  
  144.  
  145.   class GraphLabel <GraphComponent
  146.     def initialize( props, the_bars )
  147.       @props = props
  148.       @props[:width] = ( ( the_bars.width.to_i + ( @props[:h_offset] * 2 ) ).to_s + 'px' ) if @props[:h_offset] != nil
  149.      
  150.       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' }
  151.       default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
  152.     end
  153.    
  154.     def define_css( the_bg )
  155.       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]
  156.      
  157.       css = "#vertgraph-" + the_bg.graph_id + " dl dt {\n"
  158.       prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil  }
  159.       css <<"}\n"
  160.       return css
  161.     end
  162.   end 
  163.  
  164.  
  165.   class GraphLegend <GraphComponent
  166.     def initialize( props )
  167.       @props = props
  168.      
  169.       default_prop_array = { :position => 'absolute', :list_style => 'none', :color => '#000', :border => '1px solid #000', :margin => '0px', :padding => '4px' }
  170.       default_prop_array.each { |prop, val| @props[prop] = val if @props[prop] == nil }
  171.     end
  172.    
  173.     def define_css( the_bg )
  174.       if !@props[:data] then return '' end
  175.        
  176.       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]
  177.    
  178.       css = "#vertgraph-" + the_bg.graph_id + " #legend {\n"
  179.       prop_array.each { |prop| css <<"#{prop.to_s.dasherize}: #{@props[prop]};\n" if @props[prop] != nil  }
  180.       css <<"}\n\n"
  181.    
  182.       @props[:data].each_with_index do |d, index|
  183.         css <<"#legend_key#{index} {\n"
  184.         css <<"width: 12px; height: 12px; border: 1px solid #000; background-color: #{d[0]}; margin-right: 4px; float: left;\n"
  185.         css <<"}\n\n"
  186.       end
  187.  
  188.       return css
  189.     end
  190.    
  191.     def define_legend( the_bg )
  192.       if !@props[:data] then return '' end
  193.        
  194.       legend = '<ul id="legend">'
  195.       @props[:data].each_with_index do |d, index|
  196.         legend <<"<li><div id='legend_key#{index}'></div>" + d[1] + "</li>"
  197.       end
  198.       legend <<"</ul>"
  199.       return legend
  200.     end
  201.   end
  202. end

chris on February 16th 2007

2 Responses to “Rails Bar Graph Helper Source Code”

  1. 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?

  2. 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.

Trackback URI | Comments RSS

Leave a Reply