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,71 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Node::Traversals, :transactional do
|
6
|
+
shared_examples 'ActsAsOrderedTree::Node traversals' do |model, attrs = {}|
|
7
|
+
let(:root_1) { create model, attrs }
|
8
|
+
let(:child_1) { create model, attrs.merge(:parent => root_1) }
|
9
|
+
let(:grandchild_1) { create model, attrs.merge(:parent => child_1) }
|
10
|
+
let(:root_2) { create model, attrs }
|
11
|
+
let(:child_2) { create model, attrs.merge(:parent => root_2) }
|
12
|
+
let(:grandchild_2) { create model, attrs.merge(:parent => child_2) }
|
13
|
+
|
14
|
+
before { [root_1, child_1, grandchild_1].each(&:reload) }
|
15
|
+
before { [root_2, child_2, grandchild_2].each(&:reload) }
|
16
|
+
|
17
|
+
describe '#root' do
|
18
|
+
it { expect(root_1.root).to eq root_1 }
|
19
|
+
it { expect{root_1.root}.not_to query_database }
|
20
|
+
it { expect(child_1.root).to eq root_1 }
|
21
|
+
it { expect(grandchild_1.root).to eq root_1 }
|
22
|
+
|
23
|
+
it { expect(root_2.root).to eq root_2 }
|
24
|
+
it { expect{root_2.root}.not_to query_database }
|
25
|
+
it { expect(child_2.root).to eq root_2 }
|
26
|
+
it { expect(grandchild_2.root).to eq root_2 }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#self_and_ancestors' do
|
30
|
+
it { expect(root_1.self_and_ancestors).to eq [root_1] }
|
31
|
+
it { expect{root_1.self_and_ancestors}.not_to query_database }
|
32
|
+
it { expect(child_1.self_and_ancestors).to eq [root_1, child_1] }
|
33
|
+
it { expect(grandchild_1.self_and_ancestors).to eq [root_1, child_1, grandchild_1] }
|
34
|
+
|
35
|
+
it { expect(child_1.self_and_ancestors).to respond_to :each_with_level }
|
36
|
+
it { expect(child_1.self_and_ancestors).to respond_to :each_without_orphans }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#ancestors' do
|
40
|
+
it { expect(root_1.ancestors).to eq [] }
|
41
|
+
it { expect{root_1.ancestors}.not_to query_database }
|
42
|
+
it { expect(child_1.ancestors).to eq [root_1] }
|
43
|
+
it { expect(grandchild_1.ancestors).to eq [root_1, child_1] }
|
44
|
+
|
45
|
+
it { expect(child_1.ancestors).to respond_to :each_with_level }
|
46
|
+
it { expect(child_1.ancestors).to respond_to :each_without_orphans }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#self_and_descendants' do
|
50
|
+
it { expect(root_1.self_and_descendants).to eq [root_1, child_1, grandchild_1] }
|
51
|
+
it { expect(child_1.self_and_descendants).to eq [child_1, grandchild_1] }
|
52
|
+
it { expect(grandchild_1.self_and_descendants).to eq [grandchild_1] }
|
53
|
+
|
54
|
+
it { expect(root_1.self_and_descendants).to respond_to :each_with_level }
|
55
|
+
it { expect(root_1.self_and_descendants).to respond_to :each_without_orphans }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#descendants' do
|
59
|
+
it { expect(root_1.descendants).to eq [child_1, grandchild_1] }
|
60
|
+
it { expect(child_1.descendants).to eq [grandchild_1] }
|
61
|
+
it { expect(grandchild_1.descendants).to eq [] }
|
62
|
+
|
63
|
+
it { expect(root_1.descendants).to respond_to :each_with_level }
|
64
|
+
it { expect(root_1.descendants).to respond_to :each_without_orphans }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
include_examples 'ActsAsOrderedTree::Node traversals', :default
|
69
|
+
include_examples 'ActsAsOrderedTree::Node traversals', :default_with_counter_cache
|
70
|
+
include_examples 'ActsAsOrderedTree::Node traversals', :scoped, :scope_type => 'a'
|
71
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/persevering_transaction'
|
6
|
+
|
7
|
+
describe ActsAsOrderedTree::PerseveringTransaction, :non_transactional do
|
8
|
+
def create_transaction(connection = ActiveRecord::Base.connection)
|
9
|
+
described_class.new(connection)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'Transaction state' do
|
13
|
+
def transaction
|
14
|
+
@transaction ||= create_transaction
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'becomes committed only when real transaction ends' do
|
18
|
+
transaction.start do
|
19
|
+
nested_transaction = create_transaction
|
20
|
+
|
21
|
+
nested_transaction.start { }
|
22
|
+
|
23
|
+
expect(nested_transaction).not_to be_committed
|
24
|
+
expect(transaction).not_to be_committed
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(transaction).to be_committed
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'becomes rolledback when real transaction is rolledback' do
|
31
|
+
transaction.start do
|
32
|
+
raise ActiveRecord::Rollback
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(transaction).to be_rolledback
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'After commit callbacks' do
|
40
|
+
it 'executes callbacks only when real transaction commits' do
|
41
|
+
executed = []
|
42
|
+
|
43
|
+
outer = create_transaction
|
44
|
+
outer.after_commit { executed << 1 }
|
45
|
+
|
46
|
+
outer.start do
|
47
|
+
inner = create_transaction
|
48
|
+
inner.after_commit { executed << 2 }
|
49
|
+
|
50
|
+
inner.start { }
|
51
|
+
|
52
|
+
expect(executed).to be_empty
|
53
|
+
end
|
54
|
+
|
55
|
+
expect(executed).to eq [1, 2]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'Deadlock handling' do
|
60
|
+
def start_in_thread(&block)
|
61
|
+
Thread.start do
|
62
|
+
ActiveRecord::Base.connection_pool.with_connection do |connection|
|
63
|
+
trans = create_transaction(connection)
|
64
|
+
trans.start(&block)
|
65
|
+
trans
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
let!(:resource1) { Default.create!(:name => 'resource 1') }
|
71
|
+
let!(:resource2) { Default.create!(:name => 'resource 2') }
|
72
|
+
|
73
|
+
# this test randomly fails on Rails 3.1
|
74
|
+
it 'Restarts transaction when deadlock occurred' do
|
75
|
+
threads = []
|
76
|
+
|
77
|
+
threads << start_in_thread do
|
78
|
+
resource1.lock!
|
79
|
+
sleep 0.1
|
80
|
+
resource2.lock!
|
81
|
+
sleep 0.1
|
82
|
+
end
|
83
|
+
|
84
|
+
threads << start_in_thread do
|
85
|
+
resource2.lock!
|
86
|
+
sleep 0.1
|
87
|
+
resource1.lock!
|
88
|
+
sleep 0.1
|
89
|
+
end
|
90
|
+
|
91
|
+
transactions = threads.map(&:value)
|
92
|
+
|
93
|
+
expect(transactions[0]).to be_committed
|
94
|
+
expect(transactions[1]).to be_committed
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree::Relation::Arrangeable, :transactional do
|
6
|
+
tree :factory => :default do
|
7
|
+
root {
|
8
|
+
child_1 {
|
9
|
+
grandchild_11
|
10
|
+
grandchild_12
|
11
|
+
}
|
12
|
+
child_2 {
|
13
|
+
grandchild_21
|
14
|
+
grandchild_22
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
specify '#descendants scope should be arrangeable' do
|
20
|
+
expect(root.descendants.arrange).to eq Hash[
|
21
|
+
child_1 => {
|
22
|
+
grandchild_11 => {},
|
23
|
+
grandchild_12 => {}
|
24
|
+
},
|
25
|
+
child_2 => {
|
26
|
+
grandchild_21 => {},
|
27
|
+
grandchild_22 => {}
|
28
|
+
}
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
specify '#self_and_descendants should be arrangeable' do
|
33
|
+
expect(root.self_and_descendants.arrange).to eq Hash[
|
34
|
+
root => {
|
35
|
+
child_1 => {
|
36
|
+
grandchild_11 => {},
|
37
|
+
grandchild_12 => {}
|
38
|
+
},
|
39
|
+
child_2 => {
|
40
|
+
grandchild_21 => {},
|
41
|
+
grandchild_22 => {}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
specify '#ancestors should be arrangeable' do
|
48
|
+
expect(grandchild_11.ancestors.arrange).to eq Hash[
|
49
|
+
root => {
|
50
|
+
child_1 => {}
|
51
|
+
}
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
specify '#self_and_ancestors should be arrangeable' do
|
56
|
+
expect(grandchild_11.self_and_ancestors.arrange).to eq Hash[
|
57
|
+
root => {
|
58
|
+
child_1 => {
|
59
|
+
grandchild_11 => {}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should not discard orphaned nodes by default' do
|
66
|
+
relation = root.descendants.where(root.class.arel_table[:id].not_eq(child_1.id))
|
67
|
+
|
68
|
+
expect(relation.arrange).to eq Hash[
|
69
|
+
grandchild_11 => {},
|
70
|
+
grandchild_12 => {},
|
71
|
+
child_2 => {
|
72
|
+
grandchild_21 => {},
|
73
|
+
grandchild_22 => {}
|
74
|
+
}
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should discard orphans if option :discard passed' do
|
79
|
+
relation = root.descendants.where(root.class.arel_table[:id].not_eq(child_1.id))
|
80
|
+
|
81
|
+
expect(relation.arrange(:orphans => :discard)).to eq Hash[
|
82
|
+
child_2 => {
|
83
|
+
grandchild_21 => {},
|
84
|
+
grandchild_22 => {}
|
85
|
+
}
|
86
|
+
]
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/relation/iterable'
|
6
|
+
|
7
|
+
describe ActsAsOrderedTree::Relation::Iterable, :transactional do
|
8
|
+
shared_examples 'iterable' do |model|
|
9
|
+
tree :factory => model do
|
10
|
+
root_1 {
|
11
|
+
child_1 {
|
12
|
+
child_2
|
13
|
+
}
|
14
|
+
child_3 {
|
15
|
+
child_4
|
16
|
+
child_5
|
17
|
+
}
|
18
|
+
}
|
19
|
+
root_2 {
|
20
|
+
child_6
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#each_with_level' do
|
25
|
+
it 'iterates over collection and yields level' do
|
26
|
+
relation = current_tree.order(:id).extending(described_class)
|
27
|
+
|
28
|
+
expect { |b|
|
29
|
+
relation.each_with_level(&b)
|
30
|
+
}.to yield_successive_args [root_1, 0],
|
31
|
+
[child_1, 1],
|
32
|
+
[child_2, 2],
|
33
|
+
[child_3, 1],
|
34
|
+
[child_4, 2],
|
35
|
+
[child_5, 2],
|
36
|
+
[root_2, 0],
|
37
|
+
[child_6, 1]
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'computes level relative to first selected node' do
|
41
|
+
expect { |b|
|
42
|
+
root_1.descendants.extending(described_class).each_with_level(&b)
|
43
|
+
}.to yield_successive_args [child_1, 1],
|
44
|
+
[child_2, 2],
|
45
|
+
[child_3, 1],
|
46
|
+
[child_4, 2],
|
47
|
+
[child_5, 2]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#each_without_orphans' do
|
52
|
+
let(:relation) { current_tree.order(:id).extending(described_class) }
|
53
|
+
|
54
|
+
it 'iterates over collection' do
|
55
|
+
expect { |b|
|
56
|
+
relation.each_without_orphans(&b)
|
57
|
+
}.to yield_successive_args root_1,
|
58
|
+
child_1,
|
59
|
+
child_2,
|
60
|
+
child_3,
|
61
|
+
child_4,
|
62
|
+
child_5,
|
63
|
+
root_2,
|
64
|
+
child_6
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'iterates over collection and discards orphans' do
|
68
|
+
expect { |b|
|
69
|
+
relation.where('id != ?', child_3.id).each_without_orphans(&b)
|
70
|
+
}.to yield_successive_args root_1,
|
71
|
+
child_1,
|
72
|
+
child_2,
|
73
|
+
root_2,
|
74
|
+
child_6
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'iterates over collection and discards orphans' do
|
78
|
+
expect { |b|
|
79
|
+
relation.where('id != ?', root_2.id).each_without_orphans(&b)
|
80
|
+
}.to yield_successive_args root_1,
|
81
|
+
child_1,
|
82
|
+
child_2,
|
83
|
+
child_3,
|
84
|
+
child_4,
|
85
|
+
child_5
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'iterates over collection and discards orphans' do
|
89
|
+
expect { |b|
|
90
|
+
relation.where('id != ?', root_1.id).each_without_orphans(&b)
|
91
|
+
}.to yield_successive_args root_2,
|
92
|
+
child_6
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'Model with cached level' do
|
98
|
+
it_behaves_like 'iterable', :default
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'Model without cached level' do
|
102
|
+
it_behaves_like 'iterable', :default_without_depth
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/relation/preloaded'
|
6
|
+
|
7
|
+
describe ActsAsOrderedTree::Relation::Preloaded, :transactional do
|
8
|
+
let!(:records) { create_list :default, 2 }
|
9
|
+
|
10
|
+
def relation
|
11
|
+
Default.where(nil).extending(described_class)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when preloaded records were not set' do
|
15
|
+
it { expect(relation).to match_array records }
|
16
|
+
it { expect(relation.to_a).not_to be records }
|
17
|
+
it { expect{relation.to_a}.to query_database.once }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when preloaded records were set directly' do
|
21
|
+
let(:preloaded) { relation.records(records) }
|
22
|
+
|
23
|
+
it { expect(preloaded).to eq records }
|
24
|
+
|
25
|
+
it { expect(preloaded.to_a).to be records }
|
26
|
+
it { expect{preloaded.to_a}.not_to query_database }
|
27
|
+
|
28
|
+
it { expect(preloaded.size).to eq 2 }
|
29
|
+
it { expect{preloaded.size}.not_to query_database }
|
30
|
+
|
31
|
+
context 'when preloaded relation was extended' do
|
32
|
+
let(:extended) { preloaded.extending(Module.new) }
|
33
|
+
|
34
|
+
it { expect(extended).to eq records }
|
35
|
+
|
36
|
+
it { expect(extended.to_a).to be records }
|
37
|
+
it { expect{extended.to_a}.not_to query_database }
|
38
|
+
|
39
|
+
it { expect(extended.size).to eq 2 }
|
40
|
+
it { expect{extended.size}.not_to query_database }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#reverse_order' do
|
44
|
+
it { expect(preloaded.reverse_order).not_to be preloaded }
|
45
|
+
it { expect(preloaded.reverse_order.size).to eq 2 }
|
46
|
+
it { expect(preloaded.reverse_order).to eq records.reverse }
|
47
|
+
it { expect{preloaded.reverse_order.to_a}.not_to query_database }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#reverse_order!' do
|
51
|
+
it { expect(preloaded.reverse_order!).to be preloaded }
|
52
|
+
it { expect(preloaded.reverse_order!.size).to eq 2 }
|
53
|
+
it { expect(preloaded.reverse_order!.to_a).to eq records.reverse }
|
54
|
+
it { expect{preloaded.reverse_order!.to_a}.not_to query_database }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'Reorder via save', :transactional do
|
6
|
+
tree :factory => :default do
|
7
|
+
root {
|
8
|
+
child_1
|
9
|
+
child_2
|
10
|
+
child_3
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def reorder(node, position)
|
15
|
+
name = "category #{rand(100..1000)}"
|
16
|
+
node.position = position
|
17
|
+
node.name = name
|
18
|
+
expect { node.save! }.not_to raise_error
|
19
|
+
expect(node.name).to eq name
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_order(*nodes)
|
23
|
+
nodes.each_with_index do |node, index|
|
24
|
+
expect(node.reload.position).to eq index + 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when I change position to lower' do
|
29
|
+
before { reorder child_2, 1 }
|
30
|
+
|
31
|
+
it 'moves node up' do
|
32
|
+
assert_order child_2, child_1, child_3
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when I change position to lower' do
|
37
|
+
before { reorder child_2, 3 }
|
38
|
+
|
39
|
+
it 'moves node down' do
|
40
|
+
assert_order child_1, child_3, child_2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when I move highest node lower' do
|
45
|
+
before { reorder child_1, 3 }
|
46
|
+
|
47
|
+
it 'moves node down' do
|
48
|
+
assert_order child_2, child_3, child_1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when I move lowest node upper' do
|
53
|
+
before { reorder child_3, 1 }
|
54
|
+
|
55
|
+
it 'moves node down' do
|
56
|
+
assert_order child_3, child_1, child_2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when I move to very high position' do
|
61
|
+
before { reorder child_1, 5 }
|
62
|
+
|
63
|
+
it 'moves node to bottom' do
|
64
|
+
assert_order child_2, child_3, child_1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when I move to zero position' do
|
69
|
+
before { reorder child_2, 0 }
|
70
|
+
|
71
|
+
it 'moves it to top' do
|
72
|
+
assert_order child_2, child_1, child_3
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when I move to same position' do
|
77
|
+
before { reorder child_2, 2 }
|
78
|
+
|
79
|
+
specify 'order remains the same' do
|
80
|
+
assert_order child_1, child_2, child_3
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|