mongoid-tree 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,6 +44,8 @@ module Mongoid
44
44
 
45
45
  ##
46
46
  # Returns a chainable criteria for this document's ancestors
47
+ #
48
+ # @return [Mongoid::Criteria] Mongoid criteria to retrieve the documents ancestors
47
49
  def ancestors
48
50
  base_class.unscoped.where(:_id.in => parent_ids)
49
51
  end
@@ -51,6 +53,8 @@ module Mongoid
51
53
  ##
52
54
  # Returns siblings below the current document.
53
55
  # Siblings with a position greater than this documents's position.
56
+ #
57
+ # @return [Mongoid::Criteria] Mongoid criteria to retrieve the documents lower_siblings
54
58
  def lower_siblings
55
59
  self.siblings.where(:position.gt => self.position)
56
60
  end
@@ -58,36 +62,48 @@ module Mongoid
58
62
  ##
59
63
  # Returns siblings above the current document.
60
64
  # Siblings with a position lower than this documents's position.
65
+ #
66
+ # @return [Mongoid::Criteria] Mongoid criteria to retrieve the documents higher_siblings
61
67
  def higher_siblings
62
68
  self.siblings.where(:position.lt => self.position)
63
69
  end
64
70
 
65
71
  ##
66
72
  # Returns the lowest sibling (could be self)
73
+ #
74
+ # @return [Mongoid::Document] The lowest sibling
67
75
  def last_sibling_in_list
68
76
  siblings_and_self.last
69
77
  end
70
78
 
71
79
  ##
72
80
  # Returns the highest sibling (could be self)
81
+ #
82
+ # @return [Mongoid::Document] The highest sibling
73
83
  def first_sibling_in_list
74
84
  siblings_and_self.first
75
85
  end
76
86
 
77
87
  ##
78
88
  # Is this the highest sibling?
89
+ #
90
+ # @return [Boolean] Whether the document is the highest sibling
79
91
  def at_top?
80
92
  higher_siblings.empty?
81
93
  end
82
94
 
83
95
  ##
84
96
  # Is this the lowest sibling?
97
+ #
98
+ # @return [Boolean] Whether the document is the lowest sibling
85
99
  def at_bottom?
86
100
  lower_siblings.empty?
87
101
  end
88
102
 
89
103
  ##
90
104
  # Move this node above all its siblings
105
+ #
106
+ # @return [undefined]
91
107
  def move_to_top
92
108
  return true if at_top?
93
109
  move_above(first_sibling_in_list)
@@ -95,6 +111,8 @@ module Mongoid
95
111
 
96
112
  ##
97
113
  # Move this node below all its siblings
114
+ #
115
+ # @return [undefined]
98
116
  def move_to_bottom
99
117
  return true if at_bottom?
100
118
  move_below(last_sibling_in_list)
@@ -102,6 +120,8 @@ module Mongoid
102
120
 
103
121
  ##
104
122
  # Move this node one position up
123
+ #
124
+ # @return [undefined]
105
125
  def move_up
106
126
  return if at_top?
107
127
  siblings.where(:position => self.position - 1).first.inc(:position, 1)
@@ -110,6 +130,8 @@ module Mongoid
110
130
 
111
131
  ##
112
132
  # Move this node one position down
133
+ #
134
+ # @return [undefined]
113
135
  def move_down
114
136
  return if at_bottom?
115
137
  siblings.where(:position => self.position + 1).first.inc(:position, -1)
@@ -120,6 +142,10 @@ module Mongoid
120
142
  # Move this node above the specified node
121
143
  #
122
144
  # This method changes the node's parent if nescessary.
145
+ #
146
+ # @param [Mongoid::Tree] other document to move this document above
147
+ #
148
+ # @return [undefined]
123
149
  def move_above(other)
124
150
  unless sibling_of?(other)
125
151
  self.parent_id = other.parent_id
@@ -144,6 +170,10 @@ module Mongoid
144
170
  # Move this node below the specified node
145
171
  #
146
172
  # This method changes the node's parent if nescessary.
