skip down to the 4 comments

Another tag cloud script for Ruby on Rails

Or any other Ruby program really. I know there are attempts already out there but none that really suited my purposes. I needed a simple way of creating that tag-cloud-look for many situations. I needed a tag cloud by size or by colour and I didn’t want it to do everything, just provide me with the style attributes I needed to get the job done. Below is an example of what gets returned by default:

font-size:16px;color:rgb(70,70,70);

Here’s the code; it comes with no warranty.

With all these tags cloud scripts you really only need 3 parameters:

  1. The total counts of items – For example you have 10 tags each that has been used numerous times. The total counts would be all of the uses added together
  2. The minimum count of items in a batch – Using the example above this would be a count of the uses for the tag that has been used least
  3. The maximum count of items in a batch – Using the example above this would be a count of the uses for the tag that has been used most

Below is the actual script and then below that I have stuck a quick example of how you could use it.

def font_size_for_tag_cloud( total, lowest, highest, options={} )
  return nil if total.nil? or highest.nil? or lowest.nil?
  #
  # options
  maxf = options.delete( :max_font_size ) || 14
  minf = options.delete( :min_font_size ) || 11
  maxc = options.delete( :max_color ) || [ 0, 0, 0 ]
  minc = options.delete( :min_color ) || [ 156, 156, 156 ]
  hide_sizes = options.delete( :hide_sizes )
  hide_colours = options.delete( :hide_colours )
  #
  # function to work out rgb values
  def rgb_color( a, b, i, x)
    return nil if i <= 1 or x <= 1
    if a & b
      a-(Math.log(i)*(a-b)/Math.log(x)).floor
    else
      (Math.log(i)*(b-a)/Math.log(x)+a).floor
    end
  end
  #
  # work out colours
  c = []
  (0..2).each { |i| c && rgb_color( minc[i], maxc[i], total, highest ) || nil }
  colors = c.compact.empty? ? minc.join(',') : c.join(',')
  #
  # work out the font size
  spread = highest.to_f - lowest.to_f
  spread = 1.to_f if spread <= 0
  fontspread = maxf.to_f - minf.to_f
  fontstep = spread / fontspread
  size = ( minf + ( total.to_f / fontstep ) ).to_i
  size = maxf if size > maxf
  #
  # display the results
  size_txt = "font-size:#{ size.to_s }px;" unless hide_sizes
  color_txt = "color:rgb(#{ colors });" unless hide_colours
  return [ size_txt, color_txt ].join
end

and now a quick example of it’s use in a rails view. This code assumes the db find call looks something like this:

@foo = Foo.find( :all,
  :select => '*, COUNT(DISTINCT(i.id)) AS item_cnt',
  :joins => 'AS f INNER JOIN bars AS b ON f.id=b.foo_id',
  :group => 'b.id',
  :order => 'item_cnt DESC',
  :limit => 50
)

Below just loop through your db results and calls font_size_for_tag_cloud for each item, passing in the required parameters. There are a few options available but if you leave them out you should still see something useful.

<ul>
<% for f in @foo -%>
<%= content_tag( 'li', f.some_label, :style => font_size_for_tag_cloud( f.bars.size, 
  f.first.count_items, 
  f.last.count_items,  
) ) %>
<% end -%>
</ul>

That’s it! I guess I should turn it into a plugin but hey, there’s to much reading to be done on ActiveResource at the moment heyhey!

There have been 4 responses to this post.

  1. PacoGuzman

    Could you explain me the mean of the parameters of the function?

    total, is the counts for a specify tags?
    lowest, is the lowest value for all tags?
    highest, is the highest values for all tags?

  2. Dan Brickley

    Looks handy!

    Is this posted under any particular (hopefully opensource :) license, or just here for education and enlightenment?

  3. Duncan

    PacoGuzman: I have updated the description. Hopefully that will help.

    Dan: Feel free to use this code where ever you like. You might like to give me a heads up in the source somewhere just to be polite.

  4. Niel Robertson

    First of all – thanks for publishing this code. It works great. A few things i noticed:

    1) Your description of totals makes it sound like its the total # of elements in the whole set and not just for that specific item. Once i figured that out from the example it made a lot more sense (and its obvious).

    2) I can’t seem to get the color grading to work. Maybe its oriented just at shading around a core color (as I know moving through a color wheel programmatically is a mess) but any ideas you have for good min/max would help

    Otherwise – this is awesome and I’m so glad you published it! Thank you.

    I am using it to do PPC keyword analysis.. very interesting.


back to the top