mongoid_tree 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/mongoid_tree.rb CHANGED
@@ -3,33 +3,51 @@ module Mongoid
3
3
  module Tree
4
4
  extend ActiveSupport::Concern
5
5
  include Comparable
6
-
6
+
7
7
  included do
8
- references_many :children, :class_name => self.name, :stored_as => :array, :inverse_of => :parent do
8
+ references_many :children, :class_name => self.name, :stored_as => :array, :inverse_of => :parents do
9
9
  def <<(*objects)
10
10
  objects.flatten.each_with_index do |object, index|
11
+ reverse_key = reverse_key(object)
11
12
  if object.position == nil
13
+ # This one sets the position for a new object added via children << object
12
14
  object.position = @parent.send(@foreign_key).count + index + 1
13
15
  end
16
+ # Stores the parents path into it's own path array.
17
+ #raise @parent.inspect
18
+ object.send(reverse_key).concat(@parent.send(reverse_key))
14
19
  end
15
20
  super(objects)
21
+
16
22
  end
17
23
  end
18
24
 
19
- referenced_in :parent, :class_name => self.name, :inverse_of => :children
25
+ #referenced_in :parent, :class_name => self.name, :inverse_of => :children
26
+ references_many :parents, :class_name => self.name, :stored_as => :array, :inverse_of => :children
20
27
 
21
28
  # This stores the position in the children array of the parent object.
22
29
  # Makes it easier to flatten / export / import a tree
23
30
  field :position, :type => Integer
31
+ field :depth, :type => Integer
24
32
  end
25
33
 
26
34
  module InstanceMethods
27
35
 
36
+ def parent
37
+ self.parents.last
38
+ end
39
+
40
+ def depth
41
+ self.parents.count
42
+ end
43
+
28
44
  #Comparable
29
45
  def <=> (another_node)
30
46
  self.position <=> another_node.position
31
47
  end
32
-
48
+
49
+
50
+ # TODO change this into a Mongoid Query
33
51
  def depth_first
34
52
  result = [self]
35
53
  if children.empty?
@@ -42,7 +60,6 @@ module Mongoid
42
60
  return result
43
61
  end
44
62
 
45
-
46
63
  def insert_before( new_child )
47
64
  new_child.position = self.position
48
65
  self.parent.children.each do |child|
@@ -50,19 +67,17 @@ module Mongoid
50
67
  child.position += 1
51
68
  end
52
69
  end
53
- self.parent.children << new_child
70
+ self.parent.reload.children << new_child
54
71
  end
55
72
 
56
73
  def insert_after ( new_child )
57
- self.save
58
- self.parent.save
59
- new_child.position = self.position
74
+ new_child.position = self.position + 1
60
75
  self.parent.children.each do |child|
61
76
  if child.position >= new_child.position
62
77
  child.position += 1
63
78
  end
64
79
  end
65
- self.parent.children << new_child
80
+ self.parent.children << new_child
66
81
  end
67
82
  end
68
83
 
data/mongoid_tree.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongoid_tree}
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Rainer Kuhn"]
12
- s.date = %q{2010-08-10}
12
+ s.date = %q{2010-08-12}
13
13
  s.description = %q{Fully featured tree implementation for Mongoid using materialized paths and relative associations. Featuring Depth and Breadth first search.}
14
14
  s.email = %q{rkuhn@littleweblab.com}
