acts_as_ordered_tree 1.3.1 → 2.0.0.beta3
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.
- 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
|