Charlie Massry

Taggable Rails

October 30, 2014

The popular social media website Twitter has made features such as tagging through their system of hashtags very intuitive and fun. You can add similar functionality in your Rails app with relative ease using a gem called Acts-As-Taggable-On. First you must add the gem to your Gemfile.

gem "acts-as-taggable-on", "~> 3.4"

Then

$ bundle install

Now you can run the migration generator

rake acts_as_taggable_on_engine:install:migrations

This copies some files from the gem to your db/migrations folder. When you look through these migrations, you’ll notice that the tags are polymorphic, which is good so you can easily just slap a acts_as_taggable on your taggable model. You should then go ahead and migrate your newly generated files.

rake db:migrate

Now in the model you want taggable just add the aforementioned class method.

class Post < ActiveRecord::Base
  acts_as_taggable
  ...
end

Now you can tag your posts, at least in the console. To get it working in the browser requires a little configuration but it shouldn’t be too difficult. In your form, just add

<p>
  <%= form.label :tag_list %>
  <%= form.text_field :tag_list %>
</p>

Now you must configure it for strong params

def post_params
  params.require(:post).permit(:title, :text, :tag_list)
end

Now you can add tags, but how do you display them?

<% @post.tags.each do |tag| %>
  <%= link_to tag, tag_path(tag.name) %>
<% end %>

So you are looping through your tags and making a separate tags#show page for each of them. this will generate paths like /tags/ruby. Next you need to make a path and a controller for the tags#show action. In config/routes.rb add

resources :tags, only: [:show]

Then make the controller.

class TagsController < ApplicationController
  def show
   @posts = Post.tagged_with(params[:id]).page(params[:page]).includes(:tags)
  end
end

What this method chain is doing is finding the tags by their name, which is in params[:id], which you’ll remember we linked to the tag name to give us paths like /tags/ruby and not tags/1, .tagged_with finds tags by name and probably uses polymorphism internally to determine the type of tags it should find. The #page method is for pagination with Kaminari as this feature still works because a tag collection is just an ActiveRecord Relation object, albeit a modified one. And you’ll want to slap an #includes method to the end to get rid of any N+1 queries. The view would look the same as the posts view but you might want to add the name of the tag which you can get without even setting an instance variable of using a SQL query by just adding params[:id] where you want the name to be displayed. Now your Rails app can act as taggable.

But wait, there’s more. This gem also includes this cool feature called a tag cloud which changes the css class based on the tags frequency. You can leverage this to create larger links for more frequent tags for a cool effect. You can see this result on my posts page.

<% tag_cloud(@tags, %w(sm md lg xl)) do |tag, css_class| %>
  <%= link_to tag.name, tag_path(tag.name), class: css_class %>
<% end %>

What this does is add the different css classes to your html links and you can style them by adding to your stylesheet.

.sm {
  font-size: 1em;
}

.md {
  font-size: 1.33em;
}

.lg {
  font-size: 1.66em;
}

.xl {
  font-size: 2em;
}

Now the more frequent tags will be larger and the less frequent will be smaller. Pretty cool feature I’d say.