diff --git a/models/tree.rb b/models/tree.rb index fb7a91d..1daadd8 100644 --- a/models/tree.rb +++ b/models/tree.rb @@ -24,4 +24,59 @@ def leaf? def siblings root? ? [] : parent.children - [self] end -end \ No newline at end of file + + + def to_s + @name + end + + + # Similar to acts_as_tree .descendants method + # each_with_object will put each child into the do/|| block in the 'child' variable. + # array_of_children comes from the first argument from each_with_object, in this case + # we are giving 'each_with_object' the 'children' array. + # You could change children to @children because it is an instance variable. + # array_of_children is modified with each iteration. + def descendants + # This line is talking about the direct children (Paul, Peter and Mary, + # if you were starting with Fred.) + children.each_with_object(children) { |child, array_of_children| + # This line is talking the grand children and so forth (following on, + # Pan and Rabbit). + array_of_children.concat child.descendants + # uniq ensures that no children get counted twice (shouldn't happen anyway) + }.uniq + end + + + + def ancestors + # This commented out part was taken directly from acts_as_tree + # changed it to be more understandable. + # They both do the same thing. + # node, nodes = self, [] + # nodes << node = node.parent while node.parent + # nodes + + current_tree = self + trees = [] + # Assuming grandchild1 is given, this will iterate twice. + # current_tree will initially be grandchild1 + # after the first loop, it will become child1 + # after the second loop it will become parent. + # Then the loop will end, because parent is an orphan. + while current_tree && current_tree.parent + current_tree = current_tree.parent + # << this is the same as trees.push + trees << current_tree + end + # Going on that assumption, the final array would be (child1, parent). + trees + end + + + def detach_node! + parent.children.delete self + @parent = nil + end +end diff --git a/specs/models/tree_spec.rb b/specs/models/tree_spec.rb index 12e7195..6bddb89 100644 --- a/specs/models/tree_spec.rb +++ b/specs/models/tree_spec.rb @@ -101,4 +101,65 @@ expect(fred.leaf?).to be_falsey end end + + describe '.descendants' do + it 'makes an array of all children of children of children, etc' do + parent = Tree.new('Parent') + child1 = Tree.new('Child1') + child2 = Tree.new('Child2') + grandchild1 = Tree.new('Grandchild1') + grandchild2 = Tree.new('Grandchild2') + + parent.add_child(child1) + parent.add_child(child2) + + child1.add_child(grandchild1) + child1.add_child(grandchild2) + + + descendants = parent.descendants + expect(descendants).to include(child1, child2, grandchild1, grandchild2) + expect(descendants).to_not include(parent) + end + end + + describe '.ancestors' do + it 'makes an array of all ancestors' do + parent = Tree.new('Parent') + child1 = Tree.new('Child1') + child2 = Tree.new('Child2') + grandchild1 = Tree.new('Grandchild1') + grandchild2 = Tree.new('Grandchild2') + + parent.add_child(child1) + parent.add_child(child2) + + child1.add_child(grandchild1) + child1.add_child(grandchild2) + + ancestors = grandchild1.ancestors + expect(ancestors).to include(child1, parent) + expect(ancestors).to_not include(child2, grandchild2) + end + end + + describe '.detach_node' do + it 'removes itself from its parent. The child becomes emancipated.' do + parent = Tree.new('Parent') + child1 = Tree.new('Child1') + child2 = Tree.new('Child2') + grandchild1 = Tree.new('Grandchild1') + grandchild2 = Tree.new('Grandchild2') + + parent.add_child(child1) + parent.add_child(child2) + + child1.add_child(grandchild1) + child1.add_child(grandchild2) + + child1.detach_node! + expect(parent.children).to_not include(child1) + expect(child1.parent).to_not eq(parent) + end + end end