I'm in the process of learning Rails and trying to create a site with nested polymorphic comments.
I have been following this tutorial http://www.tweetegy.com/2013/04/create-nested-comments-in-rails-using-ancestry-gem/ as well as some RailsCasts (262-trees-with-ancestry and 154-polymorphic-association).
I have successfully managed to create nested polymorphic comments (when I check rails console for each comment). However, the problem I'm having is outputting the comments. They all show up on every resources' show page, not on the relevant show page. In other words, all of the comments are showing on /videos/1 and videos/2 and lessons/1 and lessons/3 (the same comments are showing on every page).
I suspect my problem is in the comments_helper.rb or /lib/commentable.rb file, however I haven't had any luck debugging the problem.
Routes
config/routes.rb
devise_for :users
resources :videos do
resources :comments
end
resources :lessons do
resources :comments
end
Controllers
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
@parent_id = params.delete(:parent_id)
@commentable = find_commentable
@comment = Comment.new( :parent_id => @parent_id,
:commentable_id => @commentable.id,
:commentable_type => @commentable.class.to_s)
end
def create
@commentable = find_commentable
@comment = @commentable.comments.build(comment_params)
@comment.user_id = current_user.id
if @comment.save
flash[:notice] = "Successfully created comment."
redirect_to @commentable
else
flash[:error] = "Error adding comment."
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def comment_params
params.require(:comment).permit(:body, :parent_id, :commentable_id, :commentable_type)
end
end
app/controllers/lessons_controller.rb
class LessonsController < ApplicationController
include Commentable
...
end
app/controllers/videos_controller.rb
class VideosController < ApplicationController
include Commentable
...
end
Models
app/models/comment.rb
class Comment < ActiveRecord::Base
has_ancestry
belongs_to :user
belongs_to :commentable, polymorphic: true
end
app/models/lesson.rb
class Lesson < ActiveRecord::Base
has_many :comments, :as => :commentable, :dependent => :destroy
end
app/models/video.rb
class Video < ActiveRecord::Base
has_many :comments, :as => :commentable, :dependent => :destroy
end
app/models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :comments, :as => :commentable, :dependent => :destroy
end
Helpers
app/helpers/comments_helper.rb
module CommentsHelper
def nested_comments(comments)
comments.map do |comment, sub_comments|
content_tag(:div, render(comment), :class => "media media-nested")
end.join.html_safe
end
end
Lib
/lib/commentable.rb
require 'active_support/concern'
module Commentable
extend ActiveSupport::Concern
included do
before_filter :comments, :only => [:show]
end
def comments
@commentable = find_commentable
@comments = @commentable.comments.arrange(:order => :created_at)
@comment = Comment.new
end
private
def find_commentable
return params[:controller].singularize.classify.constantize.find(params[:id])
end
end
Schema
create_table "comments", force: true do |t|
t.text "body"
t.integer "user_id"
t.integer "commentable_id"
t.string "commentable_type"
t.datetime "created_at"
t.datetime "updated_at"
t.string "ancestry"
end
add_index "comments", ["ancestry"], name: "index_comments_on_ancestry"
Views
Lessons & Views show.html.erb
<div class="tab-pane" id="comments">
<%= nested_comments @comments %>
<%= render "comments/form" %>
</div>
app/views/comments/_form.html.erb
<%= form_for [@commentable, @comment] do |f| %>
<%= f.hidden_field :parent_id %>
<p>
<%= f.label :body, "New comment" %>
</p>
<%= f.text_area :body, :rows => 4 %>
<p>
<%= f.submit "Post Comment" %>
</p>
<% end %>
app/views/comments/_comment.html.erb
<div class="media-body">
<h4 class="media-heading"><%= comment.user.username %></h4>
<i>
<%= comment.created_at.strftime('%b %d, %Y at %H:%M') %>
</i>
<p>
<%= simple_format comment.body %>
</p>
<div class="actions">
<%= link_to "Reply", new_polymorphic_path([@commentable, Comment.new], :parent_id => comment) %>
</div>
<%= nested_comments comment.children %>
</div>
Any feedback or ideas would be much appreciated. Thanks ;)
Figured out a solution, however now child comments appear twice.
show.html.erb