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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/lib/acts_as_ordered_tree.rb +22 -100
  3. data/lib/acts_as_ordered_tree/adapters.rb +17 -0
  4. data/lib/acts_as_ordered_tree/adapters/abstract.rb +23 -0
  5. data/lib/acts_as_ordered_tree/adapters/postgresql.rb +150 -0
  6. data/lib/acts_as_ordered_tree/adapters/recursive.rb +157 -0
  7. data/lib/acts_as_ordered_tree/compatibility.rb +22 -0
  8. data/lib/acts_as_ordered_tree/compatibility/active_record/association_scope.rb +9 -0
  9. data/lib/acts_as_ordered_tree/compatibility/active_record/default_scoped.rb +19 -0
  10. data/lib/acts_as_ordered_tree/compatibility/active_record/null_relation.rb +71 -0
  11. data/lib/acts_as_ordered_tree/compatibility/features.rb +153 -0
  12. data/lib/acts_as_ordered_tree/deprecate.rb +24 -0
  13. data/lib/acts_as_ordered_tree/hooks.rb +38 -0
  14. data/lib/acts_as_ordered_tree/hooks/update.rb +86 -0
  15. data/lib/acts_as_ordered_tree/instance_methods.rb +92 -453
  16. data/lib/acts_as_ordered_tree/iterators/arranger.rb +35 -0
  17. data/lib/acts_as_ordered_tree/iterators/level_calculator.rb +52 -0
  18. data/lib/acts_as_ordered_tree/iterators/orphans_pruner.rb +58 -0
  19. data/lib/acts_as_ordered_tree/node.rb +78 -0
  20. data/lib/acts_as_ordered_tree/node/attributes.rb +48 -0
  21. data/lib/acts_as_ordered_tree/node/movement.rb +62 -0
  22. data/lib/acts_as_ordered_tree/node/movements.rb +111 -0
  23. data/lib/acts_as_ordered_tree/node/predicates.rb +98 -0
  24. data/lib/acts_as_ordered_tree/node/reloading.rb +49 -0
  25. data/lib/acts_as_ordered_tree/node/siblings.rb +139 -0
  26. data/lib/acts_as_ordered_tree/node/traversals.rb +53 -0
  27. data/lib/acts_as_ordered_tree/persevering_transaction.rb +93 -0
  28. data/lib/acts_as_ordered_tree/position.rb +143 -0
  29. data/lib/acts_as_ordered_tree/relation/arrangeable.rb +33 -0
  30. data/lib/acts_as_ordered_tree/relation/iterable.rb +41 -0
  31. data/lib/acts_as_ordered_tree/relation/preloaded.rb +46 -11
  32. data/lib/acts_as_ordered_tree/transaction/base.rb +57 -0
  33. data/lib/acts_as_ordered_tree/transaction/callbacks.rb +67 -0
  34. data/lib/acts_as_ordered_tree/transaction/create.rb +68 -0
  35. data/lib/acts_as_ordered_tree/transaction/destroy.rb +34 -0
  36. data/lib/acts_as_ordered_tree/transaction/dsl.rb +214 -0
  37. data/lib/acts_as_ordered_tree/transaction/factory.rb +67 -0
  38. data/lib/acts_as_ordered_tree/transaction/move.rb +70 -0
  39. data/lib/acts_as_ordered_tree/transaction/passthrough.rb +12 -0
  40. data/lib/acts_as_ordered_tree/transaction/reorder.rb +42 -0
  41. data/lib/acts_as_ordered_tree/transaction/save.rb +64 -0
  42. data/lib/acts_as_ordered_tree/transaction/update.rb +78 -0
  43. data/lib/acts_as_ordered_tree/tree.rb +148 -0
  44. data/lib/acts_as_ordered_tree/tree/association.rb +20 -0
  45. data/lib/acts_as_ordered_tree/tree/callbacks.rb +57 -0
  46. data/lib/acts_as_ordered_tree/tree/children_association.rb +120 -0
  47. data/lib/acts_as_ordered_tree/tree/columns.rb +102 -0
  48. data/lib/acts_as_ordered_tree/tree/deprecated_columns_accessors.rb +24 -0
  49. data/lib/acts_as_ordered_tree/tree/parent_association.rb +31 -0
  50. data/lib/acts_as_ordered_tree/tree/perseverance.rb +19 -0
  51. data/lib/acts_as_ordered_tree/tree/scopes.rb +56 -0
  52. data/lib/acts_as_ordered_tree/validators.rb +1 -1
  53. data/lib/acts_as_ordered_tree/version.rb +1 -1
  54. data/spec/acts_as_ordered_tree_spec.rb +80 -909
  55. data/spec/adapters/postgresql_spec.rb +14 -0
  56. data/spec/adapters/recursive_spec.rb +12 -0
  57. data/spec/adapters/shared.rb +272 -0
  58. data/spec/callbacks_spec.rb +177 -0
  59. data/spec/counter_cache_spec.rb +31 -0
  60. data/spec/create_spec.rb +110 -0
  61. data/spec/destroy_spec.rb +57 -0
  62. data/spec/inheritance_spec.rb +176 -0
  63. data/spec/move_spec.rb +94 -0
  64. data/spec/node/movements/concurrent_movements_spec.rb +354 -0
  65. data/spec/node/movements/move_higher_spec.rb +46 -0
  66. data/spec/node/movements/move_lower_spec.rb +46 -0
  67. data/spec/node/movements/move_to_child_of_spec.rb +147 -0
  68. data/spec/node/movements/move_to_child_with_index_spec.rb +124 -0
  69. data/spec/node/movements/move_to_child_with_position_spec.rb +85 -0
  70. data/spec/node/movements/move_to_left_of_spec.rb +120 -0
  71. data/spec/node/movements/move_to_right_of_spec.rb +120 -0
  72. data/spec/node/movements/move_to_root_spec.rb +67 -0
  73. data/spec/node/predicates_spec.rb +211 -0
  74. data/spec/node/reloading_spec.rb +42 -0
  75. data/spec/node/siblings_spec.rb +193 -0
  76. data/spec/node/traversals_spec.rb +71 -0
  77. data/spec/persevering_transaction_spec.rb +98 -0
  78. data/spec/relation/arrangeable_spec.rb +88 -0
  79. data/spec/relation/iterable_spec.rb +104 -0
  80. data/spec/relation/preloaded_spec.rb +57 -0
  81. data/spec/reorder_spec.rb +83 -0
  82. data/spec/spec_helper.rb +30 -38
  83. data/spec/support/db/boot.rb +22 -0
  84. data/spec/{db → support/db}/config.travis.yml +2 -0
  85. data/spec/{db → support/db}/config.yml +1 -0
  86. data/spec/{db → support/db}/schema.rb +9 -0
  87. data/spec/support/factories.rb +2 -2
  88. data/spec/support/matchers.rb +67 -58
  89. data/spec/support/models.rb +6 -14
  90. data/spec/support/tree_factory.rb +315 -0
  91. data/spec/tree/children_association_spec.rb +72 -0
  92. data/spec/tree/columns_spec.rb +65 -0
  93. data/spec/tree/scopes_spec.rb +39 -0
  94. metadata +161 -43
  95. data/lib/acts_as_ordered_tree/adapters/postgresql_adapter.rb +0 -104
  96. data/lib/acts_as_ordered_tree/arrangeable.rb +0 -80
  97. data/lib/acts_as_ordered_tree/class_methods.rb +0 -72
  98. data/lib/acts_as_ordered_tree/relation/base.rb +0 -26
  99. data/lib/acts_as_ordered_tree/tenacious_transaction.rb +0 -30
  100. 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