<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>I.NFECTIO.US: Implement acts_as_threaded without a plugin</title>
    <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>"Nothing in life is worth doing if you have no life while doing it"</description>
    <item>
      <title>Implement acts_as_threaded without a plugin</title>
      <description>It's been awhile since I've posted so I thought I'd toss this out there. If you've used my acts_as_threaded plugin, you know its an off-shoot from the acts_as_nested set inside of AR itself. I've managed to create the same functionality without the use of a plugin using the native acts_as_nested_set feature of AR. The relevant code is below. The threading functionality has been moved to the model now, add the following code to your model and you are set.

&lt;div style="margin:10px;border: 1px solid #8DBA47; width:500px; height:250px; overflow:scroll; background-color: #F1F8E4;"&gt;
&lt;pre&gt;
  acts_as_nested_set :scope =&gt; :root
    
  def before_create
    # Update the child object with its parents attrs
    unless self[:parent_id].to_i.zero?
      self[:depth] = parent[:depth].to_i + 1
      self[:root_id] = parent[:root_id].to_i
    end
  end
  
  def after_create
    # Update the parent root_id with its id
    if self[:parent_id].to_i.zero?
      self[:root_id] = self[:id]
      self.save
    else
      parent.add_child self
    end
  end
  
  def parent
    @parent ||= self.class.find(self[:parent_id])
  end

&lt;/pre&gt;
&lt;/div&gt;
Your database schema will need to have the following definition:
&lt;div style="margin:10px;border: 1px solid #8DBA47; width:500px; height:250px; overflow:scroll; background-color: #F1F8E4;"&gt;
&lt;pre&gt;
create_table "my_table_name", :force =&gt; true do |t|
  t.column "root_id", :integer
  t.column "parent_id", :integer
  t.column "lft", :integer
  t.column "rgt", :integer
  t.column "depth", :integer
end
&lt;/pre&gt;
&lt;/div&gt;
The trick from this point, is that whenever you create a new thread, if it has a parent_id, then it will automatically be added as a child to that parent record. Otherwise, it will be set as a root thread. This version no longer requires that the fields have a default value of 0 relying on the fact that 'NilClass.to_i == 0'.
&lt;br /&gt;&lt;br /&gt;
Hope you enjoy this, it's come in very handy for modeling structured content in some of my apps (like categories and multi-level organizations).</description>
      <pubDate>Fri, 31 Mar 2006 12:40:00 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:854dfcb793466712366993246b8ea372</guid>
      <author>Bob Silva</author>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin</link>
      <category>Ruby on Rails</category>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Michael Carney</title>
      <description>Sorry about the formattingg above. I couldn't figure out how to get paragraphs and code in. But the idea is to set lft=1, rgt=2, and depth=3 in the after_create callback at the same timee that you set root_id=id. 

Any thoughts are appreciated.

Thanks.</description>
      <pubDate>Tue, 27 Mar 2007 09:12:55 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:2294fae3-2905-4a1d-a4e4-3db64114fea4</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-271</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Michael Carney</title>
      <description>I ran into a similar problem to Philip Tsai above where I was getting the first response to an item having a lft child with a value of zero. I followed his advice, but then ran into a different problem. If you have a post with no children and call children_count, you end up with a problem because of the NULL values for lft and rgt. I ended up with a different solution which I think feels better. I modified the original code from above as follows:
[code]
 def after_create
    # Update the parent root_id with its id
    if self[:parent_id].to_i.zero?
      self[:root_id] = self[:id]
      self[:lft] = 1
      self[:rgt] = 2
      self[:depth] = 0
      self.save
    else
      parent.add_child self
    end
  end
[/code]

The values of 1 or 2 are the default values that are assumed by nested_set if lft or rgt is NULL, but by setting them here all of the code works properly even if there are no children. For instance, calling all_children returns an empty array rather than barfing because of a poorly formed SQL query (with things like "(rgt  )" because the value of rgt is NULL). 

This seems pretty bullet proof. Does anybody see any problems with it?

Thanks.
</description>
      <pubDate>Tue, 27 Mar 2007 08:33:27 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:cc4eab32-fc48-4945-ae12-e56618f6990e</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-270</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Jonas</title>
      <description>Thanks Bob for spreading the information to noobs like me! 
Is there any way to accomplish a sort of "acts_as_list" with a nested set, so that you can place order children, roots and whatever in certain positions? 

Thanks again..!</description>
      <pubDate>Wed, 17 Jan 2007 03:35:21 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:2248b0f5-45ad-4e7c-8e9e-4ee10a08d607</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-247</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Farooq Ali</title>
      <description>I found this useful to get all ancestors in a single query:

&lt;pre&gt;
def ancestors
  if root? then []
  else
    Topic.find(:all, :conditions =&gt; "id = #{root_id} or " +
                     "(root_id = #{root_id} and depth &amp;lt; #{depth} " +
                     "and #{left_col_name} &amp;lt; #{self[left_col_name]} " +
                     "and #{right_col_name} &amp;gt; #{self[right_col_name]})",
                     :order =&gt; 'depth')
  end
