mongoid-tree-rational 0.1.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/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Gemfile +23 -0
- data/Guardfile +6 -0
- data/LICENSE +21 -0
- data/README.md +287 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/mongoid/locale/en.yml +11 -0
- data/lib/mongoid/locale/nb.yml +8 -0
- data/lib/mongoid/tree.rb +443 -0
- data/lib/mongoid/tree/ordering.rb +236 -0
- data/lib/mongoid/tree/rational_numbering.rb +805 -0
- data/lib/mongoid/tree/traversal.rb +122 -0
- data/mongoid-tree-rational.gemspec +103 -0
- data/spec/mongoid/tree/ordering_spec.rb +342 -0
- data/spec/mongoid/tree/rational_numbering_spec.rb +765 -0
- data/spec/mongoid/tree/traversal_spec.rb +177 -0
- data/spec/mongoid/tree_spec.rb +444 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/macros/tree_macros.rb +53 -0
- data/spec/support/models/node.rb +47 -0
- metadata +320 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Tree
|
3
|
+
##
|
4
|
+
# = Mongoid::Tree::Traversal
|
5
|
+
#
|
6
|
+
# Mongoid::Tree::Traversal provides a #traverse method to walk through the tree.
|
7
|
+
# It supports these traversal methods:
|
8
|
+
#
|
9
|
+
# * depth_first
|
10
|
+
# * breadth_first
|
11
|
+
#
|
12
|
+
# == Depth First Traversal
|
13
|
+
#
|
14
|
+
# See http://en.wikipedia.org/wiki/Depth-first_search for a proper description.
|
15
|
+
#
|
16
|
+
# Given a tree like:
|
17
|
+
#
|
18
|
+
# node1:
|
19
|
+
# - node2:
|
20
|
+
# - node3
|
21
|
+
# - node4:
|
22
|
+
# - node5
|
23
|
+
# - node6
|
24
|
+
# - node7
|
25
|
+
#
|
26
|
+
# Traversing the tree using depth first traversal would visit each node in this order:
|
27
|
+
#
|
28
|
+
# node1, node2, node3, node4, node5, node6, node7
|
29
|
+
#
|
30
|
+
# == Breadth First Traversal
|
31
|
+
#
|
32
|
+
# See http://en.wikipedia.org/wiki/Breadth-first_search for a proper description.
|
33
|
+
#
|
34
|
+
# Given a tree like:
|
35
|
+
#
|
36
|
+
# node1:
|
37
|
+
# - node2:
|
38
|
+
# - node5
|
39
|
+
# - node3:
|
40
|
+
# - node6
|
41
|
+
# - node7
|
42
|
+
# - node4
|
43
|
+
#
|
44
|
+
# Traversing the tree using breadth first traversal would visit each node in this order:
|
45
|
+
#
|
46
|
+
# node1, node2, node3, node4, node5, node6, node7
|
47
|
+
#
|
48
|
+
module Traversal
|
49
|
+
extend ActiveSupport::Concern
|
50
|
+
|
51
|
+
|
52
|
+
##
|
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
|
+
#
|
77
|
+
def traverse(type = :depth_first, &block)
|
78
|
+
roots.collect { |root| root.traverse(type, &block) }.flatten
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Traverses the tree using the given traversal method (Default is :depth_first)
|
84
|
+
# and passes each document node to the block.
|
85
|
+
#
|
86
|
+
# See Mongoid::Tree::Traversal for available traversal methods.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
#
|
90
|
+
# results = []
|
91
|
+
# root.traverse(:depth_first) do |node|
|
92
|
+
# results << node
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# root.traverse(:depth_first).map(&:name)
|
96
|
+
# root.traverse(:depth_first, &:name)
|
97
|
+
#
|
98
|
+
def traverse(type = :depth_first, &block)
|
99
|
+
block ||= lambda { |node| node }
|
100
|
+
send("#{type}_traversal", &block)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def depth_first_traversal(&block)
|
106
|
+
result = [block.call(self)] + self.children.collect { |c| c.send(:depth_first_traversal, &block) }
|
107
|
+
result.flatten
|
108
|
+
end
|
109
|
+
|
110
|
+
def breadth_first_traversal(&block)
|
111
|
+
result = []
|
112
|
+
queue = [self]
|
113
|
+
while queue.any? do
|
114
|
+
node = queue.shift
|
115
|
+
result << block.call(node)
|
116
|
+
queue += node.children
|
117
|
+
end
|
118
|
+
result
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "mongoid-tree-rational"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Leif Ringstad", "Benedikt Deicke"]
|
12
|
+
s.date = "2013-10-09"
|
13
|
+
s.description = "A tree structure for Mongoid documents using the materialized path pattern and rational number sorting."
|
14
|
+
s.email = "leifcr@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".rspec",
|
21
|
+
".travis.yml",
|
22
|
+
"Gemfile",
|
23
|
+
"Guardfile",
|
24
|
+
"LICENSE",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"lib/mongoid/locale/en.yml",
|
29
|
+
"lib/mongoid/locale/nb.yml",
|
30
|
+
"lib/mongoid/tree.rb",
|
31
|
+
"lib/mongoid/tree/ordering.rb",
|
32
|
+
"lib/mongoid/tree/rational_numbering.rb",
|
33
|
+
"lib/mongoid/tree/traversal.rb",
|
34
|
+
"mongoid-tree-rational.gemspec",
|
35
|
+
"spec/mongoid/tree/ordering_spec.rb",
|
36
|
+
"spec/mongoid/tree/rational_numbering_spec.rb",
|
37
|
+
"spec/mongoid/tree/traversal_spec.rb",
|
38
|
+
"spec/mongoid/tree_spec.rb",
|
39
|
+
"spec/spec_helper.rb",
|
40
|
+
"spec/support/macros/tree_macros.rb",
|
41
|
+
"spec/support/models/node.rb"
|
42
|
+
]
|
43
|
+
s.homepage = "https://github.com/boxcms/mongoid-tree-rational"
|
44
|
+
s.licenses = ["MIT"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = "1.8.25"
|
47
|
+
s.summary = "A tree structure for Mongoid documents with rational numbers"
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<mongoid>, ["<= 4.0", ">= 3.0"])
|
54
|
+
s.add_runtime_dependency(%q<rational_number>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<guard-rspec>, [">= 2.6.0"])
|
60
|
+
s.add_development_dependency(%q<rb-inotify>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<wdm>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<hirb>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<wirble>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<coveralls>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<mongoid>, ["<= 4.0", ">= 3.0"])
|
70
|
+
s.add_dependency(%q<rational_number>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
72
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
73
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
74
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
75
|
+
s.add_dependency(%q<guard-rspec>, [">= 2.6.0"])
|
76
|
+
s.add_dependency(%q<rb-inotify>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
78
|
+
s.add_dependency(%q<wdm>, [">= 0"])
|
79
|
+
s.add_dependency(%q<hirb>, [">= 0"])
|
80
|
+
s.add_dependency(%q<wirble>, [">= 0"])
|
81
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
82
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
83
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
84
|
+
end
|
85
|
+
else
|
86
|
+
s.add_dependency(%q<mongoid>, ["<= 4.0", ">= 3.0"])
|
87
|
+
s.add_dependency(%q<rational_number>, [">= 0"])
|
88
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
90
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
91
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
92
|
+
s.add_dependency(%q<guard-rspec>, [">= 2.6.0"])
|
93
|
+
s.add_dependency(%q<rb-inotify>, [">= 0"])
|
94
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
95
|
+
s.add_dependency(%q<wdm>, [">= 0"])
|
96
|
+
s.add_dependency(%q<hirb>, [">= 0"])
|
97
|
+
s.add_dependency(%q<wirble>, [">= 0"])
|
98
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
99
|
+
s.add_dependency(%q<coveralls>, [">= 0"])
|
100
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,342 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongoid::Tree::Ordering do
|
4
|
+
|
5
|
+
subject { OrderedNode }
|
6
|
+
|
7
|
+
it "should store position as an Integer with a default of nil" do
|
8
|
+
f = OrderedNode.fields['position']
|
9
|
+
expect(f).not_to be_nil
|
10
|
+
expect(f.options[:type]).to eq(Integer)
|
11
|
+
expect(f.options[:default]).not_to be
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'when saved' do
|
15
|
+
before(:each) do
|
16
|
+
setup_tree <<-ENDTREE
|
17
|
+
- root:
|
18
|
+
- child:
|
19
|
+
- subchild:
|
20
|
+
- subsubchild
|
21
|
+
- other_root:
|
22
|
+
- other_child
|
23
|
+
- another_child
|
24
|
+
ENDTREE
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should assign a default position of 0 to each node without a sibling" do
|
28
|
+
expect(node(:child).position).to eq(0)
|
29
|
+
expect(node(:subchild).position).to eq(0)
|
30
|
+
expect(node(:subsubchild).position).to eq(0)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should place siblings at the end of the list by default" do
|
34
|
+
expect(node(:root).position).to eq(0)
|
35
|
+
expect(node(:other_root).position).to eq(1)
|
36
|
+
expect(node(:other_child).position).to eq(0)
|
37
|
+
expect(node(:another_child).position).to eq(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should move a node to the end of a list when it is moved to a new parent" do
|
41
|
+
other_root = node(:other_root)
|
42
|
+
child = node(:child)
|
43
|
+
expect(child.position).to eq(0)
|
44
|
+
other_root.children << child
|
45
|
+
child.reload
|
46
|
+
expect(child.position).to eq(2)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should correctly reposition siblings when one of them is removed" do
|
50
|
+
node(:other_child).destroy
|
51
|
+
expect(node(:another_child).position).to eq(0)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should correctly reposition siblings when one of them is added to another parent" do
|
55
|
+
node(:root).children << node(:other_child)
|
56
|
+
expect(node(:another_child).position).to eq(0)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should correctly reposition siblings when the parent is changed" do
|
60
|
+
other_child = node(:other_child)
|
61
|
+
other_child.parent = node(:root)
|
62
|
+
other_child.save!
|
63
|
+
expect(node(:another_child).position).to eq(0)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not reposition siblings when it's not yet saved" do
|
67
|
+
new_node = OrderedNode.new(:name => 'new')
|
68
|
+
new_node.parent = node(:root)
|
69
|
+
expect(new_node).not_to receive(:reposition_former_siblings)
|
70
|
+
new_node.save
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'destroy strategies' do
|
75
|
+
before(:each) do
|
76
|
+
setup_tree <<-ENDTREE
|
77
|
+
- root:
|
78
|
+
- child:
|
79
|
+
- subchild
|
80
|
+
- other_child
|
81
|
+
- other_root
|
82
|
+
ENDTREE
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ':move_children_to_parent' do
|
86
|
+
it "should set its childen's parent_id to the documents parent_id" do
|
87
|
+
node(:child).move_children_to_parent
|
88
|
+
expect(node(:child)).to be_leaf
|
89
|
+
expect(node(:root).children.to_a).to eq([node(:child), node(:other_child), node(:subchild)])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'utility methods' do
|
95
|
+
before(:each) do
|
96
|
+
setup_tree <<-ENDTREE
|
97
|
+
- first_root:
|
98
|
+
- first_child_of_first_root
|
99
|
+
- second_child_of_first_root
|
100
|
+
- second_root
|
101
|
+
- third_root
|
102
|
+
ENDTREE
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#lower_siblings' do
|
106
|
+
it "should return a collection of siblings lower on the list" do
|
107
|
+
node(:second_child_of_first_root).reload
|
108
|
+
expect(node(:first_root).lower_siblings.to_a).to eq([node(:second_root), node(:third_root)])
|
109
|
+
expect(node(:second_root).lower_siblings.to_a).to eq([node(:third_root)])
|
110
|
+
expect(node(:third_root).lower_siblings.to_a).to eq([])
|
111
|
+
expect(node(:first_child_of_first_root).lower_siblings.to_a).to eq([node(:second_child_of_first_root)])
|
112
|
+
expect(node(:second_child_of_first_root).lower_siblings.to_a).to eq([])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#higher_siblings' do
|
117
|
+
it "should return a collection of siblings lower on the list" do
|
118
|
+
expect(node(:first_root).higher_siblings.to_a).to eq([])
|
119
|
+
expect(node(:second_root).higher_siblings.to_a).to eq([node(:first_root)])
|
120
|
+
expect(node(:third_root).higher_siblings.to_a).to eq([node(:first_root), node(:second_root)])
|
121
|
+
expect(node(:first_child_of_first_root).higher_siblings.to_a).to eq([])
|
122
|
+
expect(node(:second_child_of_first_root).higher_siblings.to_a).to eq([node(:first_child_of_first_root)])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#at_top?' do
|
127
|
+
it "should return true when the node is first in the list" do
|
128
|
+
expect(node(:first_root)).to be_at_top
|
129
|
+
expect(node(:first_child_of_first_root)).to be_at_top
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return false when the node is not first in the list" do
|
133
|
+
expect(node(:second_root)).not_to be_at_top
|
134
|
+
expect(node(:third_root)).not_to be_at_top
|
135
|
+
expect(node(:second_child_of_first_root)).not_to be_at_top
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#at_bottom?' do
|
140
|
+
it "should return true when the node is last in the list" do
|
141
|
+
expect(node(:third_root)).to be_at_bottom
|
142
|
+
expect(node(:second_child_of_first_root)).to be_at_bottom
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should return false when the node is not last in the list" do
|
146
|
+
expect(node(:first_root)).not_to be_at_bottom
|
147
|
+
expect(node(:second_root)).not_to be_at_bottom
|
148
|
+
expect(node(:first_child_of_first_root)).not_to be_at_bottom
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#last_sibling_in_list' do
|
153
|
+
it "should return the last sibling in the list containing the current sibling" do
|
154
|
+
expect(node(:first_root).last_sibling_in_list).to eq(node(:third_root))
|
155
|
+
expect(node(:second_root).last_sibling_in_list).to eq(node(:third_root))
|
156
|
+
expect(node(:third_root).last_sibling_in_list).to eq(node(:third_root))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#first_sibling_in_list' do
|
161
|
+
it "should return the first sibling in the list containing the current sibling" do
|
162
|
+
expect(node(:first_root).first_sibling_in_list).to eq(node(:first_root))
|
163
|
+
expect(node(:second_root).first_sibling_in_list).to eq(node(:first_root))
|
164
|
+
expect(node(:third_root).first_sibling_in_list).to eq(node(:first_root))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#ancestors' do
|
169
|
+
it "should be returned in the correct order" do
|
170
|
+
setup_tree <<-ENDTREE
|
171
|
+
- root:
|
172
|
+
- level_1_a
|
173
|
+
- level_1_b:
|
174
|
+
- level_2_a:
|
175
|
+
- leaf
|
176
|
+
ENDTREE
|
177
|
+
|
178
|
+
expect(node(:leaf).ancestors.to_a).to eq([node(:root), node(:level_1_b), node(:level_2_a)])
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should return the ancestors in correct order even after rearranging" do
|
182
|
+
setup_tree <<-ENDTREE
|
183
|
+
- root:
|
184
|
+
- child:
|
185
|
+
- subchild
|
186
|
+
ENDTREE
|
187
|
+
|
188
|
+
child = node(:child); child.parent = nil; child.save!
|
189
|
+
root = node(:root); root.parent = node(:child); root.save!
|
190
|
+
subchild = node(:subchild); subchild.parent = root; subchild.save!
|
191
|
+
|
192
|
+
expect(subchild.ancestors.to_a).to eq([child, root])
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe 'moving nodes around' do
|
198
|
+
before(:each) do
|
199
|
+
setup_tree <<-ENDTREE
|
200
|
+
- first_root:
|
201
|
+
- first_child_of_first_root
|
202
|
+
- second_child_of_first_root
|
203
|
+
- second_root:
|
204
|
+
- first_child_of_second_root
|
205
|
+
- third_root:
|
206
|
+
- first
|
207
|
+
- second
|
208
|
+
- third
|
209
|
+
ENDTREE
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#move_below' do
|
213
|
+
it 'should fix positions within the current list when moving an sibling away from its current parent' do
|
214
|
+
node_to_move = node(:first_child_of_first_root)
|
215
|
+
node_to_move.move_below(node(:first_child_of_second_root))
|
216
|
+
expect(node(:second_child_of_first_root).position).to eq(0)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should work when moving to a different parent' do
|
220
|
+
node_to_move = node(:first_child_of_first_root)
|
221
|
+
new_parent = node(:second_root)
|
222
|
+
node_to_move.move_below(node(:first_child_of_second_root))
|
223
|
+
node_to_move.reload
|
224
|
+
expect(node_to_move).to be_at_bottom
|
225
|
+
expect(node(:first_child_of_second_root)).to be_at_top
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'should be able to move the first node below the second node' do
|
229
|
+
first_node = node(:first_root)
|
230
|
+
second_node = node(:second_root)
|
231
|
+
first_node.move_below(second_node)
|
232
|
+
first_node.reload
|
233
|
+
second_node.reload
|
234
|
+
expect(second_node).to be_at_top
|
235
|
+
expect(first_node.higher_siblings.to_a).to eq([second_node])
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should be able to move the last node below the first node' do
|
239
|
+
first_node = node(:first_root)
|
240
|
+
last_node = node(:third_root)
|
241
|
+
last_node.move_below(first_node)
|
242
|
+
first_node.reload
|
243
|
+
last_node.reload
|
244
|
+
expect(last_node).not_to be_at_bottom
|
245
|
+
expect(node(:second_root)).to be_at_bottom
|
246
|
+
expect(last_node.higher_siblings.to_a).to eq([first_node])
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe '#move_above' do
|
251
|
+
it 'should fix positions within the current list when moving an sibling away from its current parent' do
|
252
|
+
node_to_move = node(:first_child_of_first_root)
|
253
|
+
node_to_move.move_above(node(:first_child_of_second_root))
|
254
|
+
expect(node(:second_child_of_first_root).position).to eq(0)
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'should work when moving to a different parent' do
|
258
|
+
node_to_move = node(:first_child_of_first_root)
|
259
|
+
new_parent = node(:second_root)
|
260
|
+
node_to_move.move_above(node(:first_child_of_second_root))
|
261
|
+
node_to_move.reload
|
262
|
+
expect(node_to_move).to be_at_top
|
263
|
+
expect(node(:first_child_of_second_root)).to be_at_bottom
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should be able to move the last node above the second node' do
|
267
|
+
last_node = node(:third_root)
|
268
|
+
second_node = node(:second_root)
|
269
|
+
last_node.move_above(second_node)
|
270
|
+
last_node.reload
|
271
|
+
second_node.reload
|
272
|
+
expect(second_node).to be_at_bottom
|
273
|
+
expect(last_node.higher_siblings.to_a).to eq([node(:first_root)])
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'should be able to move the first node above the last node' do
|
277
|
+
first_node = node(:first_root)
|
278
|
+
last_node = node(:third_root)
|
279
|
+
first_node.move_above(last_node)
|
280
|
+
first_node.reload
|
281
|
+
last_node.reload
|
282
|
+
expect(node(:second_root)).to be_at_top
|
283
|
+
expect(first_node.higher_siblings.to_a).to eq([node(:second_root)])
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "#move_to_top" do
|
288
|
+
it "should return true when attempting to move the first sibling" do
|
289
|
+
expect(node(:first_root).move_to_top).to eq(true)
|
290
|
+
expect(node(:first_child_of_first_root).move_to_top).to eq(true)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should be able to move the last sibling to the top" do
|
294
|
+
first_node = node(:first_root)
|
295
|
+
last_node = node(:third_root)
|
296
|
+
last_node.move_to_top
|
297
|
+
first_node.reload
|
298
|
+
expect(last_node).to be_at_top
|
299
|
+
expect(first_node).not_to be_at_top
|
300
|
+
expect(first_node.higher_siblings.to_a).to eq([last_node])
|
301
|
+
expect(last_node.lower_siblings.to_a).to eq([first_node, node(:second_root)])
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#move_to_bottom" do
|
306
|
+
it "should return true when attempting to move the last sibling" do
|
307
|
+
expect(node(:third_root).move_to_bottom).to eq(true)
|
308
|
+
expect(node(:second_child_of_first_root).move_to_bottom).to eq(true)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should be able to move the first sibling to the bottom" do
|
312
|
+
first_node = node(:first_root)
|
313
|
+
middle_node = node(:second_root)
|
314
|
+
last_node = node(:third_root)
|
315
|
+
first_node.move_to_bottom
|
316
|
+
middle_node.reload
|
317
|
+
last_node.reload
|
318
|
+
expect(first_node).not_to be_at_top
|
319
|
+
expect(first_node).to be_at_bottom
|
320
|
+
expect(last_node).not_to be_at_bottom
|
321
|
+
expect(last_node).not_to be_at_top
|
322
|
+
expect(middle_node).to be_at_top
|
323
|
+
expect(first_node.lower_siblings.to_a).to eq([])
|
324
|
+
expect(last_node.higher_siblings.to_a).to eq([middle_node])
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "#move_up" do
|
329
|
+
it "should correctly move nodes up" do
|
330
|
+
node(:third).move_up
|
331
|
+
expect(node(:third_root).children).to eq([node(:first), node(:third), node(:second)])
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe "#move_down" do
|
336
|
+
it "should correctly move nodes down" do
|
337
|
+
node(:first).move_down
|
338
|
+
expect(node(:third_root).children).to eq([node(:second), node(:first), node(:third)])
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end # moving nodes around
|
342
|
+
end # Mongoid::Tree::Ordering
|