How to better generalize a tree?


#1

first some background info:
I have a blog application that has a comments table with the following
columns:
id
parent_id
post_id
created_at
body

here’s my model:

class Comment < ActiveRecord::Base
belongs_to :post
acts_as_tree :order => ‘created_at’

def print_children (options={})
before = options[:before] ? options[:before]
: ‘’
after = options[:after] ? options[:after]
: ‘’
before_children = options[:before_children] ?
options[:before_children] : ‘’
after_children = options[:after_children] ?
options[:after_children] : ‘’

output = ''

for child in children
  output += before
  output += child.body
  if !child.children.empty?
    output += before_children
    output += child.print_children options
    output += after_children
  end
  output += after
end
return output

end
end

the print_children method takes several options, for example, in my
view it call it like:

    <% for comment in @comments %>
  • <%= comment.body %> <% if !comment.children.empty? %>
      <%= comment.print_children :before => '
    • ', :after => '
    • ', :before_children => '
        ', :after_children => '
      ' %>
    <% end %>
  • <% end %>

@comments is an array of all the comments for the current post where
parent_id is null (each of those potentially has a tree of comments
beneath it)
that will give me all the comments in nice nested lists.

Now for the question. Is there a way to make something like this use a
partial template, such that, for each comment, I could render a
template, instead of passing several options to print_children? The
way it works now, if i want to display more comment properties (for
example, created_at, or in the future, the author) or if i wanted to
format each comment in some odd way I have to change both my template
AND my print_children method.

-Dana


#2

The solution is to use a recursive partial template, this keeps all the
display code in view where it belongs.

the model:
class Comment < ActiveRecord::Base
belongs_to :post
acts_as_tree :order => ‘created_at’
end

the main template:

    <% for comment in @comments %> <%= render :partial => 'comment', :locals => {:comment => comment} %> <% end %>

and the partial template (_comment.rhtml), notice how it renders itself:

  • <%= comment.body %>
    <% if !comment.children.empty? %>
      <% for comment in comment.children %> <%= render :partial => 'comment', :locals => {:comment => comment } %> <% end %>
    <% end %>

  • #3

    Dana wrote:
    I can’t believe I wrote the same kind of code today. I chose to use the
    :collection member of the options hash to let Rails iterate the
    collections. This just happens to be my application – a menu tree –
    but it looks pretty much like any other kind of tree:)

    BTW: This can be also accomplished very nicely with Builder::XmlMarkup
    used in a simple recursion.

    template:

      <%= render :partial => 'menu_list', :collection => @menus %>

    partial:

  • <%= menu_list.menu_name %> <% if menu_list.has_children? -%>
      <%= render(:partial => 'menu_list', :collection => menu_list.children) %>
    <% end -%>
  • the main template:

      <% for comment in @comments %> <%= render :partial => 'comment', :locals => {:comment => comment} %> <% end %>

    and the partial template (_comment.rhtml), notice how it renders itself:

  • <%= comment.body %>
    <% if !comment.children.empty? %>
      <% for comment in comment.children %> <%= render :partial => 'comment', :locals => {:comment => comment } %> <% end %>
    <% end %>