end
&lt;/pre&gt;</description>
      <pubDate>Mon, 20 Nov 2006 13:52:22 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:188cdc27-52d2-44cf-b277-9c3a628ae745</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-225</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Praxedis</title>
      <description>Here's my example without the use of the plugin: 

NeoAztlan forum: &lt;a href="http://forum.neoaztlan.com" rel="nofollow"&gt;http://forum.neoaztlan.com&lt;/a&gt;

Bryan, if you're having issues, start with the acts as threaded plugin tutorial first. It describes the model and everything.</description>
      <pubDate>Mon, 09 Oct 2006 14:29:45 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:9e20d068-c167-4c8a-bc0a-881d613d49a0</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-184</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Bryan </title>
      <description>I've been trying to figure out how to get this to work.
I don't understand how the rgt  and lft attributes are generated.

I looked at the code for acts_as_nested_list and did not see where that data would be generated either.</description>
      <pubDate>Tue, 03 Oct 2006 14:47:28 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:4e041b26-222f-4d9a-8d1b-6f18e008f053</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-171</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Bryan </title>
      <description>I've been trying to figure out how to get this to work.
I don't understand how the rgt  and lft attributes are generated.

I looked at the code for acts_as_nested_list and did not see where that data would be generated either.</description>
      <pubDate>Tue, 03 Oct 2006 14:47:15 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:464f8919-a3be-4538-b74e-d69b87e79a22</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-170</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by http://www.railsroad.com</title>
      <description>I've been trying to figure out how to get this to work.
I don't understand how the rgt  and lft attributes are generated.

I looked at the code for acts_as_nested_list and did not see where that data would be generated either.</description>
      <pubDate>Tue, 03 Oct 2006 14:46:12 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:f8399350-08f0-4b1a-924f-ffba5a919d56</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-169</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Luis Carrasco</title>
      <description>Hi, I have been having trouble trying to show the nested tree as a list, i mean using UL and LI and stuff like that, to later use it whith &lt;a href="http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree" rel="nofollow"&gt;http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree&lt;/a&gt;
Any one can help me?

Thanks in advance! </description>
      <pubDate>Wed, 06 Sep 2006 23:02:51 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:f0cc92f2-1677-43a9-95b5-270968de8370</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-119</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Luis Carrasco</title>
      <description>Hi, I have been having trouble trying to show the nested tree as a list, i mean using UL and LI and stuff like that, to later use it whith &lt;a href="http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree" rel="nofollow"&gt;http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree&lt;/a&gt;
Any one can help me?

Thanks in advance!</description>
      <pubDate>Wed, 06 Sep 2006 23:02:48 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:c39f2b22-e1f5-4ff4-9867-ae2efd0253d3</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-118</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Luis Carrasco</title>
      <description>Hi, I have been having trouble trying to show the nested tree as a list, i mean using UL and LI and stuff like that, to later use it whith &lt;a href="http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree" rel="nofollow"&gt;http://www.dhtmlgoodies.com/index.html?whichScript=drag-drop-folder-tree&lt;/a&gt;
Any one can help me?

Thanks in advance!</description>
      <pubDate>Wed, 06 Sep 2006 23:02:38 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:076064a8-36b4-4da5-811e-a169745dfd69</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-117</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by philip tsai</title>
      <description>
So the problem, regarding to my first post above, was that following the screencast, I had all five column with :null = false constraints on.  Looking into the acts_as_nested_set code helped me realize this.  Taking those constraints out now works as the plug-in version.  Honestly, I like more constraints if appropriate, so the plugin version works better; however, I also prefer to augment on existing plugin -- more portable....

As for the root post... I guess I will just overwrite the method to fit the semantics that I deem appropriate.</description>
      <pubDate>Fri, 01 Sep 2006 17:10:10 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:b51d9e1c-d36e-4525-961a-1f158f1b5697</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-88</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by philip tsai</title>
      <description> the other thing that I find strange is that -- when a post  is newly created without any children, that post.root? should return true, right?  But  the code for the method, in the plug-in version as well, is the same as nested_set's -- which means that it is only a root when lft=1 and rgt  lft...  Wouldn't the semantics for at least acts_as_threaded be that a newly created post is a root by itself? </description>
      <pubDate>Thu, 31 Aug 2006 19:52:19 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:6458cb0f-ae64-4a6c-a496-e1490c203841</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-87</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by philip tsai</title>
      <description> Hello Bob,

 Is it just me or the code shown above may not work now?
 I created a new AR model.  I pasted in the code shown above. Creating the first root post (let's call it m1) works fine but when I am creating the child post, m2, as such:  m2 = Post.create(:body='this is a reply', :parent_id = m1.id) -- things look OK until I query m1.all_children which return [] and m1.children_count() return -1.   Looking into the DB table, I see that the lft field for m2 stays at 0....  What could be going wrong here?...</description>
      <pubDate>Thu, 31 Aug 2006 19:08:11 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:6f081d21-e106-48cc-a28d-3574e49ff8b2</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-86</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by philip tsai</title>
      <description> Hello Bob,

 Is it just me or the code shown above may not work now?
 I created a new AR model.  I pasted in the code shown above. Creating the first root post (let's call it m1) works fine but when I am creating the child post, m2, as such:  m2 = Post.create(:body='this is a reply', :parent_id = m1.id) -- things look OK until I query m1.all_children which return [] and m1.children_count() return -1.   Looking into the DB table, I see that the lft field for m2 stays at 0....  What could be going wrong here?...</description>
      <pubDate>Thu, 31 Aug 2006 19:07:56 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:3c429462-d969-4eaf-9a05-fc959f629939</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-85</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Bruce</title>
      <description>Hi All,

Works great, but I had to add a few things. I modified my forum migration so the depth is 0 by default for the top level forums. Also made a partial to display the forums. I had to add "px;" to the style so the indentation would happen. Here's the code for my _forum.rhtml partial:

 'show', :id = forum.id}, {'style' = 'color: #00f'}) + " " +
