# Help enhancing acts\_as\_nested\_set

**URL:** <https://www.ruby-forum.com/t/help-enhancing-acts-as-nested-set/62124>\
**Category:** Rails\
**Created:** [May 31, 2006, 7:05pm UTC](https://www.ruby-forum.com/t/help-enhancing-acts-as-nested-set/62124 "2006-05-31T19:05:57Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [May 31, 2006, 7:05pm UTC](https://www.ruby-forum.com/t/help-enhancing-acts-as-nested-set/62124/1 "2006-05-31T19:05:57Z")

</div>

I’m trying to enhance acts\_as\_nested\_set. Well actually I already have,  
but I’ve done a hack up job of it. I would like to contribute these  
enhancements back, but I need a little help.

I’m a complete newbie to this stuff. The only thing I new before coming  
into this project was vanilla HTML. So over the course of two weeks  
I’ve  
picked up Ruby, Rails, MySQL, Apache and Lighttpd. I had never done any  
database stuff before either. Suffice to say Rails is amazing. There  
is  
no way I could have come as far as I have this fast, if it weren’t for  
Rails. Enough of the brown nosing, on to the the real stuff.

My application needs to store store hierarchical data, and I need to  
access  
it in the manner which a nested set is ideally suited. It’s not a staff  
org chart, but the logical structure of this data is the same.  
act\_as\_nested\_set is a bit feature light. I found I needed a few more  
features.

Enhancements:  
Multiple root level objects  
Moving objects around  
A clipboard of sorts (I call it a cellar)

I have all of this working, but as I have attempted to clean up the code  
I’m running into my limited understanding of Ruby and Rails. I’m having  
trouble with what is a class method where it should be defined and  
instance  
methods and how they are accessible. Probably your typical stuff for  
newbie rubies.

Here’s a snippet from nested\_set.rb. I wanted to add some more class  
methods and had trouble accessing the left\_col\_name, etc. methods. I  
first  
converted them into class methods by prepending “self.” in front of  
them,  
only to discover that the instance methods lost the ability to access  
them.  
So I ended up defining them twice as you can see below. I am woefully  
lacking in my understanding of how this stuff gets mixed in, and the  
difference between eval, class\_eval, and module\_eval. I’m beginning to  
become quite productive on the application development side of things,  
but  
I feel I’m still at the buzz-word stage when it comes to understanding  
the  
guts of Ruby and Rails. Help! At the far bottom of this messgae I’ll  
include the entire nested\_set.rb file I created. Sorry if this is not  
the  
best way to post to this forum.

```
    def acts_as_nested_set(options = {})
      configuration = { :parent_column => "parent_id", :left_column 

```

=\>  
“lft”, :right\_column =\> “rgt”}

```
      configuration.update(options) if options.is_a?(Hash)

      class_eval <<-EOV
        include ActiveRecord::Acts::NestedSet::InstanceMethods
        def self.left_col_name() "#{configuration[:left_column]}" 

```

end  
def left\_col\_name() “#{configuration[:left\_column]}” end

```
        def self.right_col_name() "#{configuration[:right_column]}" 

```

end  
def right\_col\_name() “#{configuration[:right\_column]}” end

```
        def self.parent_column() "#{configuration[:parent_column]}" 

```

end  
def parent\_column() “#{configuration[:parent\_column]}” end  
EOV

```
      def find_all
        self.find(:all, :conditions => "#{left_col_name} > 0", 

```

:order  
=\> “#{left\_col\_name} ASC”)  
end

```
      def find_roots
        self.find(:all, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \> 0", :order =\> “#{left\_col\_name} ASC”)  
end

```
      def root_count
        self.find(:all, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \> 0").length  
end

```
      def find_cellar
        self.find(:first, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \< 0")  
end

```
      def add_root (node)
        node.reload
        raise "Node already in the hierarchy, try move?" unless

```

node.unknown?  
self.transaction {  
begin  
# get the next lft value to use for a new root node  
last = self.find(:first, :order =\> “#{right\_col\_name}  
DESC”)  
lft\_val = last[right\_col\_name]  
rescue ActiveRecord::RecordNotFound  
lft\_val = 0  
end  
node[left\_col\_name] = lft\_val + 1  
node[right\_col\_name] = lft\_val + 2  
node[parent\_column] = 0

```
            # What do to do about validation?
            node.save
          }
        end

    end

```

Ryan

My full nested\_set.rb::::

module ActiveRecord  
module Acts #:nodoc:  
module NestedSet #:nodoc:  
def self.append\_features(base)  
super  
base.extend(ClassMethods)  
end

```
  # This acts provides Nested Set functionality. Nested Set is

