acts_as_ordered_tree 1.3.1 → 2.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/acts_as_ordered_tree.rb +22 -100
- data/lib/acts_as_ordered_tree/adapters.rb +17 -0
- data/lib/acts_as_ordered_tree/adapters/abstract.rb +23 -0
- data/lib/acts_as_ordered_tree/adapters/postgresql.rb +150 -0
- data/lib/acts_as_ordered_tree/adapters/recursive.rb +157 -0
- data/lib/acts_as_ordered_tree/compatibility.rb +22 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/association_scope.rb +9 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/default_scoped.rb +19 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/null_relation.rb +71 -0
- data/lib/acts_as_ordered_tree/compatibility/features.rb +153 -0
- data/lib/acts_as_ordered_tree/deprecate.rb +24 -0
- data/lib/acts_as_ordered_tree/hooks.rb +38 -0
- data/lib/acts_as_ordered_tree/hooks/update.rb +86 -0
- data/lib/acts_as_ordered_tree/instance_methods.rb +92 -453
- data/lib/acts_as_ordered_tree/iterators/arranger.rb +35 -0
- data/lib/acts_as_ordered_tree/iterators/level_calculator.rb +52 -0
- data/lib/acts_as_ordered_tree/iterators/orphans_pruner.rb +58 -0
- data/lib/acts_as_ordered_tree/node.rb +78 -0
- data/lib/acts_as_ordered_tree/node/attributes.rb +48 -0
- data/lib/acts_as_ordered_tree/node/movement.rb +62 -0
- data/lib/acts_as_ordered_tree/node/movements.rb +111 -0
- data/lib/acts_as_ordered_tree/node/predicates.rb +98 -0
- data/lib/acts_as_ordered_tree/node/reloading.rb +49 -0
- data/lib/acts_as_ordered_tree/node/siblings.rb +139 -0
- data/lib/acts_as_ordered_tree/node/traversals.rb +53 -0
- data/lib/acts_as_ordered_tree/persevering_transaction.rb +93 -0
- data/lib/acts_as_ordered_tree/position.rb +143 -0
- data/lib/acts_as_ordered_tree/relation/arrangeable.rb +33 -0
- data/lib/acts_as_ordered_tree/relation/iterable.rb +41 -0
- data/lib/acts_as_ordered_tree/relation/preloaded.rb +46 -11
- data/lib/acts_as_ordered_tree/transaction/base.rb +57 -0
- data/lib/acts_as_ordered_tree/transaction/callbacks.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/create.rb +68 -0
- data/lib/acts_as_ordered_tree/transaction/destroy.rb +34 -0
- data/lib/acts_as_ordered_tree/transaction/dsl.rb +214 -0
- data/lib/acts_as_ordered_tree/transaction/factory.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/move.rb +70 -0
- data/lib/acts_as_ordered_tree/transaction/passthrough.rb +12 -0
- data/lib/acts_as_ordered_tree/transaction/reorder.rb +42 -0
- data/lib/acts_as_ordered_tree/transaction/save.rb +64 -0
- data/lib/acts_as_ordered_tree/transaction/update.rb +78 -0
- data/lib/acts_as_ordered_tree/tree.rb +148 -0
- data/lib/acts_as_ordered_tree/tree/association.rb +20 -0
- data/lib/acts_as_ordered_tree/tree/callbacks.rb +57 -0
- data/lib/acts_as_ordered_tree/tree/children_association.rb +120 -0
- data/lib/acts_as_ordered_tree/tree/columns.rb +102 -0
- data/lib/acts_as_ordered_tree/tree/deprecated_columns_accessors.rb +24 -0
- data/lib/acts_as_ordered_tree/tree/parent_association.rb +31 -0
- data/lib/acts_as_ordered_tree/tree/perseverance.rb +19 -0
- data/lib/acts_as_ordered_tree/tree/scopes.rb +56 -0
- data/lib/acts_as_ordered_tree/validators.rb +1 -1
- data/lib/acts_as_ordered_tree/version.rb +1 -1
- data/spec/acts_as_ordered_tree_spec.rb +80 -909
- data/spec/adapters/postgresql_spec.rb +14 -0
- data/spec/adapters/recursive_spec.rb +12 -0
- data/spec/adapters/shared.rb +272 -0
- data/spec/callbacks_spec.rb +177 -0
- data/spec/counter_cache_spec.rb +31 -0
- data/spec/create_spec.rb +110 -0
- data/spec/destroy_spec.rb +57 -0
- data/spec/inheritance_spec.rb +176 -0
- data/spec/move_spec.rb +94 -0
- data/spec/node/movements/concurrent_movements_spec.rb +354 -0
- data/spec/node/movements/move_higher_spec.rb +46 -0
- data/spec/node/movements/move_lower_spec.rb +46 -0
- data/spec/node/movements/move_to_child_of_spec.rb +147 -0
- data/spec/node/movements/move_to_child_with_index_spec.rb +124 -0
- data/spec/node/movements/move_to_child_with_position_spec.rb +85 -0
- data/spec/node/movements/move_to_left_of_spec.rb +120 -0
- data/spec/node/movements/move_to_right_of_spec.rb +120 -0
- data/spec/node/movements/move_to_root_spec.rb +67 -0
- data/spec/node/predicates_spec.rb +211 -0
- data/spec/node/reloading_spec.rb +42 -0
- data/spec/node/siblings_spec.rb +193 -0
- data/spec/node/traversals_spec.rb +71 -0
- data/spec/persevering_transaction_spec.rb +98 -0
- data/spec/relation/arrangeable_spec.rb +88 -0
- data/spec/relation/iterable_spec.rb +104 -0
- data/spec/relation/preloaded_spec.rb +57 -0
- data/spec/reorder_spec.rb +83 -0
- data/spec/spec_helper.rb +30 -38
- data/spec/support/db/boot.rb +22 -0
- data/spec/{db → support/db}/config.travis.yml +2 -0
- data/spec/{db → support/db}/config.yml +1 -0
- data/spec/{db → support/db}/schema.rb +9 -0
- data/spec/support/factories.rb +2 -2
- data/spec/support/matchers.rb +67 -58
- data/spec/support/models.rb +6 -14
- data/spec/support/tree_factory.rb +315 -0
- data/spec/tree/children_association_spec.rb +72 -0
- data/spec/tree/columns_spec.rb +65 -0
- data/spec/tree/scopes_spec.rb +39 -0
- metadata +161 -43
- data/lib/acts_as_ordered_tree/adapters/postgresql_adapter.rb +0 -104
- data/lib/acts_as_ordered_tree/arrangeable.rb +0 -80
- data/lib/acts_as_ordered_tree/class_methods.rb +0 -72
- data/lib/acts_as_ordered_tree/relation/base.rb +0 -26
- data/lib/acts_as_ordered_tree/tenacious_transaction.rb +0 -30
- data/spec/concurrency_support_spec.rb +0 -156
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Node::Movements, '#move_to_root', :transactional do
|
6
|
+
shared_examples '#move_to_root' do |factory, attrs = {}|
|
7
|
+
describe "#move_to_root #{factory}" do
|
8
|
+
tree :factory => factory, :attributes => attrs do
|
9
|
+
root_1 {
|
10
|
+
node_1
|
11
|
+
node_2
|
12
|
+
node_3 {
|
13
|
+
node_4
|
14
|
+
}
|
15
|
+
}
|
16
|
+
root_2 {
|
17
|
+
node_5
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'moving root node to root' do
|
22
|
+
before { root_2.move_to_root }
|
23
|
+
|
24
|
+
expect_tree_to_match {
|
25
|
+
root_1 {
|
26
|
+
node_1
|
27
|
+
node_2
|
28
|
+
node_3 {
|
29
|
+
node_4
|
30
|
+
}
|
31
|
+
}
|
32
|
+
root_2 {
|
33
|
+
node_5
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'moving inner node to root' do
|
39
|
+
before { node_3.move_to_root }
|
40
|
+
|
41
|
+
expect_tree_to_match {
|
42
|
+
root_1 {
|
43
|
+
node_1
|
44
|
+
node_2
|
45
|
+
}
|
46
|
+
root_2 {
|
47
|
+
node_5
|
48
|
+
}
|
49
|
+
node_3 {
|
50
|
+
node_4
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when attribute, not related to tree changed' do
|
56
|
+
before { @old_name = node_2.name }
|
57
|
+
before { node_2.name = 'new name' }
|
58
|
+
|
59
|
+
it { expect{node_2.move_to_root}.to change(node_2, :name).to(@old_name) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
include_examples '#move_to_root', :default
|
65
|
+
include_examples '#move_to_root', :default_with_counter_cache
|
66
|
+
include_examples '#move_to_root', :scoped, :scope_type => 's'
|
67
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Node::Predicates, :transactional do
|
6
|
+
shared_examples 'ActsAsOrderedTree::Node predicates' do |model, attrs = {}|
|
7
|
+
describe model do
|
8
|
+
let!(:root) { create model, attrs }
|
9
|
+
let!(:child) { create model, attrs.merge(:parent => root) }
|
10
|
+
let!(:grandchild) { create model, attrs.merge(:parent => child) }
|
11
|
+
|
12
|
+
let(:counter_cached?) { root.class.ordered_tree.columns.counter_cache? }
|
13
|
+
|
14
|
+
before { [root, child, grandchild].each(&:reload) }
|
15
|
+
|
16
|
+
describe '#root?' do
|
17
|
+
it { expect(root).to be_root }
|
18
|
+
it { expect(child).not_to be_root }
|
19
|
+
it { expect(grandchild).not_to be_root }
|
20
|
+
|
21
|
+
it { expect{root.root?}.not_to query_database }
|
22
|
+
it { expect{child.root?}.not_to query_database }
|
23
|
+
it { expect{grandchild.root?}.not_to query_database }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#has_parent?' do
|
27
|
+
it { expect(root).not_to have_parent }
|
28
|
+
it { expect(child).to have_parent }
|
29
|
+
it { expect(grandchild).to have_parent }
|
30
|
+
|
31
|
+
it { expect{root.has_parent?}.not_to query_database }
|
32
|
+
it { expect{child.has_parent?}.not_to query_database }
|
33
|
+
it { expect{grandchild.has_parent?}.not_to query_database }
|
34
|
+
|
35
|
+
it 'should be aliased but deprecated as #child?' do
|
36
|
+
expect(ActiveSupport::Deprecation).to receive(:warn)
|
37
|
+
expect(root).to receive(:has_parent?)
|
38
|
+
ActiveSupport::Deprecation.silence{root.child?}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#leaf?' do
|
43
|
+
it { expect(root).not_to be_leaf }
|
44
|
+
it { expect(child).not_to be_leaf }
|
45
|
+
it { expect(grandchild).to be_leaf }
|
46
|
+
|
47
|
+
context 'when new record given' do
|
48
|
+
let(:record) { build model, attrs }
|
49
|
+
|
50
|
+
it { expect(record).not_to be_leaf }
|
51
|
+
it { expect{record.leaf?}.not_to query_database }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when :children association is loaded' do
|
55
|
+
before { root.children << build(model, attrs) }
|
56
|
+
before { root.children.reload }
|
57
|
+
|
58
|
+
it { expect(root).not_to be_leaf }
|
59
|
+
it { expect{root.leaf?}.not_to query_database }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when :children association is not loaded' do
|
63
|
+
before { root.children << build(model, attrs) }
|
64
|
+
before { root.reload }
|
65
|
+
|
66
|
+
it { expect(root).not_to be_leaf }
|
67
|
+
it do
|
68
|
+
if counter_cached?
|
69
|
+
expect{root.leaf?}.not_to query_database
|
70
|
+
else
|
71
|
+
# we must check that #leaf? is optimized
|
72
|
+
expect{root.leaf?}.not_to query_database(/COUNT/)
|
73
|
+
expect{root.leaf?}.to query_database(/LIMIT 1/i)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#has_children?' do
|
80
|
+
# opposite of #leaf?
|
81
|
+
it { expect(root).to have_children }
|
82
|
+
it { expect(child).to have_children }
|
83
|
+
it { expect(grandchild).not_to have_children }
|
84
|
+
|
85
|
+
it 'should be aliased but deprecated as #branch?' do
|
86
|
+
expect(ActiveSupport::Deprecation).to receive(:warn)
|
87
|
+
expect(root).to receive(:has_children?)
|
88
|
+
ActiveSupport::Deprecation.silence{root.branch?}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#is_descendant_of?' do
|
93
|
+
it { expect(root) }
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#is_(or_is)_(descendant|ancestor)_of?' do
|
97
|
+
matrix3d = Hash[
|
98
|
+
:is_descendant_of? => Hash[
|
99
|
+
:root => {:root => false, :child => false, :grandchild => false},
|
100
|
+
:child => {:root => true, :child => false, :grandchild => false},
|
101
|
+
:grandchild => {:root => true, :child => true, :grandchild => false}
|
102
|
+
],
|
103
|
+
:is_or_is_descendant_of? => Hash[
|
104
|
+
:root => {:root => true, :child => false, :grandchild => false},
|
105
|
+
:child => {:root => true, :child => true, :grandchild => false},
|
106
|
+
:grandchild => {:root => true, :child => true, :grandchild => true}
|
107
|
+
],
|
108
|
+
:is_ancestor_of? => Hash[
|
109
|
+
:root => {:root => false, :child => true, :grandchild => true},
|
110
|
+
:child => {:root => false, :child => false, :grandchild => true},
|
111
|
+
:grandchild => {:root => false, :child => false, :grandchild => false}
|
112
|
+
],
|
113
|
+
:is_or_is_ancestor_of? => Hash[
|
114
|
+
:root => {:root => true, :child => true, :grandchild => true},
|
115
|
+
:child => {:root => false, :child => true, :grandchild => true},
|
116
|
+
:grandchild => {:root => false, :child => false, :grandchild => true}
|
117
|
+
]
|
118
|
+
]
|
119
|
+
|
120
|
+
matrix3d.each do |method, matrix|
|
121
|
+
matrix.each do |node, examples|
|
122
|
+
examples.each do |target, expectation|
|
123
|
+
it "expect that #{node}.#{method}(#{target}) == #{expectation.inspect}" do
|
124
|
+
expect(send(node).send(method, send(target))).to eq expectation
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe model do
|
134
|
+
let!(:list) { create_list model, 3, attrs }
|
135
|
+
let(:first) { list[0] }
|
136
|
+
let(:second) { list[1] }
|
137
|
+
let(:third) { list[2] }
|
138
|
+
|
139
|
+
describe '#first?' do
|
140
|
+
it { expect(first).to be_first }
|
141
|
+
it { expect(second).not_to be_first }
|
142
|
+
it { expect(third).not_to be_first }
|
143
|
+
|
144
|
+
it 'does not query database' do
|
145
|
+
list.each do |node|
|
146
|
+
expect{node.first?}.not_to query_database
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#last?' do
|
152
|
+
it { expect(first).not_to be_last }
|
153
|
+
it { expect(second).not_to be_last }
|
154
|
+
it { expect(third).to be_last }
|
155
|
+
|
156
|
+
it { expect{first.last?}.to query_database(/LIMIT 1/i).once }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
include_examples 'ActsAsOrderedTree::Node predicates', :default
|
162
|
+
include_examples 'ActsAsOrderedTree::Node predicates', :default_with_counter_cache do
|
163
|
+
describe '#last?' do
|
164
|
+
context 'when node is child' do
|
165
|
+
let(:root) { create :default_with_counter_cache }
|
166
|
+
let!(:node) { create :default_with_counter_cache, :parent => root }
|
167
|
+
|
168
|
+
context 'when parent is loaded' do
|
169
|
+
before { node.association(:parent).reload }
|
170
|
+
|
171
|
+
it { expect(node).to be_last }
|
172
|
+
it { expect{node.last?}.not_to query_database }
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when parent is not loaded' do
|
176
|
+
before { node.reload }
|
177
|
+
it { expect(node).to be_last }
|
178
|
+
it { expect{node.last?}.to query_database.once }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
include_examples 'ActsAsOrderedTree::Node predicates', :scoped, :scope_type => 's' do
|
185
|
+
describe '#is_(or_is)_(descendant|ancestor)_of?' do
|
186
|
+
context 'when nodes belong to different scopes' do
|
187
|
+
let(:root) { create :scoped, :scope_type => 'a' }
|
188
|
+
let(:child) { create :scoped, :parent => root }
|
189
|
+
# hack it
|
190
|
+
before { child.scope_type = 'b' }
|
191
|
+
before { child.class.where(:id => child.id).update_all(['scope_type = ?', 'b']) }
|
192
|
+
|
193
|
+
context 'when parent association is cached and cache is stale' do
|
194
|
+
it { expect(root.is_ancestor_of?(child)).to be false }
|
195
|
+
it { expect(root.is_or_is_ancestor_of?(child)).to be false }
|
196
|
+
it { expect(child.is_descendant_of?(root)).to be false }
|
197
|
+
it { expect(child.is_or_is_descendant_of?(root)).to be false }
|
198
|
+
end
|
199
|
+
|
200
|
+
context 'when parent association is not loaded' do
|
201
|
+
before { child.reload }
|
202
|
+
|
203
|
+
it { expect(root.is_ancestor_of?(child)).to be false }
|
204
|
+
it { expect(root.is_or_is_ancestor_of?(child)).to be false }
|
205
|
+
it { expect(child.is_descendant_of?(root)).to be false }
|
206
|
+
it { expect(child.is_or_is_descendant_of?(root)).to be false }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Node::Reloading, :transactional do
|
6
|
+
shared_examples 'ActsAsOrderedTree::Node#reload' do |model, attrs = {}|
|
7
|
+
describe model.to_s.camelize, '#reload' do
|
8
|
+
let(:record) { create model, attrs }
|
9
|
+
let(:node) { record.ordered_tree_node }
|
10
|
+
|
11
|
+
# change all attributes
|
12
|
+
before { node.parent_id = create(model, attrs).id }
|
13
|
+
before { node.position = 3 }
|
14
|
+
before { node.depth = 2 if record.ordered_tree.columns.depth? }
|
15
|
+
before { record[record.ordered_tree.columns.counter_cache] = 5 if record.ordered_tree.columns.counter_cache? }
|
16
|
+
before { record.name = 'another name' }
|
17
|
+
|
18
|
+
it 'reloads attributes related to tree' do
|
19
|
+
node.reload
|
20
|
+
|
21
|
+
expect(node.parent_id).to eq nil
|
22
|
+
expect(node.position).to eq 1
|
23
|
+
|
24
|
+
if record.ordered_tree.columns.depth?
|
25
|
+
expect(node.depth).to eq 0
|
26
|
+
end
|
27
|
+
|
28
|
+
if record.class.ordered_tree.columns.counter_cache?
|
29
|
+
expect(record.children.size).to eq 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not reload another attributes' do
|
34
|
+
expect{node.reload}.not_to change(record, :name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
include_examples 'ActsAsOrderedTree::Node#reload', :default
|
40
|
+
include_examples 'ActsAsOrderedTree::Node#reload', :default_with_counter_cache
|
41
|
+
include_examples 'ActsAsOrderedTree::Node#reload', :scoped
|
42
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Node::Siblings, :transactional do
|
6
|
+
shared_examples 'siblings' do |model|
|
7
|
+
# silence pending examples
|
8
|
+
#
|
9
|
+
# @todo fix all xits
|
10
|
+
def self.xit(*) end
|
11
|
+
|
12
|
+
let(:root) { create model }
|
13
|
+
let(:items) { create_list model, 3, :parent => root }
|
14
|
+
|
15
|
+
# first
|
16
|
+
it { expect(items.first.self_and_siblings).to eq items }
|
17
|
+
it { expect(items.first.siblings).to eq [items.second, items.last] }
|
18
|
+
|
19
|
+
it { expect(items.first.left_sibling).to be nil }
|
20
|
+
it { expect(items.first.right_sibling).to eq items.second }
|
21
|
+
|
22
|
+
it { expect(items.first.left_siblings).to be_empty }
|
23
|
+
it { expect(items.first.right_siblings).to eq [items.second, items.last] }
|
24
|
+
|
25
|
+
# second
|
26
|
+
it { expect(items.second.self_and_siblings).to eq items }
|
27
|
+
it { expect(items.second.siblings).to eq [items.first, items.last] }
|
28
|
+
|
29
|
+
it { expect(items.second.left_sibling).to eq items.first }
|
30
|
+
it { expect(items.second.right_sibling).to eq items.last }
|
31
|
+
|
32
|
+
it { expect(items.second.left_siblings).to eq [items.first] }
|
33
|
+
it { expect(items.second.right_siblings).to eq [items.last] }
|
34
|
+
|
35
|
+
# last
|
36
|
+
it { expect(items.last.self_and_siblings).to eq items }
|
37
|
+
it { expect(items.last.siblings).to eq [items.first, items.second] }
|
38
|
+
|
39
|
+
it { expect(items.last.left_sibling).to eq items.second }
|
40
|
+
it { expect(items.last.right_sibling).to be nil }
|
41
|
+
|
42
|
+
it { expect(items.last.left_siblings).to eq [items.first, items.second] }
|
43
|
+
it { expect(items.last.right_siblings).to be_empty }
|
44
|
+
|
45
|
+
context 'trying to set left or right sibling with random object' do
|
46
|
+
def self.expect_type_mismatch_on(value)
|
47
|
+
it "throws error when #{value.class} given" do
|
48
|
+
expect {
|
49
|
+
items.first.left_sibling = value
|
50
|
+
}.to raise_error ActiveRecord::AssociationTypeMismatch
|
51
|
+
|
52
|
+
expect {
|
53
|
+
items.first.right_sibling = value
|
54
|
+
}.to raise_error ActiveRecord::AssociationTypeMismatch
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.generate_class
|
59
|
+
Class.new(ActiveRecord::Base){ self.table_name = 'categories' }
|
60
|
+
end
|
61
|
+
|
62
|
+
expect_type_mismatch_on(generate_class.new)
|
63
|
+
expect_type_mismatch_on(nil)
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when left sibling is set' do
|
67
|
+
context 'and new left sibling has same parent' do
|
68
|
+
context 'and new left sibling is higher' do
|
69
|
+
let(:item) { items.last }
|
70
|
+
before { item.left_sibling = items.first }
|
71
|
+
|
72
|
+
it { expect(item.parent).to eq items.first.parent }
|
73
|
+
it { expect(item.position).to eq 2 }
|
74
|
+
|
75
|
+
xit { expect(item.left_sibling).to eq items.first }
|
76
|
+
xit { expect(item.right_siblings).to eq [items.second] }
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'and new left sibling is lower' do
|
80
|
+
let(:item) { items.first }
|
81
|
+
before { item.left_sibling = items.last }
|
82
|
+
|
83
|
+
it { expect(item.parent).to eq items.first.parent }
|
84
|
+
it { expect(item.position).to eq 3 }
|
85
|
+
|
86
|
+
xit { expect(item.left_sibling).to eq items.last }
|
87
|
+
xit { expect(item.left_siblings).to eq [items.second, items.last] }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'and new left sibling has other parent' do
|
92
|
+
let(:item) { items.first }
|
93
|
+
before { item.left_sibling = root }
|
94
|
+
|
95
|
+
it { expect(item.parent).to be nil }
|
96
|
+
it { expect(item.position).to eq 2 }
|
97
|
+
|
98
|
+
xit { expect(item.left_sibling).to eq root }
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'via #left_sibling_id=' do
|
102
|
+
let(:item) { items.first }
|
103
|
+
|
104
|
+
it 'throws error when non-existent ID given' do
|
105
|
+
expect {
|
106
|
+
item.left_sibling_id = -1
|
107
|
+
}.to raise_error ActiveRecord::RecordNotFound
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'delegates to #left_sibling=' do
|
111
|
+
new_sibling = items.last
|
112
|
+
|
113
|
+
expect(item.ordered_tree_node).to receive(:left_sibling=).with(new_sibling)
|
114
|
+
item.left_sibling_id = new_sibling.id
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when right sibling is set' do
|
120
|
+
context 'and new right sibling has same parent' do
|
121
|
+
context 'and new right sibling is higher' do
|
122
|
+
let(:item) { items.last }
|
123
|
+
before { item.right_sibling = items.first }
|
124
|
+
|
125
|
+
it { expect(item.parent).to eq items.first.parent }
|
126
|
+
it { expect(item.position).to eq 1 }
|
127
|
+
|
128
|
+
xit { expect(item.right_sibling).to eq item.first }
|
129
|
+
xit { expect(item.left_siblings).to be_empty }
|
130
|
+
xit { expect(item.right_siblings).to eq [items.first, items.second] }
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'and new right sibling is lower' do
|
134
|
+
let(:item) { items.first }
|
135
|
+
before { item.right_sibling = items.last }
|
136
|
+
|
137
|
+
it { expect(item.parent).to eq items.first.parent }
|
138
|
+
it { expect(item.position).to eq 2 }
|
139
|
+
|
140
|
+
xit { expect(item.right_sibling).to eq items.last }
|
141
|
+
xit { expect(item.right_siblings).to eq [items.last] }
|
142
|
+
|
143
|
+
xit { expect(item.left_sibling).to eq items.first }
|
144
|
+
xit { expect(item.left_siblings).to eq [items.first] }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'and new right sibling has other parent' do
|
149
|
+
let(:item) { items.first }
|
150
|
+
before { item.right_sibling = root }
|
151
|
+
|
152
|
+
it { expect(item.parent).to be nil }
|
153
|
+
it { expect(item.position).to eq 1 }
|
154
|
+
|
155
|
+
xit { expect(item.right_sibling).to eq root }
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'via #right_sibling_id=' do
|
159
|
+
let(:item) { items.first }
|
160
|
+
|
161
|
+
it 'throws error when non-existent ID given' do
|
162
|
+
expect {
|
163
|
+
item.right_sibling_id = -1
|
164
|
+
}.to raise_error ActiveRecord::RecordNotFound
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'delegates to #right_sibling=' do
|
168
|
+
new_sibling = items.last
|
169
|
+
|
170
|
+
expect(item.ordered_tree_node).to receive(:right_sibling=).with(new_sibling)
|
171
|
+
item.right_sibling_id = new_sibling.id
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'Tree without scopes' do
|
178
|
+
include_examples 'siblings', :default
|
179
|
+
include_examples 'siblings', :default_with_counter_cache
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'Tree with scope' do
|
183
|
+
let!(:items_1) { create_list :scoped, 3, :scope_type => 's1' }
|
184
|
+
let!(:items_2) { create_list :scoped, 3, :scope_type => 's2' }
|
185
|
+
|
186
|
+
include_examples 'siblings', :scoped do
|
187
|
+
let(:items) { items_1 }
|
188
|
+
end
|
189
|
+
include_examples 'siblings', :scoped do
|
190
|
+
let(:items) { items_2 }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|