173
+ #
174
+ # @param [Mongoid::Tree] other document to move this document below
175
+ #
176
+ # @return [undefined]
147
177
  def move_below(other)
148
178
  unless sibling_of?(other)
149
179
  self.parent_id = other.parent_id
@@ -1,4 +1,4 @@
1
- module Mongoid # :nodoc:
1
+ module Mongoid
2
2
  module Tree
3
3
  ##
4
4
  # = Mongoid::Tree::Traversal
@@ -48,37 +48,34 @@ module Mongoid # :nodoc:
48
48
  module Traversal
49
49
  extend ActiveSupport::Concern
50
50
 
51
- ##
52
- # :singleton-method: traverse
53
- # Traverses the entire tree, one root at a time, using the given traversal
54
- # method (Default is :depth_first).
55
- #
56
- # See Mongoid::Tree::Traversal for available traversal methods.
57
- #
58
- # Example:
59
- #
60
- # # Say we have the following tree, and want to print its hierarchy:
61
- # # root_1
62
- # # child_1_a
63
- # # root_2
64
- # # child_2_a
65
- # # child_2_a_1
66
- #
67
- # Node.traverse(:depth_first) do |node|
68
- # indentation = ' ' * node.depth
69
- #
70
- # puts "#{indentation}#{node.name}"
71
- # end
72
- #
73
51
 
74
52
  ##
75
- # The methods in this module are class-level methods documented above.
76
- # They're extended into the base class automatically.
77
- module ClassMethods # :nodoc:
53
+ # This module implements class methods that will be available
54
+ # on the document that includes Mongoid::Tree::Traversal
55
+ module ClassMethods
56
+ ##
57
+ # Traverses the entire tree, one root at a time, using the given traversal
58
+ # method (Default is :depth_first).
59
+ #
60
+ # See Mongoid::Tree::Traversal for available traversal methods.
61
+ #
62
+ # @example
63
+ #
64
+ # # Say we have the following tree, and want to print its hierarchy:
65
+ # # root_1
66
+ # # child_1_a
67
+ # # root_2
68
+ # # child_2_a
69
+ # # child_2_a_1
70
+ #
71
+ # Node.traverse(:depth_first) do |node|
72
+ # indentation = ' ' * node.depth
73
+ #
74
+ # puts "#{indentation}#{node.name}"
75
+ # end
76
+ #
78
77
  def traverse(type = :depth_first, &block)
79
- res = []
80
- roots.each { |root| res.concat root.traverse(type, &block) }
81
- res
78
+ roots.collect { |root| root.traverse(type, &block) }.flatten
82
79
  end
83
80
  end
84
81
 
@@ -88,7 +85,7 @@ module Mongoid # :nodoc:
88
85
  #
89
86
  # See Mongoid::Tree::Traversal for available traversal methods.
90
87
  #
91
- # Example:
88
+ # @example
92
89
  #
93
90
  # results = []
94
91
  # root.traverse(:depth_first) do |node|
@@ -96,28 +93,29 @@ module Mongoid # :nodoc:
96
93
  # end
97
94
  #
98
95
  # root.traverse(:depth_first).map(&:name)
96
+ # root.traverse(:depth_first, &:name)
99
97
  #
100
98
  def traverse(type = :depth_first, &block)
101
- res = []
102
- block ||= lambda { |node| res << node }
99
+ block ||= lambda { |node| node }
103
100
  send("#{type}_traversal", &block)
104
- res
105
101
  end
106
102
 
107
103
  private
108
104
 
109
105
  def depth_first_traversal(&block)
110
- block.call(self)
111
- self.children.each { |c| c.send(:depth_first_traversal, &block) }
106
+ result = [block.call(self)] + self.children.collect { |c| c.send(:depth_first_traversal, &block) }
107
+ result.flatten
112
108
  end
113
109
 
114
110
  def breadth_first_traversal(&block)
111
+ result = []
115
112
  queue = [self]
116
113
  while queue.any? do
117
114
  node = queue.shift
118
- block.call(node)
115
+ result << block.call(node)
119
116
  queue += node.children
