Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

new implementation of nested set based off of better nested set. Fixe…

…d some bugs, removed tons of duplication

git-svn-id: https://source.collectiveidea.com/public/rails/plugins/awesome_nested_set@243 2c0bb46f-6917-0410-8ab9-f4bf1162979e
  • Loading branch information...
commit 6f6b48ee7c57d7dacbf3fcac9f75f0f0ff3e48de 0 parents
@bkeepers bkeepers authored
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2007 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 README
@@ -0,0 +1,13 @@
+AwesomeNestedSet
+================
+
+Introduction goes here.
+
+
+Example
+=======
+
+Example goes here.
+
+
+Copyright (c) 2007 [name of plugin creator], released under the MIT license
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the awesome_nested_set plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the awesome_nested_set plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'AwesomeNestedSet'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
BIN  awesome_nested_set.sqlite3.db
Binary file not shown
10 init.rb
@@ -0,0 +1,10 @@
+require 'awesome_nested_set'
+require 'awesome_nested_set_helper'
+
+ActiveRecord::Base.class_eval do
+ include CollectiveIdea::Acts::NestedSet
+end
+
+ActionView::Base.class_eval do
+ include CollectiveIdea::Acts::NestedSetHelper
+end
0  install.rb
No changes.
473 lib/awesome_nested_set.rb
@@ -0,0 +1,473 @@
+module CollectiveIdea
+ module Acts #:nodoc:
+ module NestedSet #:nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ # better_nested_set ehances the core nested_set tree functionality provided in ruby_on_rails.
+ #
+ # This acts provides Nested Set functionality. Nested Set is a smart way to implement
+ # an _ordered_ tree, with the added feature that you can select the children and all of their
+ # descendants with a single query. The drawback is that insertion or move need some complex
+ # sql queries. But everything is done here by this module!
+ #
+ # Nested sets are appropriate each time you want either an orderd tree (menus,
+ # commercial categories) or an efficient way of querying big trees (threaded posts).
+ #
+ # == API
+ # Methods names are aligned on Tree's ones as much as possible, to make replacment from one
+ # by another easier, except for the creation:
+ #
+ # in acts_as_tree:
+ # item.children.create(:name => "child1")
+ #
+ # in acts_as_nested_set:
+ # # adds a new item at the "end" of the tree, i.e. with child.left = max(tree.right)+1
+ # child = MyClass.new(:name => "child1")
+ # child.save
+ # # now move the item to its right place
+ # child.move_to_child_of my_item
+ #
+ # You can use:
+ # * move_to_child_of
+ # * move_to_right_of
+ # * move_to_left_of
+ # and pass them an id or an object.
+ #
+ # Other methods added by this mixin are:
+ # * +root+ - root item of the tree (the one that has a nil parent; should have left_column = 1 too)
+ # * +roots+ - root items, in case of multiple roots (the ones that have a nil parent)
+ # * +level+ - number indicating the level, a root being level 0
+ # * +ancestors+ - array of all parents, with root as first item
+ # * +self_and_ancestors+ - array of all parents and self
+ # * +siblings+ - array of all siblings, that are the items sharing the same parent and level
+ # * +self_and_siblings+ - array of itself and all siblings
+ # * +children_count+ - count of all immediate children
+ # * +children+ - array of all immediate childrens
+ # * +all_children+ - array of all children and nested children
+ # * +full_set+ - array of itself and all children and nested children
+ #
+ # These should not be useful, except if you want to write direct SQL:
+ # * +left_column_name+ - name of the left column passed on the declaration line
+ # * +right_column_name+ - name of the right column passed on the declaration line
+ # * +parent_column_name+ - name of the parent column passed on the declaration line
+ #
+ # recommandations:
+ # Don't name your left and right columns 'left' and 'right': these names are reserved on most of dbs.
+ # Usage is to name them 'lft' and 'rgt' for instance.
+ #
+ 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"
+ # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
+ # (if that hasn't been already) and use that as the foreign key restriction. It's also possible
+ # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
+ # Example: <tt>acts_as_nested_set :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
+ def acts_as_nested_set(options = {})
+ options = {
+ :parent_column => 'parent_id',
+ :left_column => 'lft',
+ :right_column => 'rgt'
+ }.merge(options)
+
+ if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
+ options[:scope] = "#{options[:scope]}_id".intern
+ end
+
+ write_inheritable_attribute(:acts_as_nested_set_options, options)
+ class_inheritable_reader :acts_as_nested_set_options
+
+ # no bulk assignment
+ attr_protected acts_as_nested_set_options[:left_column].intern,
+ acts_as_nested_set_options[:right_column].intern,
+ acts_as_nested_set_options[:parent_column].intern
+ # no assignment to structure fields
+ module_eval <<-"end_eval", __FILE__, __LINE__
+ def #{acts_as_nested_set_options[:left_column]}=(x)
+ raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:left_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
+ end
+ def #{acts_as_nested_set_options[:right_column]}=(x)
+ raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:right_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
+ end
+ def #{acts_as_nested_set_options[:parent_column]}=(x)
+ raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{acts_as_nested_set_options[:parent_column]}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
+ end
+ end_eval
+
+ include InstanceMethods
+ include Comparable
+ extend ClassMethods
+ end
+
+ def roots(multiplicity = :all, *args)
+ with_scope(:find => {
+ :conditions => {acts_as_nested_set_options[:parent_column] => nil},
+ :order => acts_as_nested_set_options[:left_column] }) do
+ find(multiplicity, *args)
+ end
+ end
+
+ # Returns the first root
+ def root
+ roots(:first)
+ end
+
+ end
+
+ module InstanceMethods
+
+ def left_column_name
+ acts_as_nested_set_options[:left_column]
+ end
+
+ def right_column_name
+ acts_as_nested_set_options[:right_column]
+ end
+
+ def parent_column_name
+ acts_as_nested_set_options[:parent_column]
+ end
+
+ def parent_id
+ self[parent_column_name]
+ end
+
+ def left
+ self[left_column_name]
+ end
+
+ def right
+ self[right_column_name]
+ end
+
+ # Returns true if this is a root node.
+ def root?
+ parent_id.nil? && left == 1
+ end
+
+ # Returns true is this is a child node
+ def child?
+ !parent_id.nil? && left > 1
+ end
+
+ # order by left column
+ def <=>(x)
+ left <=> x.left
+ 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.
+ #
+ # Deprecated, will be removed in next versions
+ def add_child( child )
+ self.reload
+ child.reload
+
+ if child.root?
+ raise ActiveRecord::ActiveRecordError, "Adding sub-tree isn\'t currently supported"
+ else
+ if ( (self[left_column_name] == nil) || (right == nil) )
+ # Looks like we're now the root node! Woo
+ self[left_column_name] = 1
+ self[right_column_name] = 4
+
+ # What do to do about validation?
+ return nil unless self.save
+
+ child[acts_as_nested_set_options[:parent_column]] = self.id
+ child[left_column_name] = 2
+ child[right_column_name]= 3
+ return child.save
+ else
+ # OK, we need to add and shift everything else to the right
+ child[acts_as_nested_set_options[:parent_column]] = self.id
+ right_bound = right
+ child[left_column_name] = right_bound
+ child[right_column_name] = right_bound + 1
+ self[right_column_name] += 2
+ self.class.transaction {
+ self.class.update_all( "#{left_column_name} = (#{left_column_name} + 2)", "#{acts_as_nested_set_options[:scope]} AND #{left_column_name} >= #{right_bound}" )
+ self.class.update_all( "#{right_column_name} = (#{right_column_name} + 2)", "#{acts_as_nested_set_options[:scope]} AND #{right_column_name} >= #{right_bound}" )
+ self.save
+ child.save
+ }
+ end
+ end
+ end
+
+ # Returns root
+ def root
+ self_and_ancestors(:first)
+ end
+
+ # Returns the parent
+ def parent
+ self.class.find(parent_id) if parent_id
+ end
+
+ # Returns the array of all parents and self
+ def self_and_ancestors(multiplicity = :all, *args)
+ with_nested_set_scope do
+ with_find_scope(:conditions => "#{left_column_name} <= #{left} AND #{right_column_name} >= #{right}") { self.class.find(multiplicity, *args) }
+ end
+ end
+
+ # Returns an array of all parents
+ def ancestors(*args)
+ without_self { self_and_ancestors(*args) }
+ end
+
+ # Returns the array of all children of the parent, included self
+ def self_and_siblings(multiplicity = :all, *args)
+ with_nested_set_scope do
+ scope = if parent_id.nil?
+ {self.class.primary_key => self}
+ else
+ {parent_column_name => parent_id}
+ end
+ with_find_scope(:conditions => scope) { self.class.find(multiplicity, *args) }
+ end
+ end
+
+ # Returns the array of all children of the parent, except self
+ def siblings(*args)
+ without_self { self_and_siblings(*args) }
+ end
+
+ # Returns the level of this object in the tree
+ # root level is 0
+ def level
+ if parent_id.nil?
+ 0
+ else
+ with_nested_set_scope do
+ self.class.count(:conditions => "(#{left_column_name} < #{left} AND #{right_column_name} > #{right})")
+ end
+ end
+ end
+
+ # Returns the number of nested children of this object.
+ def children_count
+ (right - left - 1) / 2
+ end
+
+ # Returns a set of itself and all of its nested children
+ def self_and_descendants(multiplicity = :all, *args)
+ with_nested_set_scope do
+ with_find_scope(:conditions => "#{left_column_name} >= #{left}
+ AND #{right_column_name} <= #{right}"
+ ) { self.class.find(multiplicity, *args) }
+ end
+ end
+
+ # Returns a set of all of its children and nested children
+ def descendants(*args)
+ without_self { self_and_descendants(*args) }
+ end
+
+ # Returns a set of only this entry's immediate children
+ def children(multiplicity = :all, *args)
+ with_nested_set_scope do
+ with_find_scope(:conditions => {parent_column_name => self}) do
+ self.class.find(multiplicity, *args)
+ end
+ end
+ end
+
+ def is_or_descends_from?(other)
+ other.left <= self.left && self.left < other.right
+ end
+
+ def is_or_is_descendant_of?(other)
+ self.left <= other.left && other.left < self.right
+ end
+
+ # Find the first sibling to the right
+ def left_sibling
+ if parent_id.nil?
+ nil
+ else
+ with_nested_set_scope do
+ self.class.find(:first, :conditions => "#{left_column_name} < #{left}
+ AND #{parent_column_name} = #{parent_id}")
+ end
+ end
+ end
+
+ # Find the first sibling to the right
+ def right_sibling
+ if parent_id.nil?
+ nil
+ else
+ with_nested_set_scope do
+ self.class.find(:first, :conditions => "#{left_column_name} > #{left}
+ AND #{parent_column_name} = #{parent_id}"
+ )
+ end
+ end
+ end
+
+ # Shorthand method for finding the left sibling and moving to the left of it.
+ def move_left
+ self.move_to_left_of(self.left_sibling)
+ end
+
+ # Shorthand method for finding the right sibling and moving to the right of it.
+ def move_right
+ self.move_to_right_of(self.right_sibling)
+ end
+
+ # Move the node to the left of another node (you can pass id only)
+ def move_to_left_of(node)
+ self.move_to node, :left
+ end
+
+ # Move the node to the left of another node (you can pass id only)
+ def move_to_right_of(node)
+ self.move_to node, :right
+ end
+
+ # Move the node to the child of another node (you can pass id only)
+ def move_to_child_of(node)
+ self.move_to node, :child
+ end
+
+ protected
+
+ def without_self
+ with_find_scope(:conditions => ["#{self.class.primary_key} != ?", self]) do
+ yield
+ end
+ end
+
+ def with_find_scope(scope)
+ self.class.send(:with_scope, :find => scope) { yield }
+ end
+
+ def with_nested_set_scope
+ if scope_column = acts_as_nested_set_options[:scope]
+ self.class.send(:with_scope, :find => {
+ :conditions => {scope_column => self[scope_column]},
+ :order => left_column_name
+ }) { yield }
+ else
+ yield
+ end
+ end
+
+ # on creation, set automatically lft and rgt to the end of the tree
+ def before_create
+ maxright = with_nested_set_scope { self.class.maximum(right_column_name) } || 0
+ # adds the new node to the right of all existing nodes
+ self[left_column_name] = maxright + 1
+ self[right_column_name] = maxright + 2
+ 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 right.nil? || left.nil?
+ diff = right - left + 1
+
+ with_nested_set_scope do
+ self.class.transaction do
+ self.class.delete_all("#{left_column_name} > #{left} AND #{right_column_name} < #{right}")
+ self.class.update_all("#{left_column_name} = (#{left_column_name} - #{diff})",
+ "#{left_column_name} >= #{right}")
+ self.class.update_all("#{right_column_name} = (#{right_column_name} - #{diff} )",
+ "#{right_column_name} >= #{right}" )
+ end
+ end
+ end
+
+ def move_to(target, position)
+ raise ActiveRecord::ActiveRecordError, "You cannot move a new node" if self.id.nil?
+
+ # extent is the width of the tree self and children
+ extent = right - left + 1
+
+ # load object if node is not an object
+ target = self.class.find(target) if !(self.class === target)
+
+ # detect impossible move
+ if (left <= target.left && target.left <= right) or (left <= target.right && target.right <= right)
+ raise ActiveRecord::ActiveRecordError, "Impossible move, target node cannot be inside moved tree."
+ end
+
+ # compute new left/right for self
+ if position == :child
+ if target.left < left
+ new_left = target.left + 1
+ new_right = target.left + extent
+ else
+ new_left = target.left - extent + 1
+ new_right = target.left
+ end
+ elsif position == :left
+ if target.left < left
+ new_left = target.left
+ new_right = target.left + extent - 1
+ else
+ new_left = target.left - extent
+ new_right = target.left - 1
+ end
+ elsif position == :right
+ if target.right < right
+ new_left = target.right + 1
+ new_right = target.right + extent
+ else
+ new_left = target.right - extent + 1
+ new_right = target.right
+ end
+ else
+ raise ActiveRecord::ActiveRecordError, "Position should be either left or right ('#{position}' received)."
+ end
+
+ # boundaries of update action
+ b_left, b_right = [left, new_left].min, [right, new_right].max
+
+ # Shift value to move self to new position
+ shift = new_left - left
+
+ # Shift value to move nodes inside boundaries but not under self_and_children
+ updown = (shift > 0) ? -extent : extent
+
+ # change nil to NULL for new parent
+ if position == :child
+ new_parent = target.id
+ else
+ new_parent = target[acts_as_nested_set_options[:parent_column]].nil? ? 'NULL' : target[acts_as_nested_set_options[:parent_column]]
+ end
+
+ # update and that rules
+ self.class.update_all( "#{left_column_name} = CASE \
+ WHEN #{left_column_name} BETWEEN #{left} AND #{right} \
+ THEN #{left_column_name} + #{shift} \
+ WHEN #{left_column_name} BETWEEN #{b_left} AND #{b_right} \
+ THEN #{left_column_name} + #{updown} \
+ ELSE #{left_column_name} END, \
+ #{right_column_name} = CASE \
+ WHEN #{right_column_name} BETWEEN #{left} AND #{right} \
+ THEN #{right_column_name} + #{shift} \
+ WHEN #{right_column_name} BETWEEN #{b_left} AND #{b_right} \
+ THEN #{right_column_name} + #{updown} \
+ ELSE #{right_column_name} END, \
+ #{acts_as_nested_set_options[:parent_column]} = CASE \
+ WHEN #{self.class.primary_key} = #{self.id} \
+ THEN #{new_parent} \
+ ELSE #{acts_as_nested_set_options[:parent_column]} END",
+ acts_as_nested_set_options[:scope] )
+ self.reload
+ end
+
+ end
+
+ end
+ end
+end
42 lib/awesome_nested_set_helper.rb
@@ -0,0 +1,42 @@
+module CollectiveIdea
+ module Acts #:nodoc:
+
+ # This module provides some helpers for the model classes using acts_as_nested_set.
+ # It is included by default in all views. If you need to remove it, edit the last line
+ # of init.rb.
+ #
+ module NestedSetHelper
+ # Returns options for select.
+ # You can exclude some items from the tree.
+ # You can pass a block receiving an item and returning the string displayed in the select.
+ #
+ # == Usage
+ # Default is to use the whole tree and to print the first string column of your model.
+ # You can tweak this by passing your parameters, or better, pass a block that will receive
+ # an item from your nested set tree and that should return the line with the link.
+ #
+ # == Examples
+ #
+ # nested_set_options_for_select(Category)
+ #
+ # # show only a part of the tree, and exclude a category and its subtree
+ # nested_set_options_for_select(selected_category, :exclude => category)
+ #
+ # # add a custom string
+ # nested_set_options_for_select(Category, :exclude => category) { |item| "#{'&nbsp;' * item.level}#{item.name} (#{item.url})" }
+ #
+ # == Params
+ # * +class_or_item+ - Class name or item to start the display with
+ # * +text_column+ - the title column, defaults to the first string column of the model
+ # * +&block+ - a block { |item| ... item.name }
+ # If no block passed, uses {|item| "#{'··' * item.level}#{item[text_column]}"}
+ def nested_set_options_for_select(item)
+ # find class
+ item = item.root if item.is_a?(Class)
+ raise 'Not a nested set model !' if !item.respond_to? :acts_as_nested_set_options
+ item.self_and_descendants.map {|i| [yield(i), i.id] }
+ end
+ end
+ end
+end
+
118 spec/awesome_nested_set_spec.rb
@@ -0,0 +1,118 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe "acts_as_nested_set" do
+ class Default < ActiveRecord::Base
+ acts_as_nested_set
+ end
+ attr_accessor :options
+ before do
+ @options = Default.acts_as_nested_set_options
+ end
+
+ it "should set :left_column default to 'lft'" do
+ options[:left_column].should == 'lft'
+ end
+
+ it "should set :right_column default to 'rgt'" do
+ options[:right_column].should == 'rgt'
+ end
+
+ it "should set :parent_column default to 'parent_id'" do
+ options[:parent_column].should == 'parent_id'
+ end
+
+ it "should set :scope default to nil" do
+ options[:scope].should be_nil
+ end
+
+ it "should protect :left_column from being assigned" do
+ lambda { Category.new.lft = 1 }.should raise_error(ActiveRecord::ActiveRecordError)
+ end
+
+ it "should protect :right_column from being assigned" do
+ lambda { Category.new.rgt = 1 }.should raise_error(ActiveRecord::ActiveRecordError)
+ end
+
+ it "should protect :parent_column from being assigned" do
+ lambda { Category.new.parent_id = 1 }.should raise_error(ActiveRecord::ActiveRecordError)
+ end
+
+end
+
+describe "acts_as_nested_set scoped with symbol" do
+ class Scoped < ActiveRecord::Base
+ acts_as_nested_set :scope => :organization
+ end
+
+ it "should append _id to the symbol" do
+ Scoped.acts_as_nested_set_options[:scope].should == :organization_id
+ end
+
+end
+
+describe "acts_as_nested_set.roots" do
+ fixtures :categories
+
+ before do
+ @roots = Category.roots
+ end
+
+ it "should find all records without a parent_id" do
+ @roots.should == Category.find_all_by_parent_id(nil)
+ end
+
+end
+
+describe "acts_as_nested_set.root" do
+ fixtures :categories
+
+ before do
+ @root = Category.root
+ end
+
+ it "should find first record without a parent_id" do
+ @root.should == categories(:top_level)
+ end
+
+end
+
+describe "acts_as_nested_set#children" do
+ fixtures :categories
+
+ before do
+ @category = categories(:top_level)
+ @children = @category.children
+ end
+
+ it "should include direct descendents" do
+ @children.each {|c| c.parent_id.should == @category.id }
+ end
+end
+
+describe "acts_as_nested_set#root" do
+ fixtures :categories
+
+ before do
+ @child = categories(:child_3)
+ @root = categories(:top_level)
+ end
+
+ it "should return the root of the tree" do
+ @child.root.should == @root
+ end
+
+end
+
+describe "acts_as_nested_set#parent" do
+ fixtures :categories
+
+ before do
+ @child = categories(:child_2_1)
+ @parent = categories(:child_2)
+ end
+
+ it "should return the parent" do
+ @child.parent.should == @parent
+ end
+
+end
3  spec/db/database.yml
@@ -0,0 +1,3 @@
+sqlite3:
+ adapter: sqlite3
+ dbfile: awesome_nested_set.sqlite3.db
16 spec/db/schema.rb
@@ -0,0 +1,16 @@
+ActiveRecord::Schema.define(:version => 0) do
+
+ create_table :categories, :force => true do |t|
+ t.column :name, :string
+ t.column :parent_id, :integer
+ t.column :lft, :integer
+ t.column :rgt, :integer
+ end
+
+ create_table :departments, :force => true do |t|
+ t.column :name, :string
+ t.column :parent_id, :integer
+ t.column :lft, :integer
+ t.column :rgt, :integer
+ end
+end
1,006 spec/debug.log
1,006 additions, 0 deletions not shown
34 spec/fixtures/categories.yml
@@ -0,0 +1,34 @@
+top_level:
+ id: 1
+ name: Top Level
+ lft: 1
+ rgt: 12
+child_1:
+ id: 2
+ name: Child 1
+ parent_id: 1
+ lft: 2
+ rgt: 3
+child_2:
+ id: 3
+ name: Child 2
+ parent_id: 1
+ lft: 4
+ rgt: 7
+child_2_1:
+ id: 4
+ name: Child 2.1
+ parent_id: 3
+ lft: 5
+ rgt: 6
+child_3:
+ id: 5
+ name: Child 3
+ parent_id: 1
+ lft: 8
+ rgt: 9
+top_level 2:
+ id: 6
+ name: Top Level 2
+ lft: 10
+ rgt: 11
3  spec/fixtures/category.rb
@@ -0,0 +1,3 @@
+class Category < ActiveRecord::Base
+ acts_as_nested_set
+end
20 spec/spec_helper.rb
@@ -0,0 +1,20 @@
+ENV["RAILS_ENV"] = "test"
+plugin_spec_dir = File.dirname(__FILE__)
+require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
+require 'spec/rails'
+
+ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
+
+databases = YAML::load(IO.read(plugin_spec_dir + "/db/database.yml"))
+ActiveRecord::Base.establish_connection(databases[ENV["DB"] || "sqlite3"])
+ActiveRecord::Migration.verbose = false
+load(File.join(plugin_spec_dir, "db", "schema.rb"))
+
+Spec::Runner.configure do |config|
+ config.use_transactional_fixtures = true
+ config.use_instantiated_fixtures = false
+ config.fixture_path = plugin_spec_dir + '/fixtures'
+end
+
+# Rails resoure loading (models, controllers, routes).
+Dir["#{plugin_spec_dir}/fixtures/*.rb"].each {|file| require file }
4 tasks/awesome_nested_set_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :awesome_nested_set do
+# # Task goes here
+# end
8 test/awesome_nested_set_test.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class AwesomeNestedSetTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+end
1  uninstall.rb
@@ -0,0 +1 @@
+# Uninstall hook code here
Please sign in to comment.
Something went wrong with that request. Please try again.