Rails acts_as_threaded Plugin 12

Posted by Bob Silva Mon, 06 Feb 2006 01:15:00 GMT

While tinkering around with the acts_as_nested_set act of ActiveRecord, I tried to build a threaded forum with it. I ran into some difficulties in that a nested_set is designed to only have one root. Obviously, in a threaded forum, each thread would be its own root. So after wasting a couple hours trying to make nested_set work, I decided to use what I consider to be the best part of Rails: plugins. "acts_as_threaded" was born 1 hour later.


While I used it to build a threaded forum in this demo, it's design was inspired by a different project at work. The acts_as_tree behavior could have worked, but I needed a better way of containing my children (much like in real life).


I've included an 11 minute screencast of building an ugly looking Threaded Forum, but the concepts are there. I can still remember spending many man hours with my programming team at Bravenet to design and build the forum product we offered there. If Rails existed back then, we could have built it in a day. (Leave me a comment on how the screencast turned out.)


Click the image to view the screencast of acts_as_threaded in use: (8 MB)




As usual, you can download the plugin here. I will submit it as a patch when I solidify it and create the tests.

If you are following the tutorial, here is the display code in full.

def display_threads(threads)
    content = ''
    for thread in threads
      content << content_tag('div',
                  content_tag('div',"* " + link_to("#{h thread.title}", {:action => 'show', :id => thread.id}, {'style' => 'color: #00f'}) + " " +
                  content_tag('span', " by #{h thread.name} · #{thread.created_at.strftime('%b, %d %Y - %I:%M %p')}", 'style' => 'font:10px tahoma;color:#666;')),
                  'style' => "margin:5px 0px;padding-left:#{thread.depth*20}")
    end
    content
  end


UPDATE: See how to do this without a plugin using native Rails functionality.
Comments

Leave a response

  1. Leevi Graham 5 months later:
    Hi, Im having some trouble installing this plugin. I have unzipped it to the plugins directory and followed the screen cast. However I recieve the following error: undefinded local variable or method 'acts_as_threaded' for Category::Class Any ideas? Cheers Leevi
  2. steve mcghee 5 months later:
    be sure to check out bob's next post, taking this into the Model, as well as the comment regarding using to_i. using that with this code, i also recommend another instance of to_i: padding-left:#{thread.depth*20}") = padding-left:#{thread.depth.to_i*20}") this way, you don't have to rely on mysql auto-inserting 0s for you.
  3. Bob Silva 5 months later:
    As Steve said, refer to this post about using acts_as_nested_set to acheive the same functionality without the plugin.
    http://www.railtie.net/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin
  4. Rich Collins 6 months later:
    You shouldn't use acts_as_nested_set you should use acts_as_tree with recursively cascaded eager loading
  5. philip tsai 7 months later:
    Is Rich's comment correct? How would you compare performance and features between the two?
  6. Bob Silva 7 months later:
    Philip, Depends on what you are trying to accomplish. You can accomplish a threaded relationship with both components, one is read heavy and one is write heavy (acts_as_tree and acts_as_nested_set, respectively. I prefer the acts_as_nested_set for my uses. With that said, I've extracted it out into a plugin and applied some of the existing patches (as well as a few of my own) to the implementation. Core will eventually yank acts_as_nested_set out into a plugin along with the others I imagine, so I'm not worried about support which I assume is one of the reasons Rich is suggesting to use the tree format.
  7. praxedis 8 months later:
    Hey, Bob. Just wanted to say thanks for the plugin and give you my example for it. Just launched this week. Needs to be extended a little more, but you'll get the idea. http://forum.neoaztlan.com
  8. Bob Silva 8 months later:
    Glad to hear that praxedis, be sure to look at the article here that shows how to accomplish the same thing without my plugin. Using the scope operator and a few callbacks can achieve the same results using only acts_as_nested_set which in my opinion is always the best route.
  9. Trevor Turk 9 months later:
    Sweet deal. You worked at Bravenet? Man, just hearing that name brings back so many memories. I used your tools on a bunch of my sites :)
  10. Johnson Pet 9 months later:
    Nice plugin - Out of topic: What software did you use to create the screencast?
  11. Drew Noakes 11 months later:
  12. jaldrich@digiglyph.com 11 months later:
    Hello, I'm using this plugin for a couple different models, but have found that the left and right edge values are not updated correctly when adding threads in a depth-first fashion. In my case I am creating a new thread by copying an existing one (creating a document from a template). I'm wondering if someone could provide feedback on the best way to copy a thread and avoid the problem of the incorrect edge values. Thanks, Jeff
Comments