120
117
  end
118
+ result
121
119
  end
122
120
  end
123
121
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongoid::Tree::Traversal do
4
4
 
5
- subject { Node }
5
+ subject { OrderedNode }
6
6
 
7
7
  describe '#traverse' do
8
8
 
@@ -22,53 +22,59 @@ describe Mongoid::Tree::Traversal do
22
22
  subject.should_receive(:depth_first_traversal)
23
23
  subject.traverse {}
24
24
  end
25
-
26
25
  end
27
26
 
28
27
  describe 'depth first traversal' do
29
28
 
30
- it "should traverse correctly" do
31
- setup_tree <<-ENDTREE
32
- node1:
33
- - node2:
34
- - node3
35
- - node4:
36
- - node5
37
- - node6
38
- - node7
39
- ENDTREE
40
-
41
- result = []
42
- node(:node1).traverse(:depth_first) { |node| result << node }
43
- result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
44
- end
45
-
46
- it "should traverse correctly on merged trees" do
47
-
48
- setup_tree <<-ENDTREE
49
- - node4:
50
- - node5
51
- - node6:
29
+ describe 'with unmodified tree' do
30
+ before do
31
+ setup_tree <<-ENDTREE
32
+ node1:
33
+ - node2:
34
+ - node3
35
+ - node4:
36
+ - node5
37
+ - node6
52
38
  - node7
39
+ ENDTREE
40
+ end
53
41
 
54
- - node1:
55
- - node2:
56
- - node3
57
- ENDTREE
42
+ it "should traverse correctly" do
43
+ result = []
44
+ node(:node1).traverse(:depth_first) { |node| result << node }
45
+ result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
46
+ end
58
47
 
48
+ it "should return and array containing the results of the block for each node" do
49
+ result = node(:node1).traverse(:depth_first) { |n| n.name.to_sym }
50
+ result.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
51
+ end
52
+ end
59
53
 
60
- node(:node1).children << node(:node4)
54
+ describe 'with merged trees' do
55
+ before do
56
+ setup_tree <<-ENDTREE
57
+ - node4:
58
+ - node5
59
+ - node6:
60
+ - node7
61
61
 
62
+ - node1:
63
+ - node2:
64
+ - node3
65
+ ENDTREE
62
66
 
63
- result = []
64
- node(:node1).traverse(:depth_first) { |node| result << node }
65
- result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
67
+ node(:node1).children << node(:node4)
68
+ end
69
+
70
+ it "should traverse correctly" do
71
+ result = node(:node1).traverse(:depth_first) { |n| n.name.to_sym }
72
+ result.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
73
+ end
66
74
  end
67
75
 
68
76
  describe 'with reordered nodes' do
69
77
 
70
- subject { OrderedNode }
71
-
72
78
  before do
73
79
  setup_tree <<-ENDTREE
74
80
  node1:
@@ -79,6 +85,7 @@ describe Mongoid::Tree::Traversal do
79
85
  - node5
80
86
  - node7
81
87
  ENDTREE
88
+
82
89
  node(:node5).move_above(node(:node6))
83
90
  end
84
91
 
@@ -99,8 +106,8 @@ describe Mongoid::Tree::Traversal do
99
106
 
100
107
  describe 'breadth first traversal' do
101
108
 
102
- it "should traverse correctly" do
103
- tree = setup_tree <<-ENDTREE
109
+ before do
110
+ setup_tree <<-ENDTREE
104
111
  node1:
105
112
  - node2:
106
113
  - node5
@@ -109,12 +116,19 @@ describe Mongoid::Tree::Traversal do
109
116
  - node7
110
117
  - node4
111
118
  ENDTREE
119
+ end
112
120
 
121
+ it "should traverse correctly" do
113
122
  result = []
114
123
  node(:node1).traverse(:breadth_first) { |n| result << n }
115
124
  result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
116
125
  end
117
126
 
127
+ it "should return and array containing the results of the block for each node" do
128
+ result = node(:node1).traverse(:breadth_first) { |n| n.name.to_sym }
129
+ result.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
130
+ end
131
+
118
132
  end
