-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Tutorial for Posts
Tutorial is based on https://www.reddit.com/r/rails/comments/2chtgw/tagging_in_rails_4/#form-t1_cjfszawia1
This assumes that you have already created posts. You can quickly generate posts by running rails generate scaffold Post title:string content:text
Gemfile
gem 'acts-as-taggable-on'
terminal
bundle install
terminal
rake acts_as_taggable_on_engine:install:migrations
rake db:migrate
This creates some tables and doesn't need to know anything specific about your models. The models and concerns for working with these tables (Tag, Tagging, etc) live inside the gem.
app/models/post.rb
class Post < ActiveRecord::Base
acts_as_taggable_on :tags
end
Note* use the syntax acts_as_taggable_on :tags
instead of acts_as_taggable
because the default implementation seems to be broken.
Now in a rails console you can:
post = Post.create
post.tag_list = "programming, ruby, rails"
post.tag_list
# => ['programming', 'ruby', 'rails']
#tag_list=
takes a string and splits it up, using the resulting substrings to find_or_create Tags, and associate them with the taggable thing (eg, Post), through Taggings. You don't need to know this.
The important thing is that, with #tag_list=
we can manage tags via a comma-separated-list in a text field in a form.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
# ...
private
def post_params
params.require(:post).permit(:title, :content, :tag_list)
end
end
The important machinery here is whitelisting tag_list from the params.
# app/views/posts/_form.html.erb
<%= form_for post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.text_field :tag_list, value: f.object.tag_list.to_s %>
<%= f.submit %>
<% end %>
Now you can create tags for stuff. How about displaying them? Couple options, I'll go through the most explicit (a TagsController with index and show actions), but they can be rolled up into other controllers/actions.
# config/routes.rb
Rails.application.routes.draw do
resources :posts
resources :tags, only: [:index, :show]
end
# app/controllers/tags_controller.rb
class TagsController < ApplicationController
def index
@tags = ActsAsTaggableOn::Tag.all
end
def show
@tag = ActsAsTaggableOn::Tag.find(params[:id])
@posts = Post.tagged_with(@tag.name)
end
end
It's unfortunate we have to do this slightly awkward workaround with Post.tagged_with(@tag.name)
in tags#show
. The ActsAsTaggableOn::Tag
model does not have a built-in relationship with its taggable types (this is a necessary consequence of some polymorphism which we're not using here). We could add one for Post, but this way is easier to demonstrate.
# app/views/acts_as_taggable_on/tags/_tag.html.erb
<%= link_to tag.name, tag_path(tag) %>
# app/views/tags/index.html.erb
<h1>Tags</h1>
<%= render @tags %>
# app/views/tags/show.html.erb
<h1><%= @tag.name %></h1>
<div><%= render @posts %></div>
Note the partial path is acts_as_taggable_on/tags/tag. This is so we can just say render @tags and let rails do its implicit magic. There are other ways to organize everything, but this is the simplest.
# app/views/posts/_post.html.erb
<h2><%= link_to post.title, post_path(post) %></h2>
# app/views/posts/index.html.erb
<h1>Posts</h1>
<%= render @posts %>
# or
<%= post.title %>
<%= post.content %>
<h4>Tags</h4>
<%= render post.tags %>
# app/views/posts/show.html.erb
<h1><%= @post.title %></h1>
<div><%= @post.body %></div>
<div><%= render @post.tags %></div>