content_tag('span', " by #{h forum.user.login} &#183; #{forum.created_at.strftime('%b, %d %Y - %I:%M %p')}", 'style' = 'font:10px tahoma;color:#666;')),
'style' = "margin:5px 0px;padding-left:#{forum.depth*20}px;") %</description>
      <pubDate>Sun, 06 Aug 2006 22:16:41 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:93892729-9708-4178-8508-d945afb1b58c</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-43</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Mason</title>
      <description>I meant to post this on the page about the plugin.... Whoops.  I've discovered why you can't do after_save containing a save - recursive saves that cause mysql to give up.  I'll keep looking for some other way around it....  Thanks!</description>
      <pubDate>Fri, 21 Jul 2006 17:34:08 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:9421e0b6-d2fe-4c69-985a-168a168362d8</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-35</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Mason</title>
      <description>Pretty impressive stuff you've done here.

Quick question - I'm trying acts_as_threaded out on some existing data (locations where office is root and satellite-office is child).  When I call Location.add_child() on an office, it sets the lft, rgt, parent_id, and depth properly, but root_id of both parent and child stay NULL.

I see where the root_id should be set in after_create, but since the parent record already exists this never happens - it would need after_save or after_update, wouldn't it?

Thanks!</description>
      <pubDate>Fri, 21 Jul 2006 06:18:17 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:1a9aa461-631b-4e8f-9c83-1324a485a014</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-34</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Wouf</title>
      <description>First day with Ruby and Rails...
It's really impressive, all my questions have a 'minute build' reply ;)
Nice code, Thanks much !
</description>
      <pubDate>Sat, 15 Jul 2006 10:48:58 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:09369f2b-6164-4a04-8843-cf566d27afb2</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-30</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by kublaikhan55@hotmail.com</title>
      <description>Ben,
How could I have not seen that code!? Duh. 
Thanks much!
gk</description>
      <pubDate>Tue, 11 Jul 2006 23:09:49 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:623fa5ee-b9b1-4efd-8dd8-4e15cc11211a</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-29</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Bob Silva</title>
      <description>Hi Ben,

For the helper vs. partial, I simply didn't know what a partial was when I did the screen cast. I was only into Rails for a few days (hadn't read the book yet) but got really interest when I saw acts_as_nested_set. I just redid it as a parital with a collection and it is definately a better solution.

As for the pagination, it works fine for me, not sure about that.</description>
      <pubDate>Tue, 11 Jul 2006 22:12:45 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:ea85dd2b-16b0-4e65-9b49-26d186f7017b</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-28</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Ben</title>
      <description>He posted it in a code block if you scroll down just a tad here &lt;a href="http://www.railtie.net/articles/2006/02/05/rails-acts_as_threaded-plugin" rel="nofollow"&gt;http://www.railtie.net/articles/2006/02/05/rails-acts_as_threaded-plugin&lt;/a&gt;</description>
      <pubDate>Tue, 11 Jul 2006 13:37:58 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:91de9931-a3bb-4e6d-97cf-5ac3324ff559</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-27</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by kublaikhan55@hotmail.com</title>
      <description>Hi,
Could someone post the code for the module PostHelper of the demo? Some of the lines in the screencast were truncated, and, sadly, I don't have enough in me to reconstruct the missing parts. Thanks!
 </description>
      <pubDate>Mon, 10 Jul 2006 12:44:17 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:7934cf6e-73f2-43f9-a4cc-63a231b5a31a</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-26</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Ben</title>
      <description>Another thing - I'm having problems with the paginate.

When I click next, it reloads the page, but doesn't iterate to the next page.  Any ideas?</description>
      <pubDate>Sun, 09 Jul 2006 22:15:29 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:2c70c72d-4636-49e1-bf8b-67380a0794f8</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-24</link>
    </item>
    <item>
      <title>"Implement acts_as_threaded without a plugin" by Ben</title>
      <description>errr *helper method*</description>
      <pubDate>Sun, 09 Jul 2006 20:10:44 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:c53b0d54-66ae-402c-a483-cf391452014d</guid>
      <link>http://i.nfectio.us/articles/2006/03/31/implement-acts_as_threaded-without-a-plugin#comment-23</link>
    </item>
  </channel>
</rss>
