Forum: Ruby on Rails how to better generalize a tree?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Da2a8d6bd6aa2f69ec094ce6e45a14f5?d=identicon&s=25 Dana Pieluszczak (Guest)
on 2006-02-24 05:05
(Received via mailing list)
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:

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

@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
E1313ecbe13b917d683b9a5d80b9eae8?d=identicon&s=25 Dana (Guest)
on 2006-02-25 06:32
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:

<div class="comments">
<ul>
  <% for comment in @comments %>
    <%= render :partial => 'comment', :locals => {:comment => comment}
%>
  <% end %>
</ul>
</div>

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

<li>
  <div>
    <%= comment.body %>
  </div>
  <% if !comment.children.empty? %>
    <ul>
      <% for comment in comment.children %>
        <%= render :partial => 'comment', :locals => {:comment =>
comment } %>
      <% end %>
    </ul>
  <% end %>
</li>
Ef0db53920b243d6758c2f6b1306df0d?d=identicon&s=25 Steve Ross (cwd)
on 2006-02-25 08:12
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:

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

partial:

<li>
  <%= menu_list.menu_name %>
  <% if menu_list.has_children? -%>
  <ul>
    <%= render(:partial => 'menu_list', :collection =>
menu_list.children)  %>
  </ul>
<% end -%>
</li>


> the main template:
>
> <div class="comments">
> <ul>
>   <% for comment in @comments %>
>     <%= render :partial => 'comment', :locals => {:comment => comment}
> %>
>   <% end %>
> </ul>
> </div>
>
> and the partial template (_comment.rhtml), notice how it renders itself:
>
> <li>
>   <div>
>     <%= comment.body %>
>   </div>
>   <% if !comment.children.empty? %>
>     <ul>
>       <% for comment in comment.children %>
>         <%= render :partial => 'comment', :locals => {:comment =>
> comment } %>
>       <% end %>
>     </ul>
>   <% end %>
> </li>
This topic is locked and can not be replied to.