Tagging using Acts as Taggable On in Rails 5

Why Tag?

Tags are useful in situations where people are not sure what they are looking for or what's around. It helps people to explore and find related information. This means visitors to your site spend more time on your site. They are more likely to buy from you when they read your valuable content.

Basic Rails 5 App

Create a new Rails 5 app.

rails new dag --skip-spring

Create an article model.

rails g model articles name published_on:date content:text

Migrate the database.

rails db:migrate

Populate sample data using seeds.rb:

batman = Article.create! name: "Batman", content: <<-ARTICLE
Batman is a fictional character created by the artist Bob Kane and writer Bill Finger. A comic book superhero, Batman first appeared in Detective Comics #27 (May 1939), and since then has appeared primarily in publications by DC Comics. Originally referred to as "The Bat-Man" and still referred to at times as "The Batman", he is additionally known as "The Caped Crusader", "The Dark Knight", and the "World's Greatest Detective," among other titles. (from Wikipedia)
ARTICLE

superman = Article.create! name: "Superman", content: <<-ARTICLE
Superman is a fictional comic book superhero appearing in publications by DC Comics, widely considered to be an American cultural icon. Created by American writer Jerry Siegel and Canadian-born American artist Joe Shuster in 1932 while both were living in Cleveland, Ohio, and sold to Detective Comics, Inc. (later DC Comics) in 1938, the character first appeared in Action Comics #1 (June 1938) and subsequently appeared in various radio serials, television programs, films, newspaper strips, and video games. (from Wikipedia)
ARTICLE

krypton = Article.create! name: "Krypton", content: <<-ARTICLE
Krypton is a fictional planet in the DC Comics universe, and the native world of the super-heroes Superman and, in some tellings, Supergirl and Krypto the Superdog. Krypton has been portrayed consistently as having been destroyed just after Superman's flight from the planet, with exact details of its destruction varying by time period, writers and franchise. Kryptonians were the dominant people of Krypton. (from Wikipedia)
ARTICLE

lex_luthor = Article.create! name: "Lex Luthor", content: <<-ARTICLE
Lex Luthor is a fictional character, a supervillain who appears in comic books published by DC Comics. He is the archenemy of Superman, and is also a major adversary of Batman and other superheroes in the DC Universe. Created by Jerry Siegel and Joe Shuster, he first appeared in Action Comics #23 (April 1940). Luthor is described as "a power-mad, evil scientist" of high intelligence and incredible technological prowess. (from Wikipedia)
ARTICLE

robin = Article.create! name: "Robin", content: <<-ARTICLE
Robin is the name of several fictional characters appearing in comic books published by DC Comics, originally created by Bob Kane, Bill Finger and Jerry Robinson, as a junior counterpart to DC Comics superhero Batman. The team of Batman and Robin is commonly referred to as the Dynamic Duo or the Caped Crusaders. (from Wikipedia)
ARTICLE

Populate the database.

rails db:seed

Create the articles controller.

rails g controller articles index show new edit

The implementation is straightforward.

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(params[:article])
    if @article.save
      redirect_to @article, notice: "Created article."
    else
      render :new
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])
    if @article.update_attributes(params[:article])
      redirect_to @article, notice: "Updated article."
    else
      render :edit
    end
  end

  private

  def allowed_params
    params.require(:article).permit(:content, :name)
  end
end

Checkout the source code for this article Tag for the app/assets/stylesheets/layout.scss and app/views/layouts/application.html.erb. Define the routes for article resource.

resources :articles
root to: 'articles#index'

You can now play with a basic Rails 5 app that has article as the main model.

Acts as Taggable On

Add the acts-as-taggable-on gem to Gemfile.

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

Run:

bundle

You will get the error:

Bundler could not find compatible versions for gem "activerecord":
  In snapshot (Gemfile.lock):
    activerecord (= 5.0.0)

  In Gemfile:
    acts-as-taggable-on (~> 3.4) was resolved to 3.4.0, which depends on
      activerecord (< 5, >= 3.2)

    rails (~> 5.0.0) was resolved to 5.0.0, which depends on
      activerecord (= 5.0.0)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.

Point the gem to the master branch in the Gemfile.

gem 'acts-as-taggable-on', :git => 'https://github.com/mbleigh/acts-as-taggable-on'

Run:

bundle

Copy the tagging gem migration files to your project.

rails acts_as_taggable_on_engine:install:migrations

Run the migration.

rails db:migrate

Create confit/initializers/acts_as_taggable_on.rb:

ActsAsTaggableOn.force_binary_collation = true

Add:

acts_as_taggable 

to article model. In articles controller, add :tag_list to allowed_params method.

def allowed_params
  params.require(:article).permit(:content, :name, :tag_list)
end

Add text field to tag articles in form partial.

<div class='field'>
    <%= f.label :tag_list, 'Tags (separated by commas)' %><br/>
    <%= f.text_field :tag_list %>
</div>

Display the tags in article show page.

<p>Tags: <%= article.tag_list %></p>

Make the tags clickable.

<p>Tags: <%= article.tag_list.map { |t| link_to t, tag_path(t) }.join(', ') %></p>  

Define routes for the tag links:

get 'tags/:tag', to: 'articles#index', as: :tag

Change the index action to filter the article by selected tag.

if params[:tag]
  @articles = Article.tagged_with(params[:tag])
else
  @articles = Article.all
end

The articles index page display the tag as:

<a href="/tags/batman">batman</a>, <a href="/tags/comic">comic</a>

instead of creating link for the tags. Use raw method to fix this problem.

<p>Tags: <%= raw(article.tag_list.map { |t| link_to t, tag_path(t) }.join(', ')) %></p>

You can now click the link to view all the articles tagged with that particular tag.

Adding a Tag Cloud

<div id='tag_cloud'>
  <% tag_cloud Article.tag_counts, %w{s m l} do |tag, css_class| %>
    <%= link_to tag.name, tag_path(tag.name), class: css_class %>
  <% end %>
</div>

Add css to style the tag cloud, in articles.scss:

#tag_cloud {
  width: 400px;
  line-height: 1.6em;
  .s { font-size: 0.8em; }
  .m { font-size: 1.2em; }
  .l { font-size: 1.8em; }
}

You can now see the font size differ based on their frequency of occurrence.

Summary

In this article, you learned how to tag a model using acts-as-taggable-on gem in Rails 5. We were able to list all the tags as clickable links and display tag cloud.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.