Technical Pickles

Josh on Programming, Linux, and Other Technical Stuff

Adding a tag listing

My next move was to be able to have a page listing all the known tags in the system. I first refactored the tags action to by_tag, so that it reads a bit better, and so I could use tags action for my tag list action.

Here's what the new tags action looks like:

def tags
  @tags = BlogPost.tag_counts
end

This tags_counts is actually added to your model when you add acts_as_taggable to your model class. The name is a bit misleading, but it does provide you a list of tags for the given model. The Tag object also includes a count of how many things are using it, hence the name tag_counts. The acts_as_taggable homepage suggests this was intended to help you generate a tag cloud.

Here's what my rhtml looks like:

<h2>All known tags:</h2>
<ul>
  <% @tags.each do |tag| %>
    <li><%= link_to tag.name, {:controller => 'blog', :action => 'by_tag', :tag => tag.name} %></li>
  <% end %>
</ul>

And with that, I have a some nice tag listing action going on. In a future iteration, I hope to turn this into a tag cloud.

Tags
rails acts_as_taggable_on_steroids tags
Published
July 16, 2007 at 00:18

Fixing the routes for tags

After my first forray into tags, I felt like I needed to further refine and enhance my usage of them.

So we had this code chunk:

# in app/controllers/blog_controller.rb
def tags
  # FIXME do routing magic to change :id to :tag
  @blog_posts = BlogPost.find_tagged_with params[:id]
  render :action => 'all'
end
...

# in config/routes.rb
map.connect 'blog/tags/:tag', :controller => 'blog', :action => 'tags'

The issue was that I wanted to be using params[:tag], not params[:id]. The cause was that my map.connect was declared AFTER the default connect string, ie:

# Install the default route as the lowest priority.
map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id'

Apparently I didn't read the comments closely enough :) Moving it above the default route, it works like I had hoped.

Tags
tags rails acts_as_taggable_on_steroids
Published
July 14, 2007 at 16:54

That's a lot of tags!!

With the help of actsas_taggableon_steroids I now have taggable blog posts!

It was really dead simple, just followed the instructions on their page.

What good is collecting tagging information if you don't display it, so of course, I want to add it to the partial used for the blog posts. I thought to myself, well, this is a list, so wouldn't it make sense to be a <ul>? Sure, but definitely not your typical <ul> with the bullets and all. I remember reading about using CSS trickery to make <ul>s look like comma separated list, and dug up something over here.

Here's the important snippet:

ul.links { list-style: none; margin: 0; padding: 0; }
ul.links li { display: inline; }
ul.links li:after { content: ","; } 
ul.links li:last-child:after { content: ""; }

I wanted it to be a bit more generic, so I called the class 'comma-separated'. I thought a little more about it, and realized I was doing pipe-separated lists without using <ul>. After some tweaking, I ended up with this CSS:

ul.comma-separated, ul.pipe-separated { list-style: none; margin: 0; padding: 0; }
ul.comma-separated li, ul.pipe-separated li { display: inline; }
ul.comma-separated li:last-child:after, ul.pipe-separated li:last-child:after { content: ""; }

ul.comma-separated li:after { content: ","; } 
ul.pipe-separated li:after { content: " | "; }

Here's the final code snippet I added to my _blog_post.rhtml partial:

<div class="post_tags">
  <ul class="comma-separated">
    <% blog_post.tag_list.names.each do |tag_name| %>
      <li><%= link_to tag_name, :action => 'tags', :id => tag_name %></li>
    <% end %>
  </ul>
</div>

As the code suggests, I also added a new action, for displaying all items matching a particular tag. It looks a little bit like:

def tags
  # FIXME do routing magic to change :id to :tag
  @blog_posts = BlogPost.find_tagged_with params[:id]
  render :action => 'all'
end

As the fixme points out, I really would like to be using :tag, since it's kinda abusing the meaning of :id. I tried hooking this up in my config/routes.rb, but it didn't quite hold up:

map.connect 'blog/tags/:tag', :controller => 'blog', :action => 'tags'

In any event, I'd say it works well enough for now, given how little time it took.

Tags
rails acts_as_taggable_on_steroids tags
Published
June 22, 2007 at 00:59