forked from benedikt/mongoid-tree
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added basic tree implementation, including some documentation.
- Loading branch information
Showing
6 changed files
with
177 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
source :rubygems | ||
|
||
gemspec | ||
|
||
gem 'bson_ext', '1.0.4' | ||
gem 'bson_ext', '>= 1.0.4' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
module Mongoid # :nodoc: | ||
## | ||
# = Mongoid::Tree | ||
# | ||
# This module extends any Mongoid document with tree functionality. | ||
# | ||
# == Usage | ||
# | ||
# Simply include the module in any Mongoid document: | ||
# | ||
# class Node | ||
# include Mongoid::Document | ||
# include Mongoid::Tree | ||
# end | ||
# | ||
# === Using the tree structure | ||
# | ||
# Each document references many children. You can access them using the <tt>#children</tt> method. | ||
# | ||
# node = Node.create | ||
# node.children.create | ||
# node.children.count # => 1 | ||
# | ||
# Every document references one parent (unless it's a root document). | ||
# | ||
# node = Node.create | ||
# node.parent # => nil | ||
# node.children.create | ||
# node.children.first.parent # => node | ||
# | ||
module Tree | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
reference_many :children, :class_name => self.name, :foreign_key => :parent_id, :inverse_of => :parent | ||
referenced_in :parent, :class_name => self.name, :inverse_of => :children | ||
|
||
field :parent_ids, :type => Array, :default => [] | ||
|
||
set_callback :validation, :before, :rearrange | ||
set_callback :save, :after, :rearrange_children, :if => :rearrange_children? | ||
end | ||
|
||
## | ||
# :method: children | ||
# Returns a list of the document's children. It's a <tt>reference_many</tt> association. | ||
# (Generated by Mongoid) | ||
|
||
## | ||
# :method: parent | ||
# Returns the document's parent (unless it's a root document). It's a <tt>referenced_in</tt> association. | ||
# (Generated by Mongoid) | ||
|
||
## | ||
# :method: parent_ids | ||
# Returns a list of the document's parent_ids, starting with the root node. | ||
# (Generated by Mongoid) | ||
|
||
## | ||
# Forces rearranging of all children after next save | ||
def rearrange_children! | ||
@rearrange_children = true | ||
end | ||
|
||
## | ||
# Will the children be rearranged after next save? | ||
def rearrange_children? | ||
!!@rearrange_children | ||
end | ||
|
||
private | ||
|
||
def rearrange | ||
if parent_id | ||
self.parent_ids = self.class.find(self.parent_id).parent_ids + [self.parent_id] | ||
end | ||
|
||
rearrange_children! if self.parent_ids_changed? | ||
return true | ||
end | ||
|
||
def rearrange_children | ||
@rearrange_children = false | ||
self.children.find(:all).each { |c| c.save } | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
require 'spec_helper' | ||
|
||
class Node | ||
include Mongoid::Document | ||
include Mongoid::Tree | ||
end | ||
|
||
describe Mongoid::Tree do | ||
|
||
it "should reference many children as inverse of parent" do | ||
a = Node.associations['children'] | ||
a.should_not be_nil | ||
a.association.should == Mongoid::Associations::ReferencesMany | ||
a.options.class_name.should == 'Node' | ||
a.options.foreign_key.should == 'parent_id' | ||
a.options.inverse_of.should == :parent | ||
end | ||
|
||
it "should be referenced in one parent as inverse of children" do | ||
a = Node.associations['parent'] | ||
a.should_not be_nil | ||
a.association.should == Mongoid::Associations::ReferencedIn | ||
a.options.class_name.should == 'Node' | ||
a.options.inverse_of.should == :children | ||
end | ||
|
||
it "should store parent_ids as Array with [] as default" do | ||
f = Node.fields['parent_ids'] | ||
f.should_not be_nil | ||
f.options[:type].should == Array | ||
f.options[:default].should == [] | ||
end | ||
|
||
it "should rebuild its parent_ids when saved" do | ||
root = Node.create; child = Node.create; subchild = Node.create | ||
root.children << child | ||
child.children << subchild # Mongoid implicitly saves | ||
subchild.parent_ids.should == [root.id, child.id] | ||
end | ||
|
||
it "should rebuild its children's parent_ids when its own parent_ids changed" do | ||
root = Node.create; child = Node.create; subchild = Node.create; new_root = Node.create | ||
root.children << child | ||
child.children << subchild | ||
|
||
new_root.children << child | ||
|
||
subchild.reload | ||
subchild.parent_ids.should == [new_root.id, child.id] | ||
end | ||
|
||
it "should correctly rebuild its descendants' parent_ids when moved into an other subtree" do | ||
root = Node.create; child = Node.create; subchild = Node.create; subsubchild = Node.create | ||
new_root = Node.create; new_child = Node.create | ||
|
||
root.children << child | ||
child.children << subchild | ||
subchild.children << subsubchild | ||
|
||
subsubchild.parent_ids.should == [root.id, child.id, subchild.id] | ||
|
||
new_root.children << new_child | ||
new_child.children << subchild | ||
subsubchild.reload | ||
subsubchild.parent_ids.should == [new_root.id, new_child.id, subchild.id] | ||
end | ||
|
||
it "should not rebuild its children's parent_ids when its not required" do | ||
root = Node.create; child = Node.create; | ||
root.children << child | ||
root.should_not_receive(:rearrange_children) | ||
root.save | ||
end | ||
|
||
end |