Integrating Tag Cloud widget in to your Rails project


Download Tag Cloud Rails App - Source Code(info) | Download Widget Source Code

Introduction

This tutorial assumes you are not new to Web development in general and Ruby on Rails in particular. In the example below, we will be creating a rails application. We will not use a database for our example. So our initial command line would look something like below:

We will be needing exactly one model, one controller, one layout and one partial for all our work. Lets name them tag.rb, tagcloud_controller.rb, standard.rhtml and _response.rhtml respectively. Once we create these files, our application directory should look like this:

Creating the model and controller skeleton is fairly simple. In the command prompt, type ruby script/generate controller tagcloud to generate the controller.

At this point it makes sense to download the widget files and placing them in a convenient folder. The 'public' folder under rails root should be alright.

After downloading the zip archive unzip its contents in to the public folder under rails root. Now our public/tagcloud folder should look something like this:

All the files under prototype folder will look familiar to you if you have already worked with the prototype library. Otherwise take a look at it's official website. The remaining files are all Tag Cloud widget files. If you are curious and can't wait to have a look at the internal workings of the widget, by all means go ahead and do that. When you are done with it come back and continue with the rest of the article.

Ok, back to work. Now, let's create a layout that will act as our home page and the container for our partial template. We will call this 'standard.rhtml' and place it under "app/views/layouts" directory.

Let's examine the template contents one by one:

In the head section we are importing a couple of stylesheets that will style our widget.

@import "widgets/tagCloud/tagcloud.css";
@import "widgets/dialog/diaolg.css";

Below that we need to include all the javascript files that we downloaded. If you notice, about half of the javascript includes are part of the standard prototype library. If your rails project already has these, you do not need to include the files again.

<script type="text/javascript" src="libraries/prototype/prototype.js"></script>
<script type="text/javascript" src="libraries/prototype/scriptaculous.js"></script>
<script type="text/javascript" src="libraries/prototype/effects.js"></script>
<script type="text/javascript" src="libraries/prototype/builder.js"></script>
<!--loading the window library-->
<script type="text/javascript" src="libraries/prototype/window.js"></script>
<!--loading the dialog-->
<script type="text/javascript" src="widgets/dialog/dialog.js"></script>
<!--getting the tagcloud javascript file-->
<script type="text/javascript" src='widgets/tagCloud/tagcloud.js'></script>
<!--getting the tagcloud javascript dialog file-->
<script type="text/javascript" src='widgets/tagCloud/tagCloudDialog.js'></script>
<!--getting the tagcloud interface javascript file for xml-->
<script type="text/javascript" src='interface-xml.js'></script>

Then we have the standard rails yield statement.

Once we have all the required widget files in place, it is time to embed the Tag Cloud widget into the html file. Here is the listing to do that.

<div class="container">
    <div id="tagContainer"></div>
    <script type="text/javascript">
        //<![CDATA[
		var datasource = new xmlTagCloudDataSource("add_tags","#@@#");
		var pageTagCloud = new oTagCloud('pageTagCloud',"tagContainer",{mode:'view', minimalMode:'off', tagView:"view_cloud", tagsLimit:50, dialogWin:false, tagCloudHeight:200},datasource);
        //]]>
    </script>
</div>

<p>
	<a style="font-size: small" href="javascript:void(0)" onclick="openTagCloudTray('edit');">Tag Cloud In Dialog [Edit Mode]</a>
</p>
<p>
	<input type="text" id="selectedTags" size="30" />  
	<a style="font-size: small" href="javascript:void(0)" onclick="openTagCloudTray('select','selectedTags');">Tag Cloud In Dialog [Select Mode]</a>
</p>
<script type="text/javascript">
    //<![CDATA[
    function openTagCloudTray(mode,returnTxtFieldId) {
	    oTC_Dialog = new TC_Dialog('tc_tagContainer', returnTxtFieldId, mode);
    }
    //]]>
</script>

Line number 5 is where the magic takes place. We are defining a variable named datasource and passing tagcloud/add_tags as it's first parameter. What we are doing here is quite simple. We are supplying the rails controller action name add_tags to the variable.

After the controller action, add_tags requests the model, the variable will receive a nicely formatted XML document as response. This XML document contains all the tags that the widget uses. The rest of the code in the above listing sets the widget defaults.

That completes our first part of the tutorial and now we are ready to get into the familiar rails world to generate tags and feed them to the Tag Cloud widget.

The Controller

