be9-awesome_nested_set 1.4.3

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.
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ module CollectiveIdea #:nodoc:
3
+ module Acts #:nodoc:
4
+ module NestedSet #:nodoc:
5
+ module Depth
6
+ # Model scope conditions
7
+ def scope_condition(table_name=nil)
8
+ table_name ||= self.class.quoted_table_name
9
+
10
+ scope_string = Array(acts_as_nested_set_options[:scope]).map do |c|
11
+ "#{table_name}.#{connection.quote_column_name(c)} = #{self.send(c)}"
12
+ end.join(" AND ")
13
+
14
+ scope_string.blank? ? "1 = 1" : scope_string
15
+ end
16
+
17
+ # Check is model has depth column
18
+ def depth?
19
+ self.respond_to?(:depth)
20
+ end
21
+
22
+ # Update cached_level attribute
23
+ def update_depth
24
+ self.update_attribute(:depth, level)
25
+ end
26
+
27
+ # Update cached_level attribute for all record tree
28
+ def update_all_depth
29
+ if depth?
30
+ self.class.connection.execute("UPDATE #{self.class.quoted_table_name} a SET a.depth = \
31
+ (SELECT count(*) - 1 FROM (SELECT * FROM #{self.class.quoted_table_name} WHERE #{scope_condition}) AS b \
32
+ WHERE #{scope_condition('a')} AND \
33
+ (a.#{quoted_left_column_name} BETWEEN b.#{quoted_left_column_name} AND b.#{quoted_right_column_name}))
34
+ WHERE #{scope_condition('a')}
35
+ ")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module CollectiveIdea #:nodoc:
3
+ module Acts #:nodoc:
4
+ module NestedSet #:nodoc:
5
+ module Descendants
6
+ # Returns the number of nested children of this object.
7
+ def descendants_count
8
+ return (right - left - 1)/2
9
+ end
10
+
11
+ def has_descendants?
12
+ !descendants_count.zero?
13
+ end
14
+
15
+ def move_by_direction(ditection)
16
+ return if ditection.blank?
17
+
18
+ case ditection.to_sym
19
+ when :up, :left then move_left
20
+ when :down, :right then move_right
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ module CollectiveIdea #:nodoc:
3
+ module Acts #:nodoc:
4
+ module NestedSet #:nodoc:
5
+ # This module provides some helpers for the model classes using acts_as_nested_set.
6
+ # It is included by default in all views.
7
+ #
8
+ module Helper
9
+ # Returns options for select.
10
+ # You can exclude some items from the tree.
11
+ # You can pass a block receiving an item and returning the string displayed in the select.
12
+ #
13
+ # == Params
14
+ # * +class_or_item+ - Class name or top level times
15
+ # * +mover+ - The item that is being move, used to exlude impossible moves
16
+ # * +&block+ - a block that will be used to display: { |item| ... item.name }
17
+ #
18
+ # == Usage
19
+ #
20
+ # <%= f.select :parent_id, nested_set_options(Category, @category) {|i|
21
+ # "#{'–' * i.level} #{i.name}"
22
+ # }) %>
23
+ #
24
+ def nested_set_options(class_or_item, mover = nil)
25
+ class_or_item = class_or_item.roots if class_or_item.is_a?(Class)
26
+ items = Array(class_or_item)
27
+ result = []
28
+ items.each do |root|
29
+ result += root.self_and_descendants.map do |i|
30
+ if mover.nil? || mover.new_record? || mover.move_possible?(i)
31
+ [yield(i), i.id]
32
+ end
33
+ end.compact
34
+ end
35
+ result
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require 'awesome_nested_set'
3
+ require 'rails'
4
+
5
+ module CollectiveIdea
6
+ module Acts
7
+ module NestedSet
8
+ class Railtie < ::Rails::Railtie
9
+ initializer "awesome_nested_set.on_rails_init" do
10
+ ActiveSupport.on_load :active_record do
11
+ CollectiveIdea::Acts::NestedSet::Railtie.extend_active_record
12
+ end
13
+
14
+ ActiveSupport.on_load :action_view do
15
+ ActionView::Base.send(:include, CollectiveIdea::Acts::NestedSet::Helper)
16
+ end
17
+ end
18
+
19
+ def self.extend_active_record
20
+ ActiveRecord::Base.send :include,
21
+ CollectiveIdea::Acts::NestedSet::Base
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ # This file is here to satisfy test_help from Rails < 2.3
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+
3
+ module CollectiveIdea
4
+ module Acts #:nodoc:
5
+ module NestedSet #:nodoc:
6
+ class AwesomeNestedSetTest < ActiveSupport::TestCase
7
+ include Helper
8
+ fixtures :categories
9
+
10
+ def test_nested_set_options
11
+ expected = [
12
+ [" Top Level", 1],
13
+ ["- Child 1", 2],
14
+ ['- Child 2', 3],
15
+ ['-- Child 2.1', 4],
16
+ ['- Child 3', 5],
17
+ [" Top Level 2", 6]
18
+ ]
19
+ actual = nested_set_options(Category) do |c|
20
+ "#{'-' * c.level} #{c.name}"
21
+ end
22
+ assert_equal expected, actual
23
+ end
24
+
25
+ def test_nested_set_options_with_mover
26
+ expected = [
27
+ [" Top Level", 1],
28
+ ["- Child 1", 2],
29
+ ['- Child 3', 5],
30
+ [" Top Level 2", 6]
31
+ ]
32
+ actual = nested_set_options(Category, categories(:child_2)) do |c|
33
+ "#{'-' * c.level} #{c.name}"
34
+ end
35
+ assert_equal expected, actual
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,804 @@
1
+ require 'test_helper'
2
+
3
+ class Note < ActiveRecord::Base
4
+ acts_as_nested_set :scope => [:notable_id, :notable_type]
5
+ end
6
+
7
+ class Default < ActiveRecord::Base
8
+ acts_as_nested_set
9
+ set_table_name 'categories'
10
+ end
11
+
12
+ class ScopedCategory < ActiveRecord::Base
13
+ acts_as_nested_set :scope => :organization
14
+ set_table_name 'categories'
15
+ end
16
+
17
+ class RenamedColumns < ActiveRecord::Base
18
+ acts_as_nested_set :parent_column => 'mother_id', :left_column => 'red', :right_column => 'black'
19
+ end
20
+
21
+ class AwesomeNestedSetTest < ActiveSupport::TestCase
22
+
23
+ def test_left_column_default
24
+ assert_equal 'lft', Default.acts_as_nested_set_options[:left_column]
25
+ end
26
+
27
+ def test_right_column_default
28
+ assert_equal 'rgt', Default.acts_as_nested_set_options[:right_column]
29
+ end
30
+
31
+ def test_parent_column_default
32
+ assert_equal 'parent_id', Default.acts_as_nested_set_options[:parent_column]
33
+ end
34
+
35
+ def test_scope_default
36
+ assert_nil Default.acts_as_nested_set_options[:scope]
37
+ end
38
+
39
+ def test_left_column_name
40
+ assert_equal 'lft', Default.left_column_name
41
+ assert_equal 'lft', Default.new.left_column_name
42
+ assert_equal 'red', RenamedColumns.left_column_name
43
+ assert_equal 'red', RenamedColumns.new.left_column_name
44
+ end
45
+
46
+ def test_right_column_name
47
+ assert_equal 'rgt', Default.right_column_name
48
+ assert_equal 'rgt', Default.new.right_column_name
49
+ assert_equal 'black', RenamedColumns.right_column_name
50
+ assert_equal 'black', RenamedColumns.new.right_column_name
51
+ end
52
+
53
+ def test_parent_column_name
54
+ assert_equal 'parent_id', Default.parent_column_name
55
+ assert_equal 'parent_id', Default.new.parent_column_name
56
+ assert_equal 'mother_id', RenamedColumns.parent_column_name
57
+ assert_equal 'mother_id', RenamedColumns.new.parent_column_name
58
+ end
59
+
60
+ def test_creation_with_altered_column_names
61
+ assert_nothing_raised do
62
+ RenamedColumns.create!()
63
+ end
64
+ end
65
+
66
+ def test_quoted_left_column_name
67
+ quoted = Default.connection.quote_column_name('lft')
68
+ assert_equal quoted, Default.quoted_left_column_name
69
+ assert_equal quoted, Default.new.quoted_left_column_name
70
+ end
71
+
72
+ def test_quoted_right_column_name
73
+ quoted = Default.connection.quote_column_name('rgt')
74
+ assert_equal quoted, Default.quoted_right_column_name
75
+ assert_equal quoted, Default.new.quoted_right_column_name
76
+ end
77
+
78
+ def test_left_column_protected_from_assignment
79
+ assert_raises(ActiveRecord::ActiveRecordError) { Category.new.lft = 1 }
80
+ end
81
+
82
+ def test_right_column_protected_from_assignment
83
+ assert_raises(ActiveRecord::ActiveRecordError) { Category.new.rgt = 1 }
84
+ end
85
+
86
+ def test_colums_protected_on_initialize
87
+ c = Category.new(:lft => 1, :rgt => 2)
88
+ assert_nil c.lft
89
+ assert_nil c.rgt
90
+ end
91
+
92
+ def test_scoped_appends_id
93
+ assert_equal :organization_id, ScopedCategory.acts_as_nested_set_options[:scope]
94
+ end
95
+
96
+ def test_roots_class_method
97
+ assert_equal Category.find_all_by_parent_id(nil), Category.roots
98
+ end
99
+
100
+ def test_root_class_method
101
+ assert_equal categories(:top_level), Category.root
102
+ end
103
+
104
+ def test_root
105
+ assert_equal categories(:top_level), categories(:child_3).root
106
+ end
107
+
108
+ def test_root?
109
+ assert categories(:top_level).root?
110
+ assert categories(:top_level_2).root?
111
+ end
112
+
113
+ def test_leaves_class_method
114
+ assert_equal Category.find(:all, :conditions => "#{Category.right_column_name} - #{Category.left_column_name} = 1"), Category.leaves
115
+ assert_equal Category.leaves.count, 4
116
+ assert (Category.leaves.include? categories(:child_1))
117
+ assert (Category.leaves.include? categories(:child_2_1))
118
+ assert (Category.leaves.include? categories(:child_3))
119
+ assert (Category.leaves.include? categories(:top_level_2))
120
+ end
121
+
122
+ def test_leaf
123
+ assert categories(:child_1).leaf?
124
+ assert categories(:child_2_1).leaf?
125
+ assert categories(:child_3).leaf?
126
+ assert categories(:top_level_2).leaf?
127
+
128
+ assert !categories(:top_level).leaf?
129
+ assert !categories(:child_2).leaf?
130
+ assert !Category.new.leaf?
131
+ end
132
+
133
+
134
+ def test_parent
135
+ assert_equal categories(:child_2), categories(:child_2_1).parent
136
+ end
137
+
138
+ def test_self_and_ancestors
139
+ child = categories(:child_2_1)
140
+ self_and_ancestors = [categories(:top_level), categories(:child_2), child]
141
+ assert_equal self_and_ancestors, child.self_and_ancestors
142
+ end
143
+
144
+ def test_ancestors
145
+ child = categories(:child_2_1)
146
+ ancestors = [categories(:top_level), categories(:child_2)]
147
+ assert_equal ancestors, child.ancestors
148
+ end
149
+
150
+ def test_self_and_siblings
151
+ child = categories(:child_2)
152
+ self_and_siblings = [categories(:child_1), child, categories(:child_3)]
153
+ assert_equal self_and_siblings, child.self_and_siblings
154
+ assert_nothing_raised do
155
+ tops = [categories(:top_level), categories(:top_level_2)]
156
+ assert_equal tops, categories(:top_level).self_and_siblings
157
+ end
158
+ end
159
+
160
+ def test_siblings
161
+ child = categories(:child_2)
162
+ siblings = [categories(:child_1), categories(:child_3)]
163
+ assert_equal siblings, child.siblings
164
+ end
165
+
166
+ def test_leaves
167
+ leaves = [categories(:child_1), categories(:child_2_1), categories(:child_3), categories(:top_level_2)]
168
+ assert categories(:top_level).leaves, leaves
169
+ end
170
+
171
+ def test_level
172
+ assert_equal 0, categories(:top_level).level
173
+ assert_equal 1, categories(:child_1).level
174
+ assert_equal 2, categories(:child_2_1).level
175
+ end
176
+
177
+ def test_has_children?
178
+ assert categories(:child_2_1).children.empty?
179
+ assert !categories(:child_2).children.empty?
180
+ assert !categories(:top_level).children.empty?
181
+ end
182
+
183
+ def test_self_and_descendents
184
+ parent = categories(:top_level)
185
+ self_and_descendants = [parent, categories(:child_1), categories(:child_2),
186
+ categories(:child_2_1), categories(:child_3)]
187
+ assert_equal self_and_descendants, parent.self_and_descendants
188
+ assert_equal self_and_descendants, parent.self_and_descendants.count
189
+ end
190
+
191
+ def test_descendents
192
+ lawyers = Category.create!(:name => "lawyers")
193
+ us = Category.create!(:name => "United States")
194
+ us.move_to_child_of(lawyers)
195
+ patent = Category.create!(:name => "Patent Law")
196
+ patent.move_to_child_of(us)
197
+ lawyers.reload
198
+
199
+ assert_equal 1, lawyers.children.size
200
+ assert_equal 1, us.children.size
201
+ assert_equal 2, lawyers.descendants.size
202
+ end
203
+
204
+ def test_self_and_descendents
205
+ parent = categories(:top_level)
206
+ descendants = [categories(:child_1), categories(:child_2),
207
+ categories(:child_2_1), categories(:child_3)]
208
+ assert_equal descendants, parent.descendants
209
+ end
210
+
211
+ def test_children
212
+ category = categories(:top_level)
213
+ category.children.each {|c| assert_equal category.id, c.parent_id }
214
+ end
215
+
216
+ def test_order_of_children
217
+ categories(:child_2).move_left
218
+ assert_equal categories(:child_2), categories(:top_level).children[0]
219
+ assert_equal categories(:child_1), categories(:top_level).children[1]
220
+ assert_equal categories(:child_3), categories(:top_level).children[2]
221
+ end
222
+
223
+ def test_is_or_is_ancestor_of?
224
+ assert categories(:top_level).is_or_is_ancestor_of?(categories(:child_1))
225
+ assert categories(:top_level).is_or_is_ancestor_of?(categories(:child_2_1))
226
+ assert categories(:child_2).is_or_is_ancestor_of?(categories(:child_2_1))
227
+ assert !categories(:child_2_1).is_or_is_ancestor_of?(categories(:child_2))
228
+ assert !categories(:child_1).is_or_is_ancestor_of?(categories(:child_2))
229
+ assert categories(:child_1).is_or_is_ancestor_of?(categories(:child_1))
230
+ end
231
+
232
+ def test_is_ancestor_of?
233
+ assert categories(:top_level).is_ancestor_of?(categories(:child_1))
234
+ assert categories(:top_level).is_ancestor_of?(categories(:child_2_1))
235
+ assert categories(:child_2).is_ancestor_of?(categories(:child_2_1))
236
+ assert !categories(:child_2_1).is_ancestor_of?(categories(:child_2))
237
+ assert !categories(:child_1).is_ancestor_of?(categories(:child_2))
238
+ assert !categories(:child_1).is_ancestor_of?(categories(:child_1))
239
+ end
240
+
241
+ def test_is_or_is_ancestor_of_with_scope
242
+ root = ScopedCategory.root
243
+ child = root.children.first
244
+ assert root.is_or_is_ancestor_of?(child)
245
+ child.update_attribute :organization_id, 'different'
246
+ assert !root.is_or_is_ancestor_of?(child)
247
+ end
248
+
249
+ def test_is_or_is_descendant_of?
250
+ assert categories(:child_1).is_or_is_descendant_of?(categories(:top_level))
251
+ assert categories(:child_2_1).is_or_is_descendant_of?(categories(:top_level))
252
+ assert categories(:child_2_1).is_or_is_descendant_of?(categories(:child_2))
253
+ assert !categories(:child_2).is_or_is_descendant_of?(categories(:child_2_1))
254
+ assert !categories(:child_2).is_or_is_descendant_of?(categories(:child_1))
255
+ assert categories(:child_1).is_or_is_descendant_of?(categories(:child_1))
256
+ end
257
+
258
+ def test_is_descendant_of?
259
+ assert categories(:child_1).is_descendant_of?(categories(:top_level))
260
+ assert categories(:child_2_1).is_descendant_of?(categories(:top_level))
261
+ assert categories(:child_2_1).is_descendant_of?(categories(:child_2))
262
+ assert !categories(:child_2).is_descendant_of?(categories(:child_2_1))
263
+ assert !categories(:child_2).is_descendant_of?(categories(:child_1))
264
+ assert !categories(:child_1).is_descendant_of?(categories(:child_1))
265
+ end
266
+
267
+ def test_is_or_is_descendant_of_with_scope
268
+ root = ScopedCategory.root
269
+ child = root.children.first
270
+ assert child.is_or_is_descendant_of?(root)
271
+ child.update_attribute :organization_id, 'different'
272
+ assert !child.is_or_is_descendant_of?(root)
273
+ end
274
+
275
+ def test_same_scope?
276
+ root = ScopedCategory.root
277
+ child = root.children.first
278
+ assert child.same_scope?(root)
279
+ child.update_attribute :organization_id, 'different'
280
+ assert !child.same_scope?(root)
281
+ end
282
+
283
+ def test_left_sibling
284
+ assert_equal categories(:child_1), categories(:child_2).left_sibling
285
+ assert_equal categories(:child_2), categories(:child_3).left_sibling
286
+ end
287
+
288
+ def test_left_sibling_of_root
289
+ assert_nil categories(:top_level).left_sibling
290
+ end
291
+
292
+ def test_left_sibling_without_siblings
293
+ assert_nil categories(:child_2_1).left_sibling
294
+ end
295
+
296
+ def test_left_sibling_of_leftmost_node
297
+ assert_nil categories(:child_1).left_sibling
298
+ end
299
+
300
+ def test_right_sibling
301
+ assert_equal categories(:child_3), categories(:child_2).right_sibling
302
+ assert_equal categories(:child_2), categories(:child_1).right_sibling
303
+ end
304
+
305
+ def test_right_sibling_of_root
306
+ assert_equal categories(:top_level_2), categories(:top_level).right_sibling
307
+ assert_nil categories(:top_level_2).right_sibling
308
+ end
309
+
310
+ def test_right_sibling_without_siblings
311
+ assert_nil categories(:child_2_1).right_sibling
312
+ end
313
+
314
+ def test_right_sibling_of_rightmost_node
315
+ assert_nil categories(:child_3).right_sibling
316
+ end
317
+
318
+ def test_move_left
319
+ categories(:child_2).move_left
320
+ assert_nil categories(:child_2).left_sibling
321
+ assert_equal categories(:child_1), categories(:child_2).right_sibling
322
+ assert Category.valid?
323
+ end
324
+
325
+ def test_move_right
326
+ categories(:child_2).move_right
327
+ assert_nil categories(:child_2).right_sibling
328
+ assert_equal categories(:child_3), categories(:child_2).left_sibling
329
+ assert Category.valid?
330
+ end
331
+
332
+ def test_move_to_left_of
333
+ categories(:child_3).move_to_left_of(categories(:child_1))
334
+ assert_nil categories(:child_3).left_sibling
335
+ assert_equal categories(:child_1), categories(:child_3).right_sibling
336
+ assert Category.valid?
337
+ end
338
+
339
+ def test_move_to_right_of
340
+ categories(:child_1).move_to_right_of(categories(:child_3))
341
+ assert_nil categories(:child_1).right_sibling
342
+ assert_equal categories(:child_3), categories(:child_1).left_sibling
343
+ assert Category.valid?
344
+ end
345
+
346
+ def test_move_to_root
347
+ categories(:child_2).move_to_root
348
+ assert_nil categories(:child_2).parent
349
+ assert_equal 0, categories(:child_2).level
350
+ assert_equal 1, categories(:child_2_1).level
351
+ assert_equal 1, categories(:child_2).left
352
+ assert_equal 4, categories(:child_2).right
353
+ assert Category.valid?
354
+ end
355
+
356
+ def test_move_to_child_of
357
+ categories(:child_1).move_to_child_of(categories(:child_3))
358
+ assert_equal categories(:child_3).id, categories(:child_1).parent_id
359
+ assert Category.valid?
360
+ end
361
+
362
+ def test_move_to_child_of_appends_to_end
363
+ child = Category.create! :name => 'New Child'
364
+ child.move_to_child_of categories(:top_level)
365
+ assert_equal child, categories(:top_level).children.last
366
+ end
367
+
368
+ def test_subtree_move_to_child_of
369
+ assert_equal 4, categories(:child_2).left
370
+ assert_equal 7, categories(:child_2).right
371
+
372
+ assert_equal 2, categories(:child_1).left
373
+ assert_equal 3, categories(:child_1).right
374
+
375
+ categories(:child_2).move_to_child_of(categories(:child_1))
376
+ assert Category.valid?
377
+ assert_equal categories(:child_1).id, categories(:child_2).parent_id
378
+
379
+ assert_equal 3, categories(:child_2).left
380
+ assert_equal 6, categories(:child_2).right
381
+ assert_equal 2, categories(:child_1).left
382
+ assert_equal 7, categories(:child_1).right
383
+ end
384
+
385
+ def test_slightly_difficult_move_to_child_of
386
+ assert_equal 11, categories(:top_level_2).left
387
+ assert_equal 12, categories(:top_level_2).right
388
+
389
+ # create a new top-level node and move single-node top-level tree inside it.
390
+ new_top = Category.create(:name => 'New Top')
391
+ assert_equal 13, new_top.left
392
+ assert_equal 14, new_top.right
393
+
394
+ categories(:top_level_2).move_to_child_of(new_top)
395
+
396
+ assert Category.valid?
397
+ assert_equal new_top.id, categories(:top_level_2).parent_id
398
+
399
+ assert_equal 12, categories(:top_level_2).left
400
+ assert_equal 13, categories(:top_level_2).right
401
+ assert_equal 11, new_top.left
402
+ assert_equal 14, new_top.right
403
+ end
404
+
405
+ def test_difficult_move_to_child_of
406
+ assert_equal 1, categories(:top_level).left
407
+ assert_equal 10, categories(:top_level).right
408
+ assert_equal 5, categories(:child_2_1).left
409
+ assert_equal 6, categories(:child_2_1).right
410
+
411
+ # create a new top-level node and move an entire top-level tree inside it.
412
+ new_top = Category.create(:name => 'New Top')
413
+ categories(:top_level).move_to_child_of(new_top)
414
+ categories(:child_2_1).reload
415
+ assert Category.valid?
416
+ assert_equal new_top.id, categories(:top_level).parent_id
417
+
418
+ assert_equal 4, categories(:top_level).left
419
+ assert_equal 13, categories(:top_level).right
420
+ assert_equal 8, categories(:child_2_1).left
421
+ assert_equal 9, categories(:child_2_1).right
422
+ end
423
+
424
+ #rebuild swaps the position of the 2 children when added using move_to_child twice onto same parent
425
+ def test_move_to_child_more_than_once_per_parent_rebuild
426
+ root1 = Category.create(:name => 'Root1')
427
+ root2 = Category.create(:name => 'Root2')
428
+ root3 = Category.create(:name => 'Root3')
429
+
430
+ root2.move_to_child_of root1
431
+ root3.move_to_child_of root1
432
+
433
+ output = Category.roots.last.to_text
434
+ Category.update_all('lft = null, rgt = null')
435
+ Category.rebuild!
436
+
437
+ assert_equal Category.roots.last.to_text, output
438
+ end
439
+
440
+ # doing move_to_child twice onto same parent from the furthest right first
441
+ def test_move_to_child_more_than_once_per_parent_outside_in
442
+ node1 = Category.create(:name => 'Node-1')
443
+ node2 = Category.create(:name => 'Node-2')
444
+ node3 = Category.create(:name => 'Node-3')
445
+
446
+ node2.move_to_child_of node1
447
+ node3.move_to_child_of node1
448
+
449
+ output = Category.roots.last.to_text
450
+ Category.update_all('lft = null, rgt = null')
451
+ Category.rebuild!
452
+
453
+ assert_equal Category.roots.last.to_text, output
454
+ end
455
+
456
+
457
+ def test_valid_with_null_lefts
458
+ assert Category.valid?
459
+ Category.update_all('lft = null')
460
+ assert !Category.valid?
461
+ end
462
+
463
+ def test_valid_with_null_rights
464
+ assert Category.valid?
465
+ Category.update_all('rgt = null')
466
+ assert !Category.valid?
467
+ end
468
+
469
+ def test_valid_with_missing_intermediate_node
470
+ # Even though child_2_1 will still exist, it is a sign of a sloppy delete, not an invalid tree.
471
+ assert Category.valid?
472
+ Category.delete(categories(:child_2).id)
473
+ assert Category.valid?
474
+ end
475
+
476
+ def test_valid_with_overlapping_and_rights
477
+ assert Category.valid?
478
+ categories(:top_level_2)['lft'] = 0
479
+ categories(:top_level_2).save
480
+ assert !Category.valid?
481
+ end
482
+
483
+ def test_rebuild
484
+ assert Category.valid?
485
+ before_text = Category.root.to_text
486
+ Category.update_all('lft = null, rgt = null')
487
+ Category.rebuild!
488
+ assert Category.valid?
489
+ assert_equal before_text, Category.root.to_text
490
+ end
491
+
492
+ def test_move_possible_for_sibling
493
+ assert categories(:child_2).move_possible?(categories(:child_1))
494
+ end
495
+
496
+ def test_move_not_possible_to_self
497
+ assert !categories(:top_level).move_possible?(categories(:top_level))
498
+ end
499
+
500
+ def test_move_not_possible_to_parent
501
+ categories(:top_level).descendants.each do |descendant|
502
+ assert !categories(:top_level).move_possible?(descendant)
503
+ assert descendant.move_possible?(categories(:top_level))
504
+ end
505
+ end
506
+
507
+ def test_is_or_is_ancestor_of?
508
+ [:child_1, :child_2, :child_2_1, :child_3].each do |c|
509
+ assert categories(:top_level).is_or_is_ancestor_of?(categories(c))
510
+ end
511
+ assert !categories(:top_level).is_or_is_ancestor_of?(categories(:top_level_2))
512
+ end
513
+
514
+ def test_left_and_rights_valid_with_blank_left
515
+ assert Category.left_and_rights_valid?
516
+ categories(:child_2)[:lft] = nil
517
+ categories(:child_2).save(:validate => false)
518
+ assert !Category.left_and_rights_valid?
519
+ end
520
+
521
+ def test_left_and_rights_valid_with_blank_right
522
+ assert Category.left_and_rights_valid?
523
+ categories(:child_2)[:rgt] = nil
524
+ categories(:child_2).save(:validate => false)
525
+ assert !Category.left_and_rights_valid?
526
+ end
527
+
528
+ def test_left_and_rights_valid_with_equal
529
+ assert Category.left_and_rights_valid?
530
+ categories(:top_level_2)[:lft] = categories(:top_level_2)[:rgt]
531
+ categories(:top_level_2).save(:validate => false)
532
+ assert !Category.left_and_rights_valid?
533
+ end
534
+
535
+ def test_left_and_rights_valid_with_left_equal_to_parent
536
+ assert Category.left_and_rights_valid?
537
+ categories(:child_2)[:lft] = categories(:top_level)[:lft]
538
+ categories(:child_2).save(:validate => false)
539
+ assert !Category.left_and_rights_valid?
540
+ end
541
+
542
+ def test_left_and_rights_valid_with_right_equal_to_parent
543
+ assert Category.left_and_rights_valid?
544
+ categories(:child_2)[:rgt] = categories(:top_level)[:rgt]
545
+ categories(:child_2).save(:validate => false)
546
+ assert !Category.left_and_rights_valid?
547
+ end
548
+
549
+ def test_moving_dirty_objects_doesnt_invalidate_tree
550
+ r1 = Category.create
551
+ r2 = Category.create
552
+ r3 = Category.create
553
+ r4 = Category.create
554
+ nodes = [r1, r2, r3, r4]
555
+
556
+ r2.move_to_child_of(r1)
557
+ assert Category.valid?
558
+
559
+ r3.move_to_child_of(r1)
560
+ assert Category.valid?
561
+
562
+ r4.move_to_child_of(r2)
563
+ assert Category.valid?
564
+ end
565
+
566
+ def test_multi_scoped_no_duplicates_for_columns?
567
+ assert_nothing_raised do
568
+ Note.no_duplicates_for_columns?
569
+ end
570
+ end
571
+
572
+ def test_multi_scoped_all_roots_valid?
573
+ assert_nothing_raised do
574
+ Note.all_roots_valid?
575
+ end
576
+ end
577
+
578
+ def test_multi_scoped
579
+ note1 = Note.create!(:body => "A", :notable_id => 2, :notable_type => 'Category')
580
+ note2 = Note.create!(:body => "B", :notable_id => 2, :notable_type => 'Category')
581
+ note3 = Note.create!(:body => "C", :notable_id => 2, :notable_type => 'Default')
582
+
583
+ assert_equal [note1, note2], note1.self_and_siblings
584
+ assert_equal [note3], note3.self_and_siblings
585
+ end
586
+
587
+ def test_multi_scoped_rebuild
588
+ root = Note.create!(:body => "A", :notable_id => 3, :notable_type => 'Category')
589
+ child1 = Note.create!(:body => "B", :notable_id => 3, :notable_type => 'Category')
590
+ child2 = Note.create!(:body => "C", :notable_id => 3, :notable_type => 'Category')
591
+
592
+ child1.move_to_child_of root
593
+ child2.move_to_child_of root
594
+
595
+ Note.update_all('lft = null, rgt = null')
596
+ Note.rebuild!
597
+
598
+ assert_equal Note.roots.find_by_body('A'), root
599
+ assert_equal [child1, child2], Note.roots.find_by_body('A').children
600
+ end
601
+
602
+ def test_same_scope_with_multi_scopes
603
+ assert_nothing_raised do
604
+ notes(:scope1).same_scope?(notes(:child_1))
605
+ end
606
+ assert notes(:scope1).same_scope?(notes(:child_1))
607
+ assert notes(:child_1).same_scope?(notes(:scope1))
608
+ assert !notes(:scope1).same_scope?(notes(:scope2))
609
+ end
610
+
611
+ def test_quoting_of_multi_scope_column_names
612
+ assert_equal ["\"notable_id\"", "\"notable_type\""], Note.quoted_scope_column_names
613
+ end
614
+
615
+ def test_equal_in_same_scope
616
+ assert_equal notes(:scope1), notes(:scope1)
617
+ assert_not_equal notes(:scope1), notes(:child_1)
618
+ end
619
+
620
+ def test_equal_in_different_scopes
621
+ assert_not_equal notes(:scope1), notes(:scope2)
622
+ end
623
+
624
+ def test_delete_does_not_invalidate
625
+ Category.acts_as_nested_set_options[:dependent] = :delete
626
+ categories(:child_2).destroy
627
+ assert Category.valid?
628
+ end
629
+
630
+ def test_destroy_does_not_invalidate
631
+ Category.acts_as_nested_set_options[:dependent] = :destroy
632
+ categories(:child_2).destroy
633
+ assert Category.valid?
634
+ end
635
+
636
+ def test_destroy_multiple_times_does_not_invalidate
637
+ Category.acts_as_nested_set_options[:dependent] = :destroy
638
+ categories(:child_2).destroy
639
+ categories(:child_2).destroy
640
+ assert Category.valid?
641
+ end
642
+
643
+ def test_assigning_parent_id_on_create
644
+ category = Category.create!(:name => "Child", :parent_id => categories(:child_2).id)
645
+ assert_equal categories(:child_2), category.parent
646
+ assert_equal categories(:child_2).id, category.parent_id
647
+ assert_not_nil category.left
648
+ assert_not_nil category.right
649
+ assert Category.valid?
650
+ end
651
+
652
+ def test_assigning_parent_on_create
653
+ category = Category.create!(:name => "Child", :parent => categories(:child_2))
654
+ assert_equal categories(:child_2), category.parent
655
+ assert_equal categories(:child_2).id, category.parent_id
656
+ assert_not_nil category.left
657
+ assert_not_nil category.right
658
+ assert Category.valid?
659
+ end
660
+
661
+ def test_assigning_parent_id_to_nil_on_create
662
+ category = Category.create!(:name => "New Root", :parent_id => nil)
663
+ assert_nil category.parent
664
+ assert_nil category.parent_id
665
+ assert_not_nil category.left
666
+ assert_not_nil category.right
667
+ assert Category.valid?
668
+ end
669
+
670
+ def test_assigning_parent_id_on_update
671
+ category = categories(:child_2_1)
672
+ category.parent_id = categories(:child_3).id
673
+ category.save
674
+ assert_equal categories(:child_3), category.parent
675
+ assert_equal categories(:child_3).id, category.parent_id
676
+ assert Category.valid?
677
+ end
678
+
679
+ def test_assigning_parent_on_update
680
+ category = categories(:child_2_1)
681
+ category.parent = categories(:child_3)
682
+ category.save
683
+ assert_equal categories(:child_3), category.parent
684
+ assert_equal categories(:child_3).id, category.parent_id
685
+ assert Category.valid?
686
+ end
687
+
688
+ def test_assigning_parent_id_to_nil_on_update
689
+ category = categories(:child_2_1)
690
+ category.parent_id = nil
691
+ category.save
692
+ assert_nil category.parent
693
+ assert_nil category.parent_id
694
+ assert Category.valid?
695
+ end
696
+
697
+ def test_creating_child_from_parent
698
+ category = categories(:child_2).children.create!(:name => "Child")
699
+ assert_equal categories(:child_2), category.parent
700
+ assert_equal categories(:child_2).id, category.parent_id
701
+ assert_not_nil category.left
702
+ assert_not_nil category.right
703
+ assert Category.valid?
704
+ end
705
+
706
+ def check_structure(entries, structure)
707
+ structure = structure.dup
708
+ Category.each_with_level(entries) do |category, level|
709
+ expected_level, expected_name = structure.shift
710
+ assert_equal expected_name, category.name, "wrong category"
711
+ assert_equal expected_level, level, "wrong level for #{category.name}"
712
+ end
713
+ end
714
+
715
+ def test_each_with_level
716
+ levels = [
717
+ [0, "Top Level"],
718
+ [1, "Child 1"],
719
+ [1, "Child 2"],
720
+ [2, "Child 2.1"],
721
+ [1, "Child 3" ]]
722
+
723
+ check_structure(Category.root.self_and_descendants, levels)
724
+
725
+ # test some deeper structures
726
+ category = Category.find_by_name("Child 1")
727
+ c1 = Category.new(:name => "Child 1.1")
728
+ c2 = Category.new(:name => "Child 1.1.1")
729
+ c3 = Category.new(:name => "Child 1.1.1.1")
730
+ c4 = Category.new(:name => "Child 1.2")
731
+ [c1, c2, c3, c4].each(&:save!)
732
+
733
+ c1.move_to_child_of(category)
734
+ c2.move_to_child_of(c1)
735
+ c3.move_to_child_of(c2)
736
+ c4.move_to_child_of(category)
737
+
738
+ levels = [
739
+ [0, "Top Level"],
740
+ [1, "Child 1"],
741
+ [2, "Child 1.1"],
742
+ [3, "Child 1.1.1"],
743
+ [4, "Child 1.1.1.1"],
744
+ [2, "Child 1.2"],
745
+ [1, "Child 2"],
746
+ [2, "Child 2.1"],
747
+ [1, "Child 3" ]]
748
+
749
+ check_structure(Category.root.self_and_descendants, levels)
750
+ end
751
+
752
+ def test_model_with_attr_accessible
753
+ model = Class.new(ActiveRecord::Base)
754
+ model.set_table_name 'categories'
755
+ model.attr_accessible :name
756
+ assert_nothing_raised do
757
+ model.acts_as_nested_set
758
+ model.new(:name => 'foo')
759
+ end
760
+ end
761
+
762
+ def test_before_move_callback
763
+ $called = false
764
+ Category.before_move { |r| $called = true }
765
+
766
+ categories(:child_2).move_to_root
767
+ assert $called
768
+ ensure
769
+ Category.class_eval { reset_callbacks :move }
770
+ end
771
+
772
+ def test_before_move_callback_returning_false_stops_move
773
+ Category.before_move { |r| false }
774
+
775
+ assert !categories(:child_3).move_to_root
776
+ assert !categories(:child_3).root?
777
+ ensure
778
+ Category.class_eval { reset_callbacks :move }
779
+ end
780
+
781
+ # NOTE this feature is hard to implement given current callbacks
782
+ # architecture. Since node is moved in an after_save hook,
783
+ # we can't stop moving by before filter. The only thing we can do
784
+ # is raise an exception preventing Model#save to commit transaction.
785
+
786
+ #def test_before_move_callback_returning_false_halts_save
787
+ #Category.before_move { |r| false }
788
+
789
+ #categories(:child_3).parent_id = nil
790
+ #assert !categories(:child_3).save
791
+ #ensure
792
+ #Category.class_eval { reset_callbacks :move }
793
+ #end
794
+
795
+ def test_calls_after_move_when_moving
796
+ $called = false
797
+ Category.after_move { $called = true }
798
+ categories(:child_3).parent = categories(:child_2)
799
+ assert categories(:child_3).save
800
+ assert $called
801
+ ensure
802
+ Category.class_eval { reset_callbacks :move }
803
+ end
804
+ end