Another tag cloud script for Ruby on Rails

July 6th, 2006 @ 09:09 skip to comments

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!

Your Responses

  1. PacoGuzman said on 12:02 November 5th, 2007

    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 said on 22:58 November 9th, 2007

    Looks handy!

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

  3. Duncan said on 22:24 November 21st, 2007

    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.

Leave a comment :

Please keep it polite and on topic. Show who you are with a Gravatar. Your email address is required, but won't be displayed or passed on ( that's a promise! )

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>