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,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, ':counter_cache option', :transactional do
|
6
|
+
describe 'Class without counter cache, #children.size' do
|
7
|
+
tree :factory => :default do
|
8
|
+
root {
|
9
|
+
child_1
|
10
|
+
child_2
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
it { expect(root.children.size).to eq 2 }
|
15
|
+
it { expect{root.children.size}.to query_database.once }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'Class with counter cache, #children.size' do
|
19
|
+
tree :factory => :default_with_counter_cache do
|
20
|
+
root {
|
21
|
+
child_1
|
22
|
+
child_2
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
before { root.reload }
|
27
|
+
|
28
|
+
it { expect(root.children.size).to eq 2 }
|
29
|
+
it { expect{root.children.size}.not_to query_database }
|
30
|
+
end
|
31
|
+
end
|
data/spec/create_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'Create node', :transactional do
|
6
|
+
shared_examples 'create ordered tree node' do |model = :default|
|
7
|
+
let(:record) { build model }
|
8
|
+
|
9
|
+
before { record.parent = parent }
|
10
|
+
|
11
|
+
context 'when position is nil' do
|
12
|
+
before { record.position = nil }
|
13
|
+
|
14
|
+
it 'does not change node parent' do
|
15
|
+
expect{record.save}.not_to change(record, :parent)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'puts record to position = 1 when there are no siblings' do
|
19
|
+
expect{record.save}.to change(record, :position).from(nil).to(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'puts record to bottom position when there are some siblings' do
|
23
|
+
create model, :parent => parent
|
24
|
+
|
25
|
+
expect{record.save}.to change(record, :position).from(nil).to(2)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'calculates depth column' do
|
29
|
+
if record.ordered_tree.columns.depth?
|
30
|
+
expect{record.save}.to change(record, :depth).from(nil).to(parent ? parent.depth + 1 : 0)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when position != nil' do
|
36
|
+
before { record.position = 3 }
|
37
|
+
|
38
|
+
it 'changes position to 1 if siblings is empty' do
|
39
|
+
expect{record.save}.to change(record, :position).from(3).to(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'changes position to highest if there are too few siblings' do
|
43
|
+
create model, :parent => parent
|
44
|
+
|
45
|
+
expect{record.save}.to change(record, :position).from(3).to(2)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'increments position of lower siblings on insert' do
|
49
|
+
first = create model, :parent => parent
|
50
|
+
second = create model, :parent => parent
|
51
|
+
third = create model, :parent => parent
|
52
|
+
|
53
|
+
expect(first.reload.position).to eq 1
|
54
|
+
expect(second.reload.position).to eq 2
|
55
|
+
expect(third.reload.position).to eq 3
|
56
|
+
|
57
|
+
expect{record.save and third.reload}.to change(third, :position).from(3).to(4)
|
58
|
+
|
59
|
+
expect(first.reload.position).to eq 1
|
60
|
+
expect(second.reload.position).to eq 2
|
61
|
+
expect(record.reload.position).to eq 3
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'calculates depth column' do
|
65
|
+
if record.ordered_tree.columns.depth?
|
66
|
+
expect{record.save}.to change(record, :depth).from(nil).to(parent ? parent.depth + 1 : 0)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when parent is nil' do
|
73
|
+
include_examples 'create ordered tree node' do
|
74
|
+
let(:parent) { nil }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when parent exists' do
|
79
|
+
include_examples 'create ordered tree node' do
|
80
|
+
let(:parent) { create :default }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when parent exists (scoped)' do
|
85
|
+
include_examples 'create ordered tree node', :scoped do
|
86
|
+
let(:type) { 'scope' }
|
87
|
+
let(:parent) { create :scoped, :scope_type => type }
|
88
|
+
|
89
|
+
it 'copies scope columns values from parent node' do
|
90
|
+
expect{record.save}.to change(record, :scope_type).to(parent.scope_type)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'when counter_cache exists' do
|
96
|
+
include_examples 'create ordered tree node', :default_with_counter_cache do
|
97
|
+
let(:parent) { create :default_with_counter_cache }
|
98
|
+
|
99
|
+
it 'sets counter_cache to 0 for new record' do
|
100
|
+
record.save
|
101
|
+
|
102
|
+
expect(record.categories_count).to eq 0
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'increments counter_cache of parent' do
|
106
|
+
expect{record.save and parent.reload}.to change(parent, :categories_count).by(1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'Destroy node', :transactional do
|
6
|
+
shared_examples 'destroy ordered tree node' do |model = :default, attrs = {}|
|
7
|
+
tree :factory => model, :attributes => attrs do
|
8
|
+
root {
|
9
|
+
child_1 {
|
10
|
+
grandchild_1 {
|
11
|
+
grandchild_2
|
12
|
+
}
|
13
|
+
}
|
14
|
+
child_2
|
15
|
+
child_3
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def assert_destroyed(record)
|
20
|
+
expect(record.class).not_to exist(record.id)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'destroys descendants' do
|
24
|
+
child_1.destroy
|
25
|
+
|
26
|
+
assert_destroyed(grandchild_1)
|
27
|
+
assert_destroyed(grandchild_2)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'decrements lower siblings positions' do
|
31
|
+
child_1.destroy
|
32
|
+
|
33
|
+
[child_2, child_3].each(&:reload)
|
34
|
+
|
35
|
+
expect(child_2.position).to eq 1
|
36
|
+
expect(child_3.position).to eq 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'Default model' do
|
41
|
+
include_examples 'destroy ordered tree node', :default
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'Scoped model' do
|
45
|
+
include_examples 'destroy ordered tree node', :scoped, :scope_type => 't'
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'Model with counter cache' do
|
49
|
+
include_examples 'destroy ordered tree node', :default_with_counter_cache
|
50
|
+
|
51
|
+
before { root.reload }
|
52
|
+
|
53
|
+
it 'decrements parent children counter' do
|
54
|
+
expect{child_1.destroy and root.reload}.to change(root, :categories_count).from(3).to(2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'inheritance without STI', :transactional do
|
6
|
+
class BaseCategory < ActiveRecord::Base
|
7
|
+
self.table_name = 'categories'
|
8
|
+
|
9
|
+
acts_as_ordered_tree
|
10
|
+
end
|
11
|
+
|
12
|
+
class ConcreteCategory < BaseCategory
|
13
|
+
end
|
14
|
+
|
15
|
+
class ConcreteCategoryWithScope < BaseCategory
|
16
|
+
default_scope { where(arel_table[:name].matches('* %')) }
|
17
|
+
end
|
18
|
+
|
19
|
+
let!(:root) { BaseCategory.create(:name => '* root') }
|
20
|
+
let!(:child_1) { BaseCategory.create(:name => 'child 1', :parent => root) }
|
21
|
+
let!(:child_2) { BaseCategory.create(:name => 'child 2', :parent => child_1) }
|
22
|
+
let!(:child_3) { BaseCategory.create(:name => 'child 3', :parent => child_1) }
|
23
|
+
let!(:child_4) { BaseCategory.create(:name => '* child 4', :parent => root) }
|
24
|
+
let!(:child_5) { BaseCategory.create(:name => '* child 5', :parent => child_4) }
|
25
|
+
let!(:child_6) { BaseCategory.create(:name => 'child 6', :parent => child_4) }
|
26
|
+
|
27
|
+
matcher :be_of do |klass|
|
28
|
+
match do |relation|
|
29
|
+
expect(relation.map(&:class).uniq).to eq [klass]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
shared_examples 'Inheritance test' do |klass|
|
34
|
+
describe "#{klass.name}#children" do
|
35
|
+
let(:root_node) { root.becomes(klass) }
|
36
|
+
|
37
|
+
it { expect(root_node).to be_a klass }
|
38
|
+
it { expect(root_node.children).to be_of klass }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#{klass.name}#parent" do
|
42
|
+
let(:node) { child_5.becomes(klass) }
|
43
|
+
|
44
|
+
it { expect(node.parent).to be_an_instance_of klass }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#{klass.name}#root" do
|
48
|
+
let(:root_node) { root.becomes(klass) }
|
49
|
+
let(:node) { child_5.becomes(klass) }
|
50
|
+
|
51
|
+
it { expect(node.root).to eq root_node }
|
52
|
+
it { expect(node.root).to be_an_instance_of klass }
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#{klass.name}#descendants" do
|
56
|
+
let(:node) { child_4.becomes(klass) }
|
57
|
+
|
58
|
+
it { expect(node.descendants).to be_of klass }
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#{klass.name}#ancestors" do
|
62
|
+
let(:node) { child_5.becomes(klass) }
|
63
|
+
|
64
|
+
it { expect(node.ancestors).to be_of klass }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#{klass.name} tree validators" do
|
68
|
+
it 'calls validators only once' do
|
69
|
+
expect_any_instance_of(ActsAsOrderedTree::Validators::CyclicReferenceValidator).to receive(:validate).once
|
70
|
+
|
71
|
+
child_1.becomes(klass).save
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
include_examples 'Inheritance test', BaseCategory
|
77
|
+
include_examples 'Inheritance test', ConcreteCategory
|
78
|
+
include_examples 'Inheritance test', ConcreteCategoryWithScope do
|
79
|
+
let(:klass) { ConcreteCategoryWithScope }
|
80
|
+
|
81
|
+
describe 'ConcreteCategoryWithScope#children' do
|
82
|
+
let(:node) { klass.find(child_4.id) }
|
83
|
+
|
84
|
+
it { expect(node).to be_a klass }
|
85
|
+
it { expect(node.children).to be_of klass }
|
86
|
+
|
87
|
+
it 'applies class default scope to #children' do
|
88
|
+
expect(node.children.size).to eq 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'ConcreteCategoryWithScope#parent' do
|
93
|
+
let(:orphaned) { child_2.becomes(klass) }
|
94
|
+
let(:out_of_scope_with_proper_parent) { child_1.becomes(klass) }
|
95
|
+
|
96
|
+
it { expect(orphaned.parent).to be_nil }
|
97
|
+
it { expect(out_of_scope_with_proper_parent.parent).to eq root.becomes(klass) }
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'ConcreteCategoryWithScope#descendants' do
|
101
|
+
let(:root_node) { klass.find(root.id) }
|
102
|
+
|
103
|
+
it { expect(root_node.descendants).to be_of klass }
|
104
|
+
it { expect(root_node.descendants.map(&:id)).to eq [child_4.id, child_5.id] }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe ActsAsOrderedTree, 'inheritance with STI', :transactional do
|
110
|
+
class StiRoot < StiExample
|
111
|
+
end
|
112
|
+
|
113
|
+
class StiExample1 < StiExample
|
114
|
+
end
|
115
|
+
|
116
|
+
class StiExample2 < StiExample
|
117
|
+
end
|
118
|
+
|
119
|
+
# build tree
|
120
|
+
let!(:root) { StiRoot.create(:name => 'root') }
|
121
|
+
let!(:child_1) { StiExample1.create(:name => 'child 1', :parent => root) }
|
122
|
+
let!(:child_2) { StiExample1.create(:name => 'child 2', :parent => child_1) }
|
123
|
+
let!(:child_3) { StiExample1.create(:name => 'child 3', :parent => child_1) }
|
124
|
+
let!(:child_4) { StiExample2.create(:name => 'child 4', :parent => root) }
|
125
|
+
let!(:child_5) { StiExample2.create(:name => 'child 5', :parent => child_4) }
|
126
|
+
let!(:child_6) { StiExample2.create(:name => 'child 6', :parent => child_4) }
|
127
|
+
|
128
|
+
before { [root, child_1, child_2, child_3, child_4, child_5, child_6].each(&:reload) }
|
129
|
+
|
130
|
+
describe '#children' do
|
131
|
+
it { expect(root.children).to eq [child_1, child_4] }
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#parent' do
|
135
|
+
it { expect(child_1.parent).to eq root }
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#descendants' do
|
139
|
+
it { expect(root.descendants).to eq [child_1, child_2, child_3, child_4, child_5, child_6] }
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '#ancestors' do
|
143
|
+
it { expect(child_5.ancestors).to eq [root, child_4] }
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#root' do
|
147
|
+
it { expect(child_5.root).to eq root }
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#left_sibling' do
|
151
|
+
it { expect(child_4.left_sibling).to eq child_1 }
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#right_sibling' do
|
155
|
+
it { expect(child_1.right_sibling).to eq child_4 }
|
156
|
+
end
|
157
|
+
|
158
|
+
describe 'predicates' do
|
159
|
+
it { expect(root).to be_is_ancestor_of(child_1) }
|
160
|
+
it { expect(root).to be_is_or_is_ancestor_of(child_1) }
|
161
|
+
it { expect(child_1).to be_is_descendant_of(root) }
|
162
|
+
it { expect(child_1).to be_is_or_is_descendant_of(root) }
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'node reload' do
|
166
|
+
it { expect(child_1.ordered_tree_node.reload).to eq child_1 }
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'node moving' do
|
170
|
+
before { child_4.move_to_child_of(child_1) }
|
171
|
+
|
172
|
+
it { expect(child_4.parent).to eq child_1 }
|
173
|
+
it { expect(child_1.children).to include child_4 }
|
174
|
+
it { expect(child_1.descendants).to include child_4 }
|
175
|
+
end
|
176
|
+
end
|
data/spec/move_spec.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'Movement via save', :transactional do
|
6
|
+
tree :factory => :default_with_counter_cache do
|
7
|
+
root {
|
8
|
+
child_1 {
|
9
|
+
child_2
|
10
|
+
child_3
|
11
|
+
}
|
12
|
+
child_4 {
|
13
|
+
child_5
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def move(node, new_parent, new_position)
|
19
|
+
name = "category #{rand(100..1000)}"
|
20
|
+
node.parent = new_parent
|
21
|
+
node.position = new_position
|
22
|
+
node.name = name
|
23
|
+
|
24
|
+
node.save
|
25
|
+
|
26
|
+
expect(node.reload.parent).to eq new_parent
|
27
|
+
expect(node.position).to eq new_position
|
28
|
+
expect(node.name).to eq name
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when child 2 moved under child 4 to position 1' do
|
32
|
+
before { move child_2, child_4, 1 }
|
33
|
+
|
34
|
+
expect_tree_to_match {
|
35
|
+
root {
|
36
|
+
child_1 :categories_count => 1 do
|
37
|
+
child_3 :position => 1
|
38
|
+
end
|
39
|
+
child_4 :categories_count => 2 do
|
40
|
+
child_2 :position => 1
|
41
|
+
child_5 :position => 2
|
42
|
+
end
|
43
|
+
}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when child 2 moved under child 4 to position 2' do
|
48
|
+
before { move child_2, child_4, 2 }
|
49
|
+
|
50
|
+
expect_tree_to_match {
|
51
|
+
root {
|
52
|
+
child_1 :categories_count => 1 do
|
53
|
+
child_3 :position => 1
|
54
|
+
end
|
55
|
+
child_4 :categories_count => 2 do
|
56
|
+
child_5 :position => 1
|
57
|
+
child_2 :position => 2
|
58
|
+
end
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when level changed' do
|
64
|
+
before { move child_1, child_4, 1 }
|
65
|
+
|
66
|
+
expect_tree_to_match {
|
67
|
+
root {
|
68
|
+
child_4 :categories_count => 2 do
|
69
|
+
child_1 :position => 1, :categories_count => 2, :depth => 2 do
|
70
|
+
child_2 :position => 1, :depth => 3
|
71
|
+
child_3 :position => 2, :depth => 3
|
72
|
+
end
|
73
|
+
child_5 :position => 2, :depth => 2
|
74
|
+
end
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when node moved to root' do
|
80
|
+
before { move child_1, nil, 1 }
|
81
|
+
|
82
|
+
expect_tree_to_match {
|
83
|
+
child_1 :position => 1, :depth => 0 do
|
84
|
+
child_2 :position => 1, :depth => 1
|
85
|
+
child_3 :position => 2, :depth => 1
|
86
|
+
end
|
87
|
+
root :position => 2, :depth => 0 do
|
88
|
+
child_4 :depth => 1 do
|
89
|
+
child_5 :depth => 2
|
90
|
+
end
|
91
|
+
end
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|