class TagcloudController < ApplicationController
  layout "standard"
  def index
  end
  
  def add_tags
    # tags will contain all your feeds. Here I have just copied the most popular tags in Flickr
    tags = "06   africa   amsterdam   animals   architecture   art   
	england   europe   fall   family la   lake   landscape   light   live   london   macro   may   me ......."
    @xml = Tag.new(tags).to_xml
    render :partial => "response", :object => @xml
  end
  
end

Our controller is fairly simple. It has just two methods. The index method does nothing. The second method add_tags defines a variable that contains all our tags. In our example, let's copy some dummy tags from somewhere. Popular tags on Flickr is a good enough tag list for our example. Let's copy that and feed it to our tags variable. Your application might have a different source of tags. You can replace this statement with your source of tags. Once we have the tags variable filled in with tag data let's call our Tag model to conduct the business logic and finally give us back a constructed XML document. The instance variable @xml contains this XML document. This instance variable is accessible to the views for rendering. Ensure that you include layout "standard" in the beginning of your controller code.

Important Note

The tags variable contains the tags list. You will have to feed your tags list to this variable.

The Model

Model is where we construct the tags and pass it on to the controller for delivery. Here is the complete listing before we get on with the explanation.
require 'rexml/document'

class Tag
    def initialize(tags)
      xmldoc = REXML::Document

      document = xmldoc.new()
      document.add_element("response")
      document.root.add_element("tags")
      parent = document.root.elements["tags"]
      
      tags.chomp!
      tags.gsub!(/[\s]+/,",")
      tags.gsub!(/[\,]+/,",")
      tags = tags.split(",")

      tags.each do |tag|
        parent.add_element("tag",{"tagtitle"=>tag,"rank"=>rand(100),"freq"=>rand(100),"created"=>Time.now.to_f.to_i})
      end
      
      @document = document
    end
    
    def to_xml
      @document
    end
    
end

Our model starts with require 'rexml/document' statement. REXML is an XML library included as part of standard Ruby distribution. We will be using it to construct the XML document. After that, we define a class called Tag with an initializer. In the initializer we first create an XML document and get a reference to it. Then we add the root element "response" followed by another element called "tags". The parent variable contains a reference to the element that we want to add our tags to. In this case it's the tags element. At this point let's do some cleaning up of tags. Normally a 'tag' is just one word. It doesn't contain spaces or commas. The incoming tag list might contain some of these characters.

The next four lines does some cleaning job and return an array with clean tags. Now we will iterate though the array, create an element for each tag and add the required attributes. Each tag will contain four attributes. See "Important notes" section below for their description. In our example we will be using some random values for all these attributes. For your application, you can replace these values with actual values. When our iterator finishes it's job, it will create an element for each tag and append those to the parent element tags. Now our document variable contains the complete XML tree structure we just constructed. Let's assign that to an instance variable called @document. We are almost done with our model. Let's create just one more method called to_xml that returns the instance variable that contains our XML. The Controller described above, calls this method to get the XML document.

Important Notes

  1. The Tag Cloud widget expects tags in a pre-defined XML format. It also accepts JSON objects.
  2. The root element of XML document is "response" followed by another container element called "tags"
  3. The tag element is an empty element with 4 attributes.
  4. The attributes are:
    • created : Tag creation date in epoch format
    • freq : Frequency of the tag.
    • rank : Ranking of the tag.
    • tagtitle: Title of the tag

The View

Our views are fairly simple too. There is a dummy index page, which you can replace with an interface to feed the tags to the model in your application.

The following listing has been copied from the most popular tags on Flickr. You may replace this to suit your requirements:

<textarea name="tagstext" rows="14" cols="97" readonly="readonly">06   africa   amsterdam   animals   architecture   art   
england   europe   fall   family la   lake   landscape   light   live   london   macro   may   me .......</textarea>
<div>^ All my tags are up here</div>

Our _response.rhtml partial contains a single line <%= @xml %> to retrieve the xml tree structure from the controller and pass it on to the Web browser for rendering.

You are now ready to to view the results. If you have followed all instructions as described above, you should be able to see something like this.

Attachments

app_dir.gif Info on app_dir.gif 5234 bytes
public_dir.gif Info on public_dir.gif 10244 bytes
rails generate.gif Info on rails generate.gif 13119 bytes
tag_cloud_final.gif Info on tag_cloud_final.gif 145448 bytes
tagcloud_dir.jpg Info on tagcloud_dir.jpg 17639 bytes
tagcloud_rails_app.zip Info on tagcloud_rails_app.zip 298030 bytes