Permalink
Browse files

- Added some tests for node creation through scopes

- Fixed some errors in de README file
  • Loading branch information...
1 parent 2bd091c commit 22a19844ef8fa5e407cf3c5e8abdcd3c41a59232 Stefan Kroes committed Oct 18, 2009
Showing with 64 additions and 40 deletions.
  1. +25 −18 README.rdoc
  2. +5 −5 lib/ancestry/acts_as_tree.rb
  3. +32 −15 test/acts_as_tree_test.rb
  4. +2 −2 test/database.yml
View
43 README.rdoc
@@ -6,17 +6,21 @@ Ancestry allows the records of a ActiveRecord model to be organised in a tree st
To apply Ancestry to any ActiveRecord model, follow these simple steps:
-1. Install Gem
+1. Install gem
+ - Install gemcutter gem: sudo gem install gemcutter (maybe you need: gem update --system)
+ - Add gemcutter.org as default gem source: gem tumble
- Add to config/environment.rb: config.gem 'ancestry'
- Install required gems: sudo rake gems:install
- - Alternatively: sudo gem install ancestry
+ - Alternatively: sudo gem install ancestry
+ - If you don't want gemcutter: config.gem 'ancestry', :source => 'gemcutter.org'
+ - Alternatively: sudo gem install ancestry --source gemcutter.org
-2. Add Ancestry Column to Your Table
+2. Add ancestry column to your table
- Create migration: ./script/generate migration add_ancestry_to_[table] ancestry:string
- Add index to migration: add_index [table], :ancestry / remove_index [table], :ancestry
- Migrate your database: rake db:migrate
-3. Add Ancestry to Your Model
+3. Add ancestry to your model
- Add to app/models/[model].rb: acts_as_tree
Your model is now a tree!
@@ -52,35 +56,38 @@ To navigate an Ancestry model, use the following methods on any instance / recor
[subtree] Scopes the model on descendants and itself
[subtree_ids] Returns a list of all ids in the record's subtree
-= Scopes
+= (Named) Scopes
Where possible, the navigation methods return scopes instead of records, this means additional ordering, conditions, limits, etc. can be applied and that the result can be either retrieved, counted or checked for existence. For example:
node.children.exists?(:name => 'Mary')
-
node.subtree.all(:order => :name, :limit => 10).each do; ...; end
-
node.descendants.count
-= Named Scopes
+For convenience, a couple of named scopes are included at the class level:
-For convenience, a couple of named scopes are included as class level:
+[roots] Only root nodes
+[ancestors_of(node)] Only ancestors of node, node can be either a record or an id
+[children_of(node)] Only children of node, node can be either a record or an id
+[descendants_of(node)] Only descendants of node, node can be either a record or an id
+[siblings_of(node)] Only siblings of node, node can be either a record or an id
-[root] Only root nodes
-[ancestor_of(node)] Only ancestors of node, node can be either a record or an id
-[child_of(node)] Only children of node, node can be either a record or an id
-[descendant_of(node)] Only descendants of node, node can be either a record or an id
-[sibling_of(node)] Only siblings of node, node can be either a record or an id
+Thanks to some convenient rails magic, it is even possible to create nodes through the children and siblings scopes:
+
+node.children.create
+node.siblings.create!
+TestNode.children_of(node_id).new
+TestNode.siblings_of(node_id).create
= acts_as_tree Options
The acts_as_tree methods supports two options:
[ancestry_column] Pass in a symbol to instruct Ancestry to use a different column name to store record ancestry
[orphan_strategy] Instruct Ancestry what to do with children of a node that is destroyed:
- [:destroy] All children are destroyed as well (default)
- [:rootify] The children of the destroyed node become root nodes
- [:restrict] An AncestryException is raised if any children exist
+ :destroy All children are destroyed as well (default)
+ :rootify The children of the destroyed node become root nodes
+ :restrict An AncestryException is raised if any children exist
= Arrangement
@@ -129,7 +136,7 @@ In the example above, the ancestry column is created as a string. This puts a li
= Future Work
-I will try to keep Ancestry up to date with changing versions of Rails and Ruby and also with any bug reports I might receive. I will implement new features on request and only as I see fit. Something that definitely needs to be added in the future is constraints on depth, something like: tree_node.subtree.to_depth(4)
+I will try to keep Ancestry up to date with changing versions of Rails and Ruby and also with any bug reports I might receive. I will implement new features on request as I see fit. Something that definitely needs to be added in the future is constraints on depth, something like: tree_node.subtree.to_depth(4)
= Feedback
View
10 lib/ancestry/acts_as_tree.rb
@@ -29,11 +29,11 @@ def acts_as_tree options = {}
validates_format_of ancestry_column, :with => /^[0-9]+(\/[0-9]+)*$/, :allow_nil => true
# Named scopes
- named_scope :root, :conditions => {ancestry_column => nil}
- named_scope :ancestor_of, lambda{ |object| {:conditions => to_node(object).ancestor_conditions} }
- named_scope :child_of, lambda{ |object| {:conditions => to_node(object).child_conditions} }
- named_scope :descendant_of, lambda{ |object| {:conditions => to_node(object).descendant_conditions} }
- named_scope :sibling_of, lambda{ |object| {:conditions => to_node(object).sibling_conditions} }
+ named_scope :roots, :conditions => {ancestry_column => nil}
+ named_scope :ancestors_of, lambda{ |object| {:conditions => to_node(object).ancestor_conditions} }
+ named_scope :children_of, lambda{ |object| {:conditions => to_node(object).child_conditions} }
+ named_scope :descendants_of, lambda{ |object| {:conditions => to_node(object).descendant_conditions} }
+ named_scope :siblings_of, lambda{ |object| {:conditions => to_node(object).sibling_conditions} }
# Update descendants with new ancestry before save
before_save :update_descendants_with_new_ancestry
View
47 test/acts_as_tree_test.rb
@@ -187,21 +187,21 @@ def test_named_scopes
roots = setup_test_nodes TestNode, 3, 3
# Roots assertion
- assert_equal roots.map(&:first), TestNode.root.all
+ assert_equal roots.map(&:first), TestNode.roots.all
TestNode.all.each do |test_node|
- # Assertions for ancestor_of named scope
- assert_equal test_node.ancestors, TestNode.ancestor_of(test_node)
- assert_equal test_node.ancestors, TestNode.ancestor_of(test_node.id)
- # Assertions for child_of named scope
- assert_equal test_node.children, TestNode.child_of(test_node)
- assert_equal test_node.children, TestNode.child_of(test_node.id)
- # Assertions for descendant_of named scope
- assert_equal test_node.descendants, TestNode.descendant_of(test_node)
- assert_equal test_node.descendants, TestNode.descendant_of(test_node.id)
- # Assertions for sibling_of named scope
- assert_equal test_node.siblings, TestNode.sibling_of(test_node)
- assert_equal test_node.siblings, TestNode.sibling_of(test_node.id)
+ # Assertions for ancestors_of named scope
+ assert_equal test_node.ancestors, TestNode.ancestors_of(test_node)
+ assert_equal test_node.ancestors, TestNode.ancestors_of(test_node.id)
+ # Assertions for children_of named scope
+ assert_equal test_node.children, TestNode.children_of(test_node)
+ assert_equal test_node.children, TestNode.children_of(test_node.id)
+ # Assertions for descendants_of named scope
+ assert_equal test_node.descendants, TestNode.descendants_of(test_node)
+ assert_equal test_node.descendants, TestNode.descendants_of(test_node.id)
+ # Assertions for siblings_of named scope
+ assert_equal test_node.siblings, TestNode.siblings_of(test_node)
+ assert_equal test_node.siblings, TestNode.siblings_of(test_node.id)
end
end
@@ -259,7 +259,7 @@ def test_orphan_destroy_strategy
assert_difference 'TestNode.count', -root.subtree.size do
root.destroy
end
- node = TestNode.root.first.children.first
+ node = TestNode.roots.first.children.first
assert_difference 'TestNode.count', -node.subtree.size do
node.destroy
end
@@ -268,7 +268,7 @@ def test_orphan_destroy_strategy
def test_orphan_restrict_strategy
TestNode.orphan_strategy = :restrict
setup_test_nodes(TestNode, 3, 3)
- root = TestNode.root.first
+ root = TestNode.roots.first
assert_raise Ancestry::AncestryException do
root.destroy
end
@@ -357,4 +357,21 @@ def test_arrangement
end
end
end
+
+ def test_node_creation_though_scope
+ node = TestNode.create!
+ child = node.children.create
+ assert_equal node, child.parent
+
+ other_child = child.siblings.create!
+ assert_equal node, other_child.parent
+
+ grandchild = TestNode.children_of(child).new
+ grandchild.save
+ assert_equal child, grandchild.parent
+
+ other_grandchild = TestNode.siblings_of(grandchild).new
+ other_grandchild.save!
+ assert_equal child, other_grandchild.parent
+ end
end
View
4 test/database.yml
@@ -1,9 +1,9 @@
sqlite:
adapter: sqlite
- dbfile: vendor/plugins/ancestry/test/ancestry_plugin.sqlite.db
+ database: vendor/plugins/ancestry/test/ancestry_plugin.sqlite.db
sqlite3:
adapter: sqlite3
- dbfile: vendor/plugins/ancestry/test/ancestry_plugin.sqlite3.db
+ database: vendor/plugins/ancestry/test/ancestry_plugin.sqlite3.db
postgresql:
adapter: postgresql
username: ancestry_plugin_test

0 comments on commit 22a1984

Please sign in to comment.