15
15
  s.extra_rdoc_files = [
@@ -2,92 +2,128 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "MongoidTree" do
4
4
 
5
- before do
6
- [ Node ].each { |klass| klass.collection.remove }
5
+ before do
6
+ [ Node ].each { |klass| klass.collection.remove }
7
+ end
8
+
9
+ describe "A tree of nodes" do
10
+
11
+ context "A node with a child" do
12
+
13
+ before do
14
+ @parent = Node.new(:name => "Parent")
15
+ @child = Node.new(:name => "Child")
16
+ @parent.children << @child
17
+ @parent.save
18
+ end
19
+
20
+ it "should have 1 child" do
21
+ @parent.children.count.should be(1)
22
+ @parent.children.first.should eq(@child)
23
+ end
24
+
25
+ it "a child should know it's parent" do
26
+ @child.parent.should eq(@parent)
27
+ end
28
+
29
+ it "the child should be at position 1" do
30
+ @parent.children.first.position.should eq(1)
31
+ end
32
+
7
33
  end
8
34
 
9
- describe "A tree of nodes" do
10
-
11
- context "A node with a child" do
12
-
13
- before do
14
- @parent = Node.new(:name => "Parent")
15
- @child = Node.new(:name => "Child")
16
- @parent.children << @child
17
- @parent.save
18
- end
19
-
20
- it "should have 1 child" do
21
- @parent.children.count.should be(1)
22
- @parent.children.first.should eq(@child)
23
- end
24
-
25
- it "a child should know it's parent" do
26
- @child.parent.should eq(@parent)
27
- end
28
-
29
- it "the child should be at position 1" do
30
- @parent.children.first.position.should eq(1)
31
- end
32
-
33
- end
34
-
35
- context "a node with 2 children" do
36
-
37
- def reload
38
- @parent.reload
39
- @child_1.reload
40
- @child_2.reload
41
- @new_child.reload
42
- end
43
-
44
- before do
45
- @parent = Node.new(:name => "Parent")
46
- @child_1 = Node.new(:name => "Child_1")
47
- @child_2 = Node.new(:name => "Child_2")
48
- @new_child = Node.new(:name => "new_child")
49
- @parent.children << @child_1
50
- @parent.children << @child_2
51
- @child_1.position.should eq(1)
52
- @child_2.position.should eq(2)
53
- @parent.save
54
- end
55
-
56
- it "should add a child before child 1" do
57
- @child_1.insert_before(@new_child)
58
- reload
59
- @parent.children.sort.should eq([@new_child, @child_1, @child_2])
60
- end
61
-
62
- it "should add a child after child 1" do
63
- @child_1.insert_after(@new_child)
64
- reload
65
- @parent.children.sort.should eq([@child_1, @new_child, @child_2])
66
- end
67
-
68
- it "should add a child before child 2" do
69
- @child_2.insert_before(@new_child)
70
- reload
71
- @parent.children.sort.should eq([@child_1, @new_child, @child_2])
72
- end
73
-
74
- it "should add a child after child 2" do
75
- @child_2.insert_after(@new_child)
76
- reload
77
- @parent.children.sort.should eq([@child_1, @child_2, @new_child])
78
- end
79
-
80
- it "should append a child to children" do
81
- @parent.reload
82
- @parent.children << @new_child
83
- @parent.children.count.should eq(3)
84
- @parent.save
85
- @new_child.save
86
- reload
87
- @parent.children.sort.should eq([@child_1, @child_2, @new_child])
88
- end
89
-
90
- end
35
+ context "a node with 2 children" do
36
+
37
+ def reload
38
+ @parent.reload
39
+ @child_1.reload
40
+ @child_2.reload
41
+ @new_child.reload
42
+ end
43
+
44
+ before do
45
+ @parent = Node.new(:name => "Parent")
46
+ @child_1 = Node.new(:name => "Child_1")
47
+ @child_2 = Node.new(:name => "Child_2")
48
+ @new_child = Node.new(:name => "new_child")
49
+ @parent.children << @child_1
50
+ @parent.children << @child_2
51
+ @child_1.position.should eq(1)
52
+ @child_2.position.should eq(2)
53
+ @parent.save
54
+ end
55
+
56
+ it "should add a child before child 1" do
57
+ @child_1.insert_before(@new_child)
58
+ reload
59
+ @parent.children.sort.should eq([@new_child, @child_1, @child_2])
60
+ end
61
+
62
+ it "should add a child after child 1" do
63
+ @child_1.insert_after(@new_child)
64
+ reload
65
+ @parent.children.sort.should eq([@child_1, @new_child, @child_2])
66
+ end
67
+
68
+ it "should add a child before child 2" do
69
+ @child_2.insert_before(@new_child)
70
+ reload
71
+ @parent.children.sort.should eq([@child_1, @new_child, @child_2])
72
+ end
73
+
74
+ it "should add a child after child 2" do
75
+ @child_2.insert_after(@new_child)
76
+ reload
77
+ @parent.children.sort.should eq([@child_1, @child_2, @new_child])
78
+ end
79
+
80
+ it "should append a child to children" do
81
+ @parent.reload
82
+ @parent.children << @new_child
83
+ @parent.children.count.should eq(3)
84
+ @parent.save
85
+ @new_child.save
86
+ reload
87
+ @parent.children.sort.should eq([@child_1, @child_2, @new_child])
88
+ end
91
89
 
92
90
  end
91
+
92
+ context "with full paths" do
93
+
94
+ def getNode(number)
95
+ Node.where(:name => "Node_#{number}").first
96
+ end
97
+
98
+ before do
99
+ # Build a tree
100
+ 12.times{ |i| Factory.create(:node, :name => "Node_#{i+1}") }
101
+
102
+ Node.where(:name => "Node_1").first.children << Node.where(:name => "Node_7").first
103
+ Node.where(:name => "Node_7").first.insert_before(Node.where(:name => "Node_2").first)
104
+ Node.where(:name => "Node_1").first.children << Node.where(:name => "Node_8").first
105
+ Node.where(:name => "Node_2").first.children << Node.where(:name => "Node_3").first
106
+ Node.where(:name => "Node_3").first.insert_after(Node.where(:name => "Node_6").first)
107
+ Node.where(:name => "Node_8").first.children << Node.where(:name => "Node_9").first
108
+ Node.where(:name => "Node_9").first.children << Node.where(:name => "Node_11").first
109
+ Node.where(:name => "Node_3").first.children << Node.where(:name => "Node_5").first
110
+ Node.where(:name => "Node_9").first.insert_after(Node.where(:name => "Node_12").first)
111
+ Node.where(:name => "Node_5").first.insert_before(Node.where(:name => "Node_4").first)
112
+ Node.where(:name => "Node_11").first.insert_before(Node.where(:name => "Node_10").first)
113
+
114
+ end
115
+
116
+ # This is not BDD, I know...
117
+ it "should store the path correctly" do
118
+ getNode(6).parent_ids.should eql([getNode(1).id, getNode(2).id])
119
+ getNode(2).parent_ids.should eql([getNode(1).id])
120
+ getNode(3).parent_ids.should eql([getNode(1).id, getNode(2).id])
121
+ getNode(4).parent_ids.should eql([getNode(1).id, getNode(2).id, getNode(3).id])
122
+ getNode(5).parent_ids.should eql([getNode(1).id, getNode(2).id, getNode(3).id])
123
+ getNode(10).parent_ids.should eql([getNode(1).id, getNode(8).id, getNode(9).id])
124
+ end
125
+
126
+ end
127
+
128
+ end
93
129
  end
data/spec/spec_helper.rb CHANGED
@@ -23,5 +23,5 @@ Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file }
23
23
  Dir[File.dirname(__FILE__) + "/factories/*.rb"].each {|file| require file }
24
24
 
25
25
  RSpec.configure do |config|
26
-
26
+
27
27
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Rainer Kuhn
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-10 00:00:00 +02:00
17
+ date: 2010-08-12 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies: []
20
20