This is the basic structure of a threaded commenting system in Ruby on Rails (3.2.9). Comment model uses polymorphic associations to differentiate between which model it belongs to.
config/routes.rb
1 2 3 4 5 6 | resources :comments do resources :comments end resources :posts do resources :comments end |
app/models/comments.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # == Schema Information # # Table name: comments # # id :integer not null, primary key # user_id :integer # commentable_id :integer # content :text # enabled :boolean # created_at :datetime not null # updated_at :datetime not null # commentable_type :string(255) # title :string(255) # class Comment < ActiveRecord::Base attr_accessible :comment_id, :content, :enabled, :user_id, :title, :parent_id belongs_to :user belongs_to :post belongs_to :comment belongs_to :commentable, :polymorphic => true has_many :comments, :as => :commentable has_many :votes, :as => :voteable validates :user_id, presence: true validates :content, presence: true, length: { maximum: 25000 } |
app/controllers/comments_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | class CommentsController < ApplicationController before_filter :load_commentable def index @comments = @commentable.comments respond_to do |format| format.html # index.html.erb end end def show @comment = @commentable.comments.find(params[:id]) respond_to do |format| format.html # show.html.erb end end def new @comment = @commentable.comments.new @comment.parent_id = params[:parent_id] respond_to do |format| format.html # new.html.erb format.js end end def edit @comment = @commentable.comments.find(params[:id]) end def create @comment = @commentable.comments.new(params[:comment]) @comment.user = current_user @comment.content = ActionView::Base.full_sanitizer.sanitize(@comment.content) @post = Post.find(@comment.parent_id) respond_to do |format| if @comment.save format.html { redirect_to @post} #only for replies format.js #ajax post, remove for testing else format.html { render :action => "new" } end end end def update @comment = @commentable.comments.find(params[:id]) respond_to do |format| if @comment.update_attributes(params[:comment]) format.html { redirect_to @comment, :notice => 'Comment was successfully updated.' } else format.html { render :action => "edit" } end end end def destroy @comment = @commentable.comments.find(params[:id]) @comment.destroy respond_to do |format| format.html { redirect_to comments_url } end end private def load_commentable resource, id = request.path.split('/')[1, 2] @commentable = resource.singularize.classify.constantize.find(id) end end |
app/views/comments/_comment.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <li class="comment" id="comment-<%= comment.id %>"> <h5><%= comment.title %> (Score:0)</h5> <span class="detail">Posted <%= time_ago_in_words(comment.created_at) %> ago by <%= link_to comment.user.name, comment.user %></span> <div class="body"> <%= comment.content %> </div> <% if @post %> <p><%= link_to 'Add a Reply', new_comment_comment_path(comment, :parent_id => @post.id) %></p> <% unless comment.comments.empty? %> <ul class="nested_comment"> <%= render comment.comments %> </ul> <% end %> <% end %> </li> |
app/views/posts/_post.html.erb
1 2 3 4 5 6 | <li> <span class="title"><%= link_to post.title, post %></span> <span class="timestamp">Posted <%= time_ago_in_words(post.created_at) %> ago</span> <span class="postedby">by <%= link_to post.user.name, post.user %></span> <span class="content"><%= raw post.content %></span> <span class="comment_count"><%= link_to post.comments.count > 0 ? "Read " + pluralize(count_threaded(post.comments), "comment"):"No comments yet", post %></span> </li> |