mongoid_tree 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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