mongoid-tree 0.7.0 → 1.0.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.
@@ -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