How would you model and render the threaded posts or comments in Rails? If you google the terms “rails threaded posts”, usually acts_as_tree, acts_as_nested_set or acts_as_threaded plugins are often the methods widely discussed and used. If we’re trying to model a real tree structure, they’re probably the way to go.
In the threaded posts or comments scenarios, however, we really just need to have a visual way to render the posts or comments so readers can follow the discussion thread more easily.
Here’s a simple model that we have been using and it performs quite well without complex SQL and calculations. Compared to other methods which are SQL heavy and database bound, this method is light on SQL but heavy on the view. The view needs to build parent/child tree when rendering a thread, but it’s much easier to scale out with more web servers than scale up with bigger DB.
For example, Comments table has the following columns: id (integer), parent_id (integer, nullable), level (integer, default to 0), content(string).
Model:
class ThreadedComment < ActiveRecord::Base
def save_as_reply!(parent)
self.parent_id = parent.id
self.level = parent.level + 1
self.save!
end
end
Controller:
...
def index
@comments = ThreadedComment.find(:all, :order => 'id ASC', :conditions => ["topic_id = ?", params[:id]]
end
...
end
def create
comment = ThreadedComment.new(:content => params[:content])
comment.save!
...
end
def reply
comment = ThreadedComment.new(:content => params[:content])
comment.save_as_reply!(ThreadedComment.find(params[:id])
...
end
...
View:
.comments
- threads = {}
- @comment.each do |c|
- if c.parent_id.to_i == 0
- threads[c.id] = [c,[]]
- else
- threads[c.parent_id][1] << c
- end
- @threads.each do |t|
...
...
Make sure there's a reply link on each post/comment that returns its id as parameter.