awesome_nested_set_jrmurad 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.
- data/.autotest +13 -0
- data/.gitignore +6 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +87 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/awesome_nested_set_jrmurad.gemspec +71 -0
- data/init.rb +12 -0
- data/lib/awesome_nested_set.rb +614 -0
- data/lib/awesome_nested_set/helper.rb +32 -0
- data/test/application.rb +1 -0
- data/test/awesome_nested_set/helper_test.rb +41 -0
- data/test/awesome_nested_set_test.rb +801 -0
- data/test/db/database.yml +18 -0
- data/test/db/schema.rb +30 -0
- data/test/fixtures/categories.yml +34 -0
- data/test/fixtures/category.rb +15 -0
- data/test/fixtures/departments.yml +3 -0
- data/test/fixtures/notes.yml +38 -0
- data/test/test_helper.rb +29 -0
- metadata +109 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module CollectiveIdea #:nodoc:
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module NestedSet #:nodoc:
|
4
|
+
# This module provides some helpers for the model classes using acts_as_nested_set.
|
5
|
+
# It is included by default in all views.
|
6
|
+
#
|
7
|
+
module Helper
|
8
|
+
# Returns options for select.
|
9
|
+
# You can exclude some items from the tree.
|
10
|
+
# You can pass a block receiving an item and returning the string displayed in the select.
|
11
|
+
#
|
12
|
+
# == Params
|
13
|
+
# * +class_or_item+ - Class name or top level times
|
14
|
+
# * +mover+ - The item that is being move, used to exlude impossible moves
|
15
|
+
# * +&block+ - a block that will be used to display: { |item, descendants, level| ... }
|
16
|
+
#
|
17
|
+
# == Usage
|
18
|
+
#
|
19
|
+
# <%= f.select :parent_id, nested_set_options(Category, @category) { |item, descendants, level|
|
20
|
+
# "#{'–' * level} #{item.name}"
|
21
|
+
# }) %>
|
22
|
+
#
|
23
|
+
def nested_set_options(class_or_item, mover = nil, &block)
|
24
|
+
class_or_item.traverse(true, mover) do |item, descendants, level|
|
25
|
+
[ block.call(item, descendants, level), item.id ]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/test/application.rb
ADDED
@@ -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,d,level|
|
20
|
+
"#{'-' * 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,d,level|
|
33
|
+
"#{'-' * 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
|