119
133
 
120
134
  describe '.traverse' do
@@ -7,16 +7,16 @@ describe Mongoid::Tree do
7
7
  it "should reference many children as inverse of parent with index" do
8
8
  a = Node.reflect_on_association(:children)
9
9
  a.should be
10
- a.macro.should eql(:references_many)
10
+ a.macro.should eql(:has_many)
11
11
  a.class_name.should eql('Node')
12
12
  a.foreign_key.should eql('parent_id')
13
- Node.index_options.should have_key('parent_id')
13
+ Node.index_options.should have_key('parent_id' => 1)
14
14
  end
15
15
 
16
16
  it "should be referenced in one parent as inverse of children" do
17
17
  a = Node.reflect_on_association(:parent)
18
18
  a.should be
19
- a.macro.should eql(:referenced_in)
19
+ a.macro.should eql(:belongs_to)
20
20
  a.class_name.should eql('Node')
21
21
  a.inverse_of.should eql(:children)
22
22
  end
@@ -26,7 +26,7 @@ describe Mongoid::Tree do
26
26
  f.should be
27
27
  f.options[:type].should eql(Array)
28
28
  f.options[:default].should eql([])
29
- Node.index_options.should have_key(:parent_ids)
29
+ Node.index_options.should have_key(:parent_ids => 1)
30
30
  end
31
31
 
32
32
  describe 'when new' do
@@ -45,7 +45,7 @@ describe Mongoid::Tree do
45
45
  it "should save its unsaved children" do
46
46
  root = Node.new(:name => 'root'); child = Node.new(:name => 'child')
47
47
  root.children << child
48
- child.should_receive(:save).at_most(2).times
48
+ child.should_receive(:save)
49
49
  root.save
50
50
  end
51
51
  end
@@ -66,15 +66,15 @@ describe Mongoid::Tree do
66
66
  it "should set the child's parent_id when added to parent's children" do
67
67
  root = Node.create; child = Node.create
68
68
  root.children << child
69
- child.parent.should == root
70
- child.parent_id.should == root.id
69
+ child.parent.should eq(root)
70
+ child.parent_id.should eq(root.id)
71
71
  end
72
72
 
73
73
  it "should set the child's parent_id parent is set on child" do
74
74
  root = Node.create; child = Node.create
75
75
  child.parent = root
76
- child.parent.should == root
77
- child.parent_id.should == root.id
76
+ child.parent.should eq(root)
77
+ child.parent_id.should eq(root.id)
78
78
  end
79
79
 
80
80
  it "should rebuild its parent_ids" do
@@ -245,9 +245,9 @@ describe Mongoid::Tree do
245
245
 
246
246
  describe '#depth' do
247
247
  it "should return the depth of this document" do
248
- node(:root).depth.should == 0
249
- node(:child).depth.should == 1
250
- node(:subchild).depth.should == 2
248
+ node(:root).depth.should eq(0)
249
+ node(:child).depth.should eq(1)
250
+ node(:subchild).depth.should eq(2)
251
251
  end
252
252
  end
253
253
 
@@ -317,7 +317,7 @@ describe Mongoid::Tree do
317
317
  end
318
318
 
319
319
  it "#siblings_and_self should return the documents siblings and itself" do
320
- node(:child).siblings_and_self.is_a?(Mongoid::Criteria).should == true
320
+ node(:child).siblings_and_self.should be_kind_of(Mongoid::Criteria)
321
321
  node(:child).siblings_and_self.to_a.should == [node(:child), node(:other_child)]
322
322
  end
323
323
 
@@ -378,5 +378,15 @@ describe Mongoid::Tree do
378
378
 
379
379
  end
380
380
 
381
+ describe 'cascading to embedded documents' do
382
+
383
+ it 'should not raise a NoMethodError' do
384
+ node = NodeWithEmbeddedDocument.new
385
+ document = node.build_embedded_document
386
+ expect { node.save }.to_not raise_error NoMethodError
387
+ end
388
+
389
+ end
390
+
381
391
  end
382
392
  end