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,14 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/adapters/postgresql'
|
6
|
+
require 'adapters/shared'
|
7
|
+
|
8
|
+
describe ActsAsOrderedTree::Adapters::PostgreSQL, :transactional, :if => ENV['DB'] == 'pg' do
|
9
|
+
it { expect(Default.ordered_tree.adapter).to be_a described_class }
|
10
|
+
|
11
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::PostgreSQL, :default
|
12
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::PostgreSQL, :default_with_counter_cache
|
13
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::PostgreSQL, :scoped
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'acts_as_ordered_tree/adapters/recursive'
|
6
|
+
require 'adapters/shared'
|
7
|
+
|
8
|
+
describe ActsAsOrderedTree::Adapters::Recursive, :transactional do
|
9
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::Recursive, :default
|
10
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::Recursive, :default_with_counter_cache
|
11
|
+
it_behaves_like 'ActsAsOrderedTree adapter', ActsAsOrderedTree::Adapters::Recursive, :scoped
|
12
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
shared_examples 'ActsAsOrderedTree adapter' do |adapter_class, model, attrs = {}|
|
4
|
+
context model.to_s do
|
5
|
+
tree :factory => model, :attributes => attrs do
|
6
|
+
root {
|
7
|
+
child_1 {
|
8
|
+
grandchild_11
|
9
|
+
grandchild_12
|
10
|
+
}
|
11
|
+
child_2 {
|
12
|
+
grandchild_21
|
13
|
+
grandchild_22
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:adapter) { adapter_class.new(current_tree.ordered_tree) }
|
19
|
+
|
20
|
+
shared_examples 'ActsAsOrderedTree traverse down for not persisted record' do |method|
|
21
|
+
context 'when new record given' do
|
22
|
+
let(:record) { build model, attrs.merge(:parent => root) }
|
23
|
+
|
24
|
+
subject(:relation) { adapter.send(method, record) }
|
25
|
+
|
26
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
27
|
+
|
28
|
+
it 'returns empty relation' do
|
29
|
+
expect(relation.to_a).to eq []
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not execute SQL queries' do
|
33
|
+
expect{relation.to_a}.not_to query_database
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when destroyed record given' do
|
38
|
+
before { child_1.destroy }
|
39
|
+
|
40
|
+
subject(:relation) { adapter.send(method, child_1) }
|
41
|
+
|
42
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
43
|
+
|
44
|
+
it 'returns empty relation' do
|
45
|
+
expect(relation.to_a).to eq []
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'does not execute SQL queries' do
|
49
|
+
expect{relation.to_a}.not_to query_database
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#self_and_descendants' do
|
55
|
+
context 'when persisted record given' do
|
56
|
+
subject(:relation) { adapter.self_and_descendants(root) }
|
57
|
+
|
58
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
59
|
+
|
60
|
+
it 'returns node and its descendants ordered by position' do
|
61
|
+
expect(relation.to_a).to eq Array[
|
62
|
+
root,
|
63
|
+
child_1,
|
64
|
+
grandchild_11,
|
65
|
+
grandchild_12,
|
66
|
+
child_2,
|
67
|
+
grandchild_21,
|
68
|
+
grandchild_22
|
69
|
+
]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
include_examples 'ActsAsOrderedTree traverse down for not persisted record', :self_and_descendants
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#self_and_descendants with traversal filters' do
|
77
|
+
it 'applies given block to recursive term' do
|
78
|
+
relation = adapter.self_and_descendants(root) { |d| d.where(d.table[:id].not_eq(child_1.id)) }
|
79
|
+
|
80
|
+
expect(relation).to eq [root, child_2, grandchild_21, grandchild_22]
|
81
|
+
end
|
82
|
+
|
83
|
+
example 'start conditions can be changed via #start_with method' do
|
84
|
+
relation = adapter.self_and_descendants(root) { |x| x.start_with { |s| s.where('id != ?', root.id) } }
|
85
|
+
|
86
|
+
expect(relation).to be_empty
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#descendants' do
|
91
|
+
context 'when persisted record given' do
|
92
|
+
subject(:relation) { adapter.descendants(root) }
|
93
|
+
|
94
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
95
|
+
|
96
|
+
it 'returns node and its descendants ordered by position' do
|
97
|
+
expect(relation.to_a).to eq Array[
|
98
|
+
child_1,
|
99
|
+
grandchild_11,
|
100
|
+
grandchild_12,
|
101
|
+
child_2,
|
102
|
+
grandchild_21,
|
103
|
+
grandchild_22
|
104
|
+
]
|
105
|
+
|
106
|
+
expect(adapter.descendants(child_1)).to eq [grandchild_11, grandchild_12]
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'updates descendants' do
|
110
|
+
relation.update_all(:name => 'x')
|
111
|
+
expect(relation.all? { |r| r.reload.name == 'x' }).to be true
|
112
|
+
|
113
|
+
adapter.descendants(child_1).update_all(:name => 'y')
|
114
|
+
adapter.descendants(child_2).update_all(:name => 'z')
|
115
|
+
|
116
|
+
expect(relation.map { |r| r.reload.name }).to eq ['x', 'y', 'y', 'x', 'z', 'z']
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
include_examples 'ActsAsOrderedTree traverse down for not persisted record', :descendants
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#descendants with traversal filters' do
|
124
|
+
it 'applies given block to recursive term' do
|
125
|
+
relation = adapter.descendants(root) { |d| d.where(d.table[:id].not_eq(child_1.id)) }
|
126
|
+
|
127
|
+
expect(relation).to eq [child_2, grandchild_21, grandchild_22]
|
128
|
+
end
|
129
|
+
|
130
|
+
example 'start conditions can be changed via #start_with method' do
|
131
|
+
relation = adapter.descendants(root) { |x| x.start_with { |s| s.where('id != ?', root.id) } }
|
132
|
+
|
133
|
+
expect(relation).to be_empty
|
134
|
+
end
|
135
|
+
|
136
|
+
example 'siblings order can be changed via #order_siblings method' do
|
137
|
+
relation = adapter.descendants(root) { |x| x.order_siblings('position desc') }
|
138
|
+
|
139
|
+
expect(relation).to eq Array[
|
140
|
+
child_2,
|
141
|
+
grandchild_22,
|
142
|
+
grandchild_21,
|
143
|
+
child_1,
|
144
|
+
grandchild_12,
|
145
|
+
grandchild_11
|
146
|
+
]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#self_and_ancestors' do
|
151
|
+
context 'when persisted record given' do
|
152
|
+
context 'when level > 0' do
|
153
|
+
subject(:relation) { adapter.self_and_ancestors(grandchild_11) }
|
154
|
+
|
155
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
156
|
+
|
157
|
+
it 'returns all node ancestors and itself starting from root' do
|
158
|
+
expect(relation.to_a).to eq Array[root, child_1, grandchild_11]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'when level = 0' do
|
163
|
+
subject(:relation) { adapter.self_and_ancestors(root) }
|
164
|
+
|
165
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
166
|
+
|
167
|
+
it { expect(relation.to_a).to eq [root] }
|
168
|
+
|
169
|
+
it 'does not query database' do
|
170
|
+
expect{relation.to_a}.not_to query_database
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when new record given' do
|
176
|
+
let(:record_grandparent) { build model, attrs.merge(:parent => grandchild_11) }
|
177
|
+
let(:record_parent) { build model, attrs.merge(:parent => record_grandparent) }
|
178
|
+
let(:record) { build model, attrs.merge(:parent => record_parent) }
|
179
|
+
|
180
|
+
subject(:relation) { adapter.self_and_ancestors(record) }
|
181
|
+
|
182
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
183
|
+
|
184
|
+
it 'returns all node ancestors and itself starting from root' do
|
185
|
+
expect(relation.to_a).to eq Array[root, child_1, grandchild_11, record_grandparent, record_parent, record]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'when destroyed record given' do
|
190
|
+
before { grandchild_11.destroy }
|
191
|
+
|
192
|
+
subject(:relation) { adapter.self_and_ancestors(grandchild_11) }
|
193
|
+
|
194
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
195
|
+
|
196
|
+
it 'returns all node ancestors and itself starting from root' do
|
197
|
+
expect(relation.to_a).to eq Array[root, child_1, grandchild_11]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#self_and_ancestors with traversal filters' do
|
203
|
+
it 'applies given block to recursive term' do
|
204
|
+
relation = adapter.self_and_ancestors(grandchild_11) { |a| a.where(a.table[:id].not_eq(root.id)) }
|
205
|
+
|
206
|
+
expect(relation).to eq [child_1, grandchild_11]
|
207
|
+
end
|
208
|
+
|
209
|
+
example 'start conditions can be changed via #start_with method' do
|
210
|
+
relation = adapter.self_and_ancestors(grandchild_11) { |x| x.start_with { |s| s.where('id != ?', grandchild_11.id) } }
|
211
|
+
|
212
|
+
expect(relation).to be_empty
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#ancestors' do
|
217
|
+
context 'when persisted record given' do
|
218
|
+
context 'when level > 0' do
|
219
|
+
subject(:relation) { adapter.ancestors(grandchild_11) }
|
220
|
+
|
221
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
222
|
+
|
223
|
+
it 'returns all node ancestors and itself starting from root' do
|
224
|
+
expect(relation.to_a).to eq Array[root, child_1]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context 'when level = 0' do
|
229
|
+
subject(:relation) { adapter.ancestors(root) }
|
230
|
+
|
231
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
232
|
+
|
233
|
+
it { expect(relation.to_a).to eq [] }
|
234
|
+
|
235
|
+
it 'does not query database' do
|
236
|
+
expect{relation.to_a}.not_to query_database
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'when new record given' do
|
242
|
+
let(:record_grandparent) { build model, attrs.merge(:parent => grandchild_11) }
|
243
|
+
let(:record_parent) { build model, attrs.merge(:parent => record_grandparent) }
|
244
|
+
let(:record) { build model, attrs.merge(:parent => record_parent) }
|
245
|
+
|
246
|
+
subject(:relation) { adapter.ancestors(record) }
|
247
|
+
|
248
|
+
it { expect(relation).to be_a ActiveRecord::Relation }
|
249
|
+
|
250
|
+
it 'returns all node ancestors and itself starting from root' do
|
251
|
+
expect(relation.to_a).to eq Array[root, child_1, grandchild_11, record_grandparent, record_parent]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe '#ancestors with traversal filters' do
|
257
|
+
it 'applies given block to recursive term' do
|
258
|
+
relation = adapter.ancestors(grandchild_11) { |a| a.where(a.table[:id].not_eq(root.id)) }
|
259
|
+
expect(relation).to eq [child_1]
|
260
|
+
|
261
|
+
relation = adapter.ancestors(grandchild_11) { |a| a.where(a.table[:id].not_eq(child_1.id)) }
|
262
|
+
expect(relation).to be_empty
|
263
|
+
end
|
264
|
+
|
265
|
+
example 'start conditions can be changed via #start_with method' do
|
266
|
+
relation = adapter.ancestors(grandchild_11) { |x| x.start_with { |s| s.where('id != ?', grandchild_11.id) } }
|
267
|
+
|
268
|
+
expect(relation).to be_empty
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActsAsOrderedTree, 'before/after add/remove callbacks', :transactional do
|
6
|
+
class Class1 < Default
|
7
|
+
cattr_accessor :triggered_callbacks
|
8
|
+
|
9
|
+
def self.triggered?(kind, *args)
|
10
|
+
if args.any?
|
11
|
+
triggered_callbacks.include?([kind, *args])
|
12
|
+
else
|
13
|
+
triggered_callbacks.any? { |c| c.first == kind }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
acts_as_ordered_tree :before_add => :before_add,
|
18
|
+
:after_add => :after_add,
|
19
|
+
:before_remove => :before_remove,
|
20
|
+
:after_remove => :after_remove
|
21
|
+
|
22
|
+
def run_callback(kind, *args)
|
23
|
+
self.class.triggered_callbacks ||= []
|
24
|
+
self.class.triggered_callbacks << [kind, self, *args]
|
25
|
+
yield if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
CALLBACKS = [:before, :after, :around].product([:add, :remove, :move, :reorder]).map { |a, b| "#{a}_#{b}".to_sym }
|
29
|
+
|
30
|
+
CALLBACKS.each do |callback|
|
31
|
+
define_method(callback) { |*args, &block| run_callback(callback, *args, &block) }
|
32
|
+
send(callback, callback) if respond_to?(callback)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
matcher :trigger_callback do |*callbacks, &block|
|
37
|
+
supports_block_expectations
|
38
|
+
|
39
|
+
match_for_should do |proc|
|
40
|
+
@with ||= nil
|
41
|
+
Class1.triggered_callbacks = []
|
42
|
+
proc.call
|
43
|
+
callbacks.all? { |callback| Class1.triggered?(callback, *@with) }
|
44
|
+
end
|
45
|
+
|
46
|
+
match_for_should_not do |proc|
|
47
|
+
@with ||= nil
|
48
|
+
Class1.triggered_callbacks = []
|
49
|
+
proc.call
|
50
|
+
callbacks.none? { |callback| Class1.triggered?(callback, *@with) }
|
51
|
+
end
|
52
|
+
|
53
|
+
chain :with do |*args, &blk|
|
54
|
+
@with = args
|
55
|
+
@block = blk
|
56
|
+
end
|
57
|
+
|
58
|
+
description do
|
59
|
+
description = "trigger callbacks #{callbacks.map(&:inspect).join(', ')}"
|
60
|
+
description << " with arguments #{@with.inspect}" if @with
|
61
|
+
description
|
62
|
+
end
|
63
|
+
|
64
|
+
failure_message_for_should do
|
65
|
+
"expected that block would #{description}"
|
66
|
+
end
|
67
|
+
|
68
|
+
failure_message_for_should_not do
|
69
|
+
desc = "expected that block would not #{description}, but following callbacks were triggered:"
|
70
|
+
Class1.triggered_callbacks.each do |kind, *args|
|
71
|
+
desc << "\n * #{kind.inspect} #{args.inspect}"
|
72
|
+
end
|
73
|
+
desc
|
74
|
+
end
|
75
|
+
end
|
76
|
+
alias_method :trigger_callbacks, :trigger_callback
|
77
|
+
|
78
|
+
# root
|
79
|
+
# child 1
|
80
|
+
# child 2
|
81
|
+
# child 3
|
82
|
+
# child 4
|
83
|
+
# child 5
|
84
|
+
let(:root) { Class1.create :name => 'root' }
|
85
|
+
let!(:child1) { Class1.create :name => 'child1', :parent => root }
|
86
|
+
let!(:child2) { Class1.create :name => 'child2', :parent => child1 }
|
87
|
+
let!(:child3) { Class1.create :name => 'child3', :parent => child1 }
|
88
|
+
let!(:child4) { Class1.create :name => 'child4', :parent => root }
|
89
|
+
let!(:child5) { Class1.create :name => 'child5', :parent => child4 }
|
90
|
+
|
91
|
+
it 'does not trigger any callbacks when tree attributes were not changed' do
|
92
|
+
expect {
|
93
|
+
child2.update_attributes :name => 'x'
|
94
|
+
}.not_to trigger_callbacks(*Class1::CALLBACKS)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'does not trigger any callbacks except :before_remove and :after_remove when node is destroyed' do
|
98
|
+
expect {
|
99
|
+
child2.destroy
|
100
|
+
}.not_to trigger_callbacks(*Class1::CALLBACKS - [:before_remove, :after_remove])
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '*_add callbacks' do
|
104
|
+
let(:new_record) { Class1.new :name => 'child6' }
|
105
|
+
|
106
|
+
it 'fires *_add callbacks when new children added to node' do
|
107
|
+
expect {
|
108
|
+
child1.children << new_record
|
109
|
+
}.to trigger_callbacks(:before_add, :after_add).with(child1, new_record)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'fires *_add callbacks when node is moved from another parent' do
|
113
|
+
expect {
|
114
|
+
child2.update_attributes!(:parent => child4)
|
115
|
+
}.to trigger_callbacks(:before_add, :after_add).with(child4, child2)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '*_remove callbacks' do
|
120
|
+
it 'fires *_remove callbacks when node is moved from another parent' do
|
121
|
+
expect {
|
122
|
+
child2.update_attributes!(:parent => child4)
|
123
|
+
}.to trigger_callbacks(:before_remove, :after_remove).with(child1, child2)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'fires *_remove callbacks when node is destroyed' do
|
127
|
+
expect {
|
128
|
+
child2.destroy
|
129
|
+
}.to trigger_callbacks(:before_remove, :after_remove).with(child1, child2)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '*_move callbacks' do
|
134
|
+
it 'fires *_move callbacks when node is moved to another parent' do
|
135
|
+
expect {
|
136
|
+
child2.update_attributes!(:parent => child4)
|
137
|
+
}.to trigger_callbacks(:before_move, :around_move, :after_move).with(child2)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'does not trigger *_move callbacks when node is not moved to another parent' do
|
141
|
+
expect {
|
142
|
+
child2.move_lower
|
143
|
+
}.not_to trigger_callbacks(:before_move, :around_move, :after_move)
|
144
|
+
|
145
|
+
expect {
|
146
|
+
root.move_to_root
|
147
|
+
}.not_to trigger_callbacks(:before_move, :around_move, :after_move)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '*_reorder callbacks' do
|
152
|
+
it 'fires *_reorder callbacks when node position is changed but parent not' do
|
153
|
+
expect {
|
154
|
+
child2.position += 1
|
155
|
+
child2.save
|
156
|
+
}.to trigger_callbacks(:before_reorder, :around_reorder, :after_reorder).with(child2)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'does not fire *_reorder callbacks when node is moved to another parent' do
|
160
|
+
expect {
|
161
|
+
child2.move_to_root
|
162
|
+
}.not_to trigger_callbacks(:before_reorder, :around_reorder, :after_reorder)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'Callbacks context' do
|
167
|
+
specify 'new parent_id should be available in before_move' do
|
168
|
+
expect(child2).to receive(:before_move) { expect(child2.parent_id).to eq child4.id }
|
169
|
+
child2.update_attributes! :parent => child4
|
170
|
+
end
|
171
|
+
|
172
|
+
specify 'new position should be available in before_reorder' do
|
173
|
+
expect(child2).to receive(:before_reorder) { expect(child2.position).to eq 2 }
|
174
|
+
child2.move_lower
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|