```

similiar to Tree, but with  
# the added feature that you can select the children and all of  
their  
descendents with  
# a single query. A good use case for this is a threaded post  
system, where you want  
# to display every reply to a comment without multiple selects.  
#  
# A google search for “Nested Set” should point you in the  
direction  
to explain the  
# database theory. I figured out a bunch of this from  
# [http://threebit.net/tutorials/nestedset/tutorial1.html](http://threebit.net/tutorials/nestedset/tutorial1.html)  
#  
# Instead of picturing a leaf node structure with children  
pointing  
back to their parent,  
# the best way to imagine how this works is to think of the parent  
entity surrounding all  
# of its children, and its parent surrounding it, etc. Assuming  
that  
they are lined up  
# horizontally, we store the left and right boundries in the  
database.  
#  
# Imagine:  
# root  
# |\_ Child 1  
# |\_ Child 1.1  
# |\_ Child 1.2  
# |\_ Child 2  
# |\_ Child 2.1  
# |\_ Child 2.2  
#  
# If my cirlces in circles description didn’t make sense, check  
out  
this sweet  
# ASCII art:  
#  
#

* * *

```
  # | Root

```

|  
# | \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ |  
# | | Child 1 | | Child 2  
| |  
# | | \_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_ | | \_\_\_\_\_\_\_\_\_\_

* * *

| |  
# | | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2  
|  
| |  
# 1 2 3\_\_\_\_\_\_\_\_\_4 5\_\_\_\_\_\_\_\_6 7 8 9\_\_\_\_\_\_\_\_\_10  
11\_\_\_\_\_\_\_12  
13 14  
# | |_**|  
|**_| |  
#  
|\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_|  
#  
# The numbers represent the left and right boundries. The table  
then  
might  
# look like this:  
# ID | PARENT | LEFT | RIGHT | DATA  
# 1 | 0 | 1 | 14 | root  
# 2 | 1 | 2 | 7 | Child 1  
# 3 | 2 | 3 | 4 | Child 1.1  
# 4 | 2 | 5 | 6 | Child 1.2  
# 5 | 1 | 8 | 13 | Child 2  
# 6 | 5 | 9 | 10 | Child 2.1  
# 7 | 5 | 11 | 12 | Child 2.2  
#  
# So, to get all children of an entry, you  
# SELECT \* WHERE CHILD.LEFT IS BETWEEN PARENT.LEFT AND  
PARENT.RIGHT  
#  
# To get the count, it’s (LEFT - RIGHT + 1)/2, etc.  
#  
# To get the direct parent, it falls back to using the PARENT\_ID  
field.  
#  
# There are instance methods for all of these.  
#  
# The structure is good if you need to group things together; the  
downside is that  
# keeping data integrity is a pain, and both adding and removing  
an  
entry  
# require a full table write.  
#  
# This sets up a before\_destroy trigger to prune the tree  
correctly  
if one of its  
# elements gets deleted.  
#  
module ClassMethods  
# Configuration options are:  
#  
# \* +parent\_column+ - specifies the column name to use for  
keeping  
the position integer (default: parent\_id)  
# \* +left\_column+ - column name for left boundry data, default  
“lft”  
# \* +right\_column+ - column name for right boundry data, default  
“rgt”  
def acts\_as\_nested\_set(options = {})  
configuration = { :parent\_column =\> “parent\_id”, :left\_column  
=\>  
“lft”, :right\_column =\> “rgt”}

```
      configuration.update(options) if options.is_a?(Hash)

      class_eval <<-EOV
        include ActiveRecord::Acts::NestedSet::InstanceMethods
        def self.left_col_name() "#{configuration[:left_column]}" 

```

end  
def left\_col\_name() “#{configuration[:left\_column]}” end

```
        def self.right_col_name() "#{configuration[:right_column]}" 

```

end  
def right\_col\_name() “#{configuration[:right\_column]}” end

```
        def self.parent_column() "#{configuration[:parent_column]}" 

```

end  
def parent\_column() “#{configuration[:parent\_column]}” end  
EOV

```
      def find_all
        self.find(:all, :conditions => "#{left_col_name} > 0", 

```

:order  
=\> “#{left\_col\_name} ASC”)  
end

```
      def find_roots
        self.find(:all, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \> 0", :order =\> “#{left\_col\_name} ASC”)  
end

```
      def root_count
        self.find(:all, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \> 0").length  
end

```
      def find_cellar
        self.find(:first, :conditions => "#{parent_column} = 0 AND

```

#{left\_col\_name} \< 0")  
end

```
      def add_root (node)
        node.reload
        raise "Node already in the hierarchy, try move?" unless

```

node.unknown?  
self.transaction {  
begin  
# get the next lft value to use for a new root node  
last = self.find(:first, :order =\> “#{right\_col\_name}  
DESC”)  
lft\_val = last[right\_col\_name]  
rescue ActiveRecord::RecordNotFound  
lft\_val = 0  
end  
node[left\_col\_name] = lft\_val + 1  
node[right\_col\_name] = lft\_val + 2  
node[parent\_column] = 0

```
            # What do to do about validation?
            node.save
          }
        end

    end
  end

  module InstanceMethods
    # Returns true is this is a root node.
    def root?
      (self[parent_column] == 0) && (self[right_col_name] >

```

self[left\_col\_name])  
end

```
    # Returns true is this is a child node
    def child?
      !(self[parent_column] == 0) && (self[right_col_name] >

```

self[left\_col\_name])  
end

```
    # Returns true if we have no idea what this is
    def unknown?
      !root? && !child?
    end

    # Adds a child to this object in the tree. If this object 

```

hasn’t  
been initialized,  
# it gets set up as a root node. Otherwise, this method will  
update all of the  
# other elements in the tree and shift them to the right,  
keeping  
everything  
# balanced.  
def add\_child( child )  
self.reload  
child.reload

```
    if self.unknown?
      ## Make self a root node if it, too is unknown
      self.class.add_root self
    end

      if child.unknown?
        # OK, add as the last child into self
        # we need to add and shift everything else to the right
        self.class.transaction {
          child[parent_column] = self.id
          right_bound = self[right_col_name]
          child[left_col_name] = right_bound
          child[right_col_name] = right_bound + 1
          self[right_col_name] += 2
          self.class.update_all( "#{left_col_name} = 

```

(#{left\_col\_name}

- 2)", “#{left\_col\_name} \>= #{right\_bound}” )  
self.class.update\_all( “#{right\_col\_name} =  
(#{right\_col\_name} + 2)”, “#{right\_col\_name} \>= #{right\_bound}” )  
self.save  
child.save  
}  
else  
raise “Child already inserted into hierarchy, try move?”  
end  
end

self[left\_col\_name]  
\< item[right\_col\_name])  
end

```
  def is_in_cellar?
      (self[right_col_name] < 0)
    end

    def is_cellar_empty?
      self.class.find(:first, :conditions => "#{parent_column} = 0 

```

AND  
#{right\_col\_name} \< 0").nil?  
end

```
    def empty_cellar
      self.class.transaction {
        self.class.delete_all( "#{right_col_name} < 0" )
      }
    end

    def move_to_cellar (force = false)
      self.reload
      raise "item not properly nested" unless !unknown?

      raise "cellar not empty" unless is_cellar_empty? || force == 

```

true

```
      empty_cellar if !is_cellar_empty?

      self.class.transaction {
        right_bound = self[right_col_name]
        move_amount = right_bound + 1
        size = right_bound - self[left_col_name] + 1
        # Move self to cellar
        self.class.update_all( "#{right_col_name} = 

```

(#{right\_col\_name}

- #{move\_amount}), #{left\_col\_name} = (#{left\_col\_name} -  
#{move\_amount})",  
“#{left\_col\_name} \>= #{self[left\_col\_name]} AND #{left\_col\_name} \<=  
#{self[right\_col\_name]}” )  
self.reload  
# Close gap after moving self  
self.class.update\_all( "#{right\_col\_name} =  
(#{right\_col\_name}
- #{size} )", “#{right\_col\_name} \> #{right\_bound}” )  
self.class.update\_all( "#{left\_col\_name} = (#{left\_col\_name}
- 

#{size})", “#{left\_col\_name} \> #{right\_bound}” )  
self[parent\_column] = 0  
self.save  
}  
true  
end

```
    def move (where, target, option = nil)
      self.reload
      target.reload
      if (target.unknown? || target.is_in_cellar? ||

```

target.is\_child\_of?(self) )  
raise “Invalid desitination”  
end  
self.class.transaction {  
size = self[right\_col\_name] - self[left\_col\_name] + 1  
case where  
when ‘into’  
case option  
when ‘front’  
dest = target[left\_col\_name] + 1  
bound\_for\_left\_match = dest  
bound\_for\_right\_match = dest  
new\_parent = target.id  
when ‘back’  
dest = target[right\_col\_name]  
bound\_for\_left\_match = dest + 1  
bound\_for\_right\_match = dest  
new\_parent = target.id  
else raise “invalid option = #{option}”  
end  
when ‘before’  
dest = target[left\_col\_name]  
bound\_for\_left\_match = dest  
bound\_for\_right\_match = dest  
new\_parent = target[parent\_column]  
when ‘after’  
dest = target[right\_col\_name] + 1  
bound\_for\_left\_match = dest  
bound\_for\_right\_match = dest  
new\_parent = target[parent\_column]  
else raise “invalid where = #{where}”  
end  
#Make hole to move into  
self.class.update\_all( “#{left\_col\_name} = (#{left\_col\_name}  
+  
#{size})”, “#{left\_col\_name} \>= #{bound\_for\_left\_match}” )  
self.class.update\_all( "#{right\_col\_name} =  
(#{right\_col\_name}

- #{size} )", “#{right\_col\_name} \>= #{bound\_for\_right\_match}” )  
self.reload

(#{right\_col\_name}

- #{move\_amount}), #{left\_col\_name} = (#{left\_col\_name} +  
#{move\_amount})",  
“#{left\_col\_name} \>= #{self[left\_col\_name]} AND #{left\_col\_name} \<=  
#{self[right\_col\_name]}” )

(#{left\_col\_name}

- #{size})", “#{left\_col\_name} \>= #{right\_bound}” )  
self.class.update\_all( “#{right\_col\_name} =  
(#{right\_col\_name} - #{size} )”, “#{right\_col\_name} \>= #{right\_bound}”  
)  
end

#{self[left\_col\_name]} and #{self[right\_col\_name]})", :order =\>  
“#{left\_col\_name} ASC” )  
end

```
    # Returns a set of all of its children and nested children
    def all_children
      self.class.find(:all, :conditions => "(#{left_col_name} >

```

#{self[left\_col\_name]}) and (#{right\_col\_name} \<  
#{self[right\_col\_name]})",  
:order =\> “#{left\_col\_name} ASC” )  
end

```
    # Returns a set of only this entry's immediate children
    def direct_children
      self.class.find(:all, :conditions => "#{parent_column} =

```

#{self.id}", :order =\> “#{left\_col\_name} ASC” )  
end

```
  # Returns the number of direct children of this object
  def direct_children_count
      self.class.find(:all, :conditions => "#{parent_column} =

```

#{self.id}").length  
end

```
    # Prunes a branch off of the tree, shifting all of the elements 

```

on  
the right  
# back to the left so the counts still work.  
def before\_destroy  
return if self[right\_col\_name].nil? ||  
self[left\_col\_name].nil?  
dif = self[right\_col\_name] - self[left\_col\_name] + 1

```
      self.class.transaction {
        self.class.delete_all( "#{left_col_name} >

```

## #{self[left\_col\_name]} and #{right\_col\_name} \< #{self[right\_col\_name]}" ) self.class.update\_all( "#{left\_col\_name} = (#{left\_col\_name}

#{dif})", “#{left\_col\_name} \>= #{self[right\_col\_name]}” )  
self.class.update\_all( "#{right\_col\_name} =  
(#{right\_col\_name}

- #{dif} )", “#{right\_col\_name} \>= #{self[right\_col\_name]}” )  
}  
end  
end  
end  
end  
end

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [July 4, 2006, 10:55am UTC](https://www.ruby-forum.com/t/help-enhancing-acts-as-nested-set/62124/2 "2006-07-04T10:55:05Z")

</div>

Hi Ryan,

Le 31 mai 06, à 19:00, removed\_email\_address@domain.invalid a écrit :

> I’m trying to enhance acts\_as\_nested\_set. Well actually I already  
> have,  
> but I’ve done a hack up job of it. I would like to contribute these  
> enhancements back, but I need a little help.

I posted a patch some months ago:  
[http://dev.rubyonrails.org/ticket/2795](http://dev.rubyonrails.org/ticket/2795)

I converted it to a plugin, I’ll release soon a public svn uri for this.  
Maybe you could help correct it instead of redo the job ?

## Jean-Christophe M.

Symétrie, édition de musique et services multimédia  
30 rue Jean-Baptiste Say  
69001 LYON (FRANCE)  
tél +33 (0)478 29 52 14  
fax +33 (0)478 30 01 11  
web [www.symetrie.com](http://www.symetrie.com)
