rd_awesome_nested_set 1.4.4

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