pkondzior-sequel_nested_set 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 Paweł Kondzior
2
+ Copyright (c) 2007-2008 collectiveidea
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ Port of awesome nested set to Sequel::Model
2
+
3
+ more readme tobedone.
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ - Do more specs for scoped nested_set
2
+ - Add move hooks (before_,after_)
3
+ - Add documentation
@@ -0,0 +1,563 @@
1
+ unless Object.respond_to?(:returning)
2
+ class Object
3
+ def returning(value)
4
+ yield(value)
5
+ value
6
+ end
7
+ end
8
+ end
9
+
10
+ module Sequel
11
+ module Plugins
12
+ # This acts provides Nested Set functionality. Nested Set is a smart way to implement
13
+ # an _ordered_ tree, with the added feature that you can select the children and all of their
14
+ # descendants with a single query. The drawback is that insertion or move need some complex
15
+ # sql queries. But everything is done here by this module!
16
+ #
17
+ # Nested sets are appropriate each time you want either an orderd tree (menus,
18
+ # commercial categories) or an efficient way of querying big trees (threaded posts).
19
+ #
20
+ # == API
21
+ #
22
+ # # adds a new item at the "end" of the tree, i.e. with child.left = max(tree.right)+1
23
+ # child = MyClass.new(:name => "child1")
24
+ # child.save
25
+ # # now move the item to its right place
26
+ # child.move_to_child_of my_item
27
+ #
28
+ # You can pass an id or an object to:
29
+ # * <tt>#move_to_child_of</tt>
30
+ # * <tt>#move_to_right_of</tt>
31
+ # * <tt>#move_to_left_of</tt>
32
+ #
33
+ module NestedSet
34
+ # Configuration options are:
35
+ #
36
+ # * +:parent_column+ - specifies the column name to use for keeping the position integer (default: :parent_id)
37
+ # * +:left_column+ - column name for left boundry data, default :lft
38
+ # * +:right_column+ - column name for right boundry data, default :rgt
39
+ # * +:scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
40
+ # (if it hasn't been already) and use that as the foreign key restriction. You
41
+ # can also pass an array to scope by multiple attributes.
42
+ # Example: <tt>is :nested_set, { :scope => [:notable_id, :notable_type] }</tt>
43
+ # * +:dependent+ - behavior for cascading destroy. If set to :destroy, all the
44
+ # child objects are destroyed alongside this object by calling their destroy
45
+ # method. If set to :delete_all (default), all the child objects are deleted
46
+ # without calling their destroy method.
47
+ #
48
+ # See Sequle::Plugins::NestedSet::ClassMethods for a list of class methods and
49
+ # Sequle::Plugins::NestedSet::InstanceMethods for a list of instance methods added
50
+ # to acts_as_nested_set models
51
+ def self.apply(model, options = {})
52
+ options = {
53
+ :parent_column => :parent_id,
54
+ :left_column => :lft,
55
+ :right_column => :rgt,
56
+ :dependent => :delete_all, # or :destroy
57
+ }.merge(options)
58
+
59
+ if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
60
+ options[:scope] = "#{options[:scope]}_id".to_sym
61
+ end
62
+
63
+ model.class.class_eval do
64
+ attr_accessor :nested_set_options
65
+ end
66
+ model.nested_set_options = options
67
+
68
+ model.before_create { set_default_left_and_right }
69
+ model.before_destroy { prune_from_tree }
70
+
71
+ model.set_restricted_columns(*([:left, :right, :parent_id, options[:parent_column], options[:left_column], options[:right_column]].uniq))
72
+ end
73
+
74
+ module DatasetMethods
75
+ # All nested set queries should use this nested dataset method, which returns Dataset that provides
76
+ # proper :scope which you can configure on is :nested, { :scope => ... }
77
+ # declaration in your Sequel::Model
78
+ def nested
79
+ order(self.model_classes[nil].qualified_left_column)
80
+ end
81
+
82
+ # Returns dataset for all root nodes
83
+ def roots
84
+ nested.filter(self.model_classes[nil].qualified_parent_column => nil)
85
+ end
86
+
87
+ # Returns dataset for all of nodes which do not have children
88
+ def leaves
89
+ nested.filter(self.model_classes[nil].qualified_right_column - self.model_classes[nil].qualified_left_column => 1)
90
+ end
91
+ end
92
+
93
+ module ClassMethods
94
+
95
+ # Returns the first root
96
+ def root
97
+ roots.first
98
+ end
99
+
100
+ def qualified_parent_column(table_name = self.implicit_table_name)
101
+ "#{table_name}__#{self.nested_set_options[:parent_column]}".to_sym
102
+ end
103
+
104
+ def qualified_parent_column_literal
105
+ self.dataset.literal(self.nested_set_options[:parent_column])
106
+ end
107
+
108
+ def qualified_left_column(table_name = self.implicit_table_name)
109
+ "#{table_name}__#{self.nested_set_options[:left_column]}".to_sym
110
+ end
111
+
112
+ def qualified_left_column_literal
113
+ self.dataset.literal(self.nested_set_options[:left_column])
114
+ end
115
+
116
+ def qualified_right_column(table_name = self.implicit_table_name)
117
+ "#{table_name}__#{self.nested_set_options[:right_column]}".to_sym
118
+ end
119
+
120
+ def qualified_right_column_literal
121
+ self.dataset.literal(self.nested_set_options[:right_column])
122
+ end
123
+
124
+ def valid?
125
+ self.left_and_rights_valid? && self.no_duplicates_for_columns? && self.all_roots_valid?
126
+ end
127
+
128
+ def left_and_rights_valid?
129
+ self.left_outer_join(Client.implicit_table_name.as(:parent), self.qualified_parent_column => "parent__#{self.primary_key}".to_sym).
130
+ filter({ self.qualified_left_column => nil } |
131
+ { self.qualified_right_column => nil } |
132
+ (self.qualified_left_column >= self.qualified_right_column) |
133
+ (~{ self.qualified_parent_column => nil } & ((self.qualified_left_column <= self.qualified_left_column(:parent)) |
134
+ (self.qualified_right_column >= self.qualified_right_column(:parent))))).count == 0
135
+ end
136
+
137
+ def left_and_rights_valid_dataset?
138
+ self.left_outer_join(Client.implicit_table_name.as(:parent), self.qualified_parent_column => "parent__#{self.primary_key}".to_sym).
139
+ filter({ self.qualified_left_column => nil } |
140
+ { self.qualified_right_column => nil } |
141
+ (self.qualified_left_column >= self.qualified_right_column) |
142
+ (~{ self.qualified_parent_column => nil } & ((self.qualified_left_column <= self.qualified_left_column(:parent)) |
143
+ (self.qualified_right_column >= self.qualified_right_column(:parent)))))
144
+ end
145
+
146
+ def no_duplicates_for_columns?
147
+ # TODO: scope
148
+ # scope_columns = Array(self.nested_set_options[:scope]).map do |c|
149
+ # connection.quote_column_name(c)
150
+ # end.push(nil).join(", ")
151
+ [self.qualified_left_column, self.qualified_right_column].all? do |column|
152
+ self.dataset.select(column, :count[column]).group(column).having(:count[column] > 1).first.nil?
153
+ end
154
+ end
155
+
156
+ # Wrapper for each_root_valid? that can deal with scope.
157
+ def all_roots_valid?
158
+ # TODO: scope
159
+ # if self.nested_set_options[:scope]
160
+ # roots.group(:group => scope_column_names).group_by{|record| scope_column_names.collect{|col| record.send(col.to_sym)}}.all? do |scope, grouped_roots|
161
+ # each_root_valid?(grouped_roots)
162
+ # end
163
+ # else
164
+ each_root_valid?(roots.all)
165
+ # end
166
+ end
167
+
168
+ def each_root_valid?(roots_to_validate)
169
+ left = right = 0
170
+ roots_to_validate.all? do |root|
171
+ returning(root.left > left && root.right > right) do
172
+ left = root.left
173
+ right = root.right
174
+ end
175
+ end
176
+ end
177
+
178
+ # Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.
179
+ def rebuild!
180
+
181
+ scope = lambda{}
182
+ # TODO: add scope stuff
183
+
184
+ # Don't rebuild a valid tree.
185
+ return true if valid?
186
+ indices = {}
187
+
188
+ move_to_child_of_lambda = lambda do |parent_node|
189
+ # Set left
190
+ parent_node[nested_set_options[:left_column]] = indices[scope.call(parent_node)] += 1
191
+ # Gather child noodes of parend_node and iterate by children
192
+ parent_node.children.order(:id).all.each do |child_node|
193
+ move_to_child_of_lambda.call(child_node)
194
+ end
195
+ # Set right
196
+ parent_node[nested_set_options[:right_column]] = indices[scope.call(parent_node)] += 1
197
+ parent_node.save
198
+ end
199
+
200
+ # Gatcher root nodes and iterate by them
201
+ self.roots.all.each do |root_node|
202
+ # setup index for this scope
203
+ indices[scope.call(root_node)] ||= 0
204
+ move_to_child_of_lambda.call(root_node)
205
+ end
206
+ end
207
+
208
+ def to_text(&block)
209
+ text = []
210
+ self.roots.each do |root|
211
+ text << root.to_text(&block)
212
+ end
213
+ text.join("\n")
214
+ end
215
+
216
+ # Returns the entire set as a nested array. If flat is true then a flat
217
+ # array is returned instead. Specify mover to exclude any impossible
218
+ # moves. Pass a block to perform an operation on each item. The block
219
+ # arguments are |item, level|.
220
+ def to_nested_a(flat = false, mover = nil, &block)
221
+ descendants = self.nested.all
222
+ array = []
223
+
224
+ while not descendants.empty?
225
+ items = descendants.shift.to_nested_a(flat, mover, descendants, 0, &block)
226
+ array.send flat ? 'concat' : '<<', items
227
+ end
228
+
229
+ return array
230
+ end
231
+ end
232
+
233
+ module InstanceMethods
234
+
235
+ # Returns hash of Model nested set options
236
+ def nested_set_options
237
+ self.class.nested_set_options
238
+ end
239
+
240
+ # Setter of the left column
241
+ def left=(value)
242
+ self[self.nested_set_options[:left_column]] = value
243
+ end
244
+
245
+ # Setter of the right column
246
+ def right=(value)
247
+ self[self.nested_set_options[:right_column]] = value
248
+ end
249
+
250
+ # Getter of the left column
251
+ def left
252
+ self[self.nested_set_options[:left_column]] || 0
253
+ end
254
+
255
+ # Getter of the right column
256
+ def right
257
+ self[self.nested_set_options[:right_column]] || 0
258
+ end
259
+
260
+ # Setter of the parent column
261
+ def parent_id=(value)
262
+ self[self.nested_set_options[:parent_column]] = value
263
+ end
264
+
265
+ # Getter of parent column
266
+ def parent_id
267
+ self[self.nested_set_options[:parent_column]]
268
+ end
269
+
270
+ # Set left=, right= and parent_id= to be procted methods
271
+ # this methods should be used only internally by nested set plugin
272
+ protected :left=, :right=, :parent_id=
273
+
274
+ # Returns the level of this object in the tree
275
+ # root level is 0
276
+ def level
277
+ root? ? 0 : ancestors.count
278
+ end
279
+
280
+ # Returns true if this is a root node
281
+ def root?
282
+ parent_id.nil?
283
+ end
284
+
285
+ # Returns true if this is a leaf node
286
+ def leaf?
287
+ right - left == 1
288
+ end
289
+
290
+ # Returns true is this is a child node
291
+ def child?
292
+ !root?
293
+ end
294
+
295
+ # order by left column
296
+ def <=>(x)
297
+ left <=> x.left
298
+ end
299
+
300
+ # Returns root
301
+ def root
302
+ self_and_ancestors.first
303
+ end
304
+
305
+ # Returns the immediate parent
306
+ def parent
307
+ dataset.nested.filter(self.primary_key => self.parent_id).first if self.parent_id
308
+ end
309
+
310
+ # Returns the dataset for all parent nodes and self
311
+ def self_and_ancestors
312
+ dataset.filter((self.class.qualified_left_column <= left) & (self.class.qualified_right_column >= right))
313
+ end
314
+
315
+ # Returns the dataset for all children of the parent, including self
316
+ def self_and_siblings
317
+ dataset.nested.filter(self.class.qualified_parent_column => self.parent_id)
318
+ end
319
+
320
+ # Returns dataset for itself and all of its nested children
321
+ def self_and_descendants
322
+ dataset.nested.filter((self.class.qualified_left_column >= left) & (self.class.qualified_right_column <= right))
323
+ end
324
+
325
+ # Filter for dataset that will exclude self object
326
+ def without_self(dataset)
327
+ dataset.nested.filter(~{self.primary_key => self.id})
328
+ end
329
+
330
+ # Returns dataset for its immediate children
331
+ def children
332
+ dataset.nested.filter(self.class.qualified_parent_column => self.id)
333
+ end
334
+
335
+ # Returns dataset for all parents
336
+ def ancestors
337
+ without_self(self_and_ancestors)
338
+ end
339
+
340
+ # Returns dataset for all children of the parent, except self
341
+ def siblings
342
+ without_self(self_and_siblings)
343
+ end
344
+
345
+ # Returns dataset for all of its children and nested children
346
+ def descendants
347
+ without_self(self_and_descendants)
348
+ end
349
+
350
+ # Returns dataset for all of its nested children which do not have children
351
+ def leaves
352
+ descendants.filter(self.class.qualified_right_column - self.class.qualified_left_column => 1)
353
+ end
354
+
355
+ def is_descendant_of?(other)
356
+ other.left < self.left && self.left < other.right && same_scope?(other)
357
+ end
358
+
359
+ def is_or_is_descendant_of?(other)
360
+ other.left <= self.left && self.left < other.right && same_scope?(other)
361
+ end
362
+
363
+ def is_ancestor_of?(other)
364
+ self.left < other.left && other.left < self.right && same_scope?(other)
365
+ end
366
+
367
+ def is_or_is_ancestor_of?(other)
368
+ self.left <= other.left && other.left < self.right && same_scope?(other)
369
+ end
370
+
371
+ # Check if other model is in the same scope
372
+ def same_scope?(other)
373
+ Array(nil).all? do |attr|
374
+ self.send(attr) == other.send(attr)
375
+ end
376
+ end
377
+
378
+ # Find the first sibling to the left
379
+ def left_sibling
380
+ siblings.filter(self.class.qualified_left_column < left).order(self.class.qualified_left_column.desc).first
381
+ end
382
+
383
+ # Find the first sibling to the right
384
+ def right_sibling
385
+ siblings.filter(self.class.qualified_left_column > left).first
386
+ end
387
+
388
+
389
+ # Shorthand method for finding the left sibling and moving to the left of it.
390
+ def move_left
391
+ self.move_to_left_of(self.left_sibling)
392
+ end
393
+
394
+ # Shorthand method for finding the right sibling and moving to the right of it.
395
+ def move_right
396
+ self.move_to_right_of(self.right_sibling)
397
+ end
398
+
399
+ # Move the node to the left of another node (you can pass id only)
400
+ def move_to_left_of(node)
401
+ self.move_to(node, :left)
402
+ end
403
+
404
+ # Move the node to the left of another node (you can pass id only)
405
+ def move_to_right_of(node)
406
+ self.move_to(node, :right)
407
+ end
408
+
409
+ # Move the node to the child of another node (you can pass id only)
410
+ def move_to_child_of(node)
411
+ self.move_to(node, :child)
412
+ end
413
+
414
+ # Move the node to root nodes
415
+ def move_to_root
416
+ self.move_to(nil, :root)
417
+ end
418
+
419
+ # Check if node move is possible for specific target
420
+ def move_possible?(target)
421
+ self != target && # Can't target self
422
+ same_scope?(target) && # can't be in different scopes
423
+ # !(left..right).include?(target.left..target.right) # this needs tested more
424
+ # detect impossible move
425
+ !((left <= target.left && right >= target.left) or (left <= target.right && right >= target.right))
426
+ end
427
+
428
+ # You can pass block that will have
429
+ def to_text
430
+ self_and_descendants.map do |node|
431
+ if block_given?
432
+ inspect = yield(node)
433
+ else
434
+ inspect = node.class.inspect
435
+ end
436
+ "#{'*'*(node.level+1)} #{inspect} (#{node.parent_id.inspect}, #{node.left}, #{node.right})"
437
+ end.join("\n")
438
+ end
439
+
440
+ # Returns self and its descendants as a nested array. If flat is true
441
+ # then a flat array is returned instead. Specify mover to exclude any
442
+ # impossible moves. Pass a block to perform an operation on each item.
443
+ # The block arguments are |item, level|. The remaining arguments for
444
+ # this method are for recursion and should not normally be given.
445
+ def to_nested_a(flat = false, mover = nil, descendants = nil, level = self.level, &block)
446
+ descendants ||= self.descendants.all
447
+ array = [ block_given? ? yield(self, level) : self ]
448
+
449
+ while not descendants.empty?
450
+ break unless descendants.first.parent_id == self.id
451
+ item = descendants.shift
452
+ items = item.to_nested_a(flat, mover, descendants, level + 1, &block)
453
+ array.send flat ? 'concat' : '<<', items if mover.nil? or mover.new? or mover.move_possible?(item)
454
+ end
455
+
456
+ return array
457
+ end
458
+
459
+ protected
460
+ # on creation, set automatically lft and rgt to the end of the tree
461
+ def set_default_left_and_right
462
+ maxright = dataset.nested.max(self.class.qualified_right_column).to_i || 0
463
+ # adds the new node to the right of all existing nodes
464
+ self.left = maxright + 1
465
+ self.right = maxright + 2
466
+ end
467
+
468
+ # Prunes a branch off of the tree, shifting all of the elements on the right
469
+ # back to the left so the counts still work.
470
+ def prune_from_tree
471
+ return if self.right.nil? || self.left.nil?
472
+ diff = self.right - self.left + 1
473
+
474
+ #TODO: implemente :dependent option
475
+ # delete_method = acts_as_nested_set_options[:dependent] == :destroy ?
476
+ # :destroy_all : :delete_all
477
+
478
+ #TODO: implement prune method
479
+ # self.class.base_class.transaction do
480
+ # nested_set_scope.send(delete_method,
481
+ # ["#{quoted_left_column_name} > ? AND #{quoted_right_column_name} < ?",
482
+ # left, right]
483
+ # )
484
+ # nested_set_scope.update_all(
485
+ # ["#{quoted_left_column_name} = (#{quoted_left_column_name} - ?)", diff],
486
+ # ["#{quoted_left_column_name} >= ?", right]
487
+ # )
488
+ # nested_set_scope.update_all(
489
+ # ["#{quoted_right_column_name} = (#{quoted_right_column_name} - ?)", diff],
490
+ # ["#{quoted_right_column_name} >= ?", right]
491
+ # )
492
+ # end
493
+ end
494
+
495
+ # reload left, right, and parent
496
+ def reload_nested_set
497
+ reload(:select => "#{quoted_left_column_name}, " +
498
+ "#{quoted_right_column_name}, #{quoted_parent_column_name}")
499
+ end
500
+
501
+ def move_to(target, position)
502
+ raise Error, "You cannot move a new node" if self.new?
503
+ # #TODO: add callback
504
+ db.transaction do
505
+ unless position == :root || self.move_possible?(target)
506
+ raise Error, "Impossible move, target node cannot be inside moved tree."
507
+ end
508
+
509
+ bound = case position
510
+ when :child; target.right
511
+ when :left; target.left
512
+ when :right; target.right + 1
513
+ when :root; 1
514
+ else raise Error, "Position should be :child, :left, :right or :root ('#{position}' received)."
515
+ end
516
+
517
+ if bound > self.right
518
+ bound = bound - 1
519
+ other_bound = self.right + 1
520
+ else
521
+ other_bound = self.left - 1
522
+ end
523
+
524
+ # there would be no change
525
+ return if bound == self.right || bound == self.left
526
+
527
+ # we have defined the boundaries of two non-overlapping intervals,
528
+ # so sorting puts both the intervals and their boundaries in order
529
+ a, b, c, d = [self.left, self.right, bound, other_bound].sort
530
+
531
+ new_parent = case position
532
+ when :child; target.id
533
+ when :root; 'NULL'
534
+ else target.parent_id
535
+ end
536
+
537
+ # TODO : scope stuff for update
538
+ self.dataset.update(
539
+ "#{self.class.qualified_left_column_literal} = (CASE " +
540
+ "WHEN #{self.class.qualified_left_column_literal} BETWEEN #{a} AND #{b} " +
541
+ "THEN #{self.class.qualified_left_column_literal} + #{d} - #{b} " +
542
+ "WHEN #{self.class.qualified_left_column_literal} BETWEEN #{c} AND #{d} " +
543
+ "THEN #{self.class.qualified_left_column_literal} + #{a} - #{c} " +
544
+ "ELSE #{self.class.qualified_left_column_literal} END), " +
545
+ "#{self.class.qualified_right_column_literal} = (CASE " +
546
+ "WHEN #{self.class.qualified_right_column_literal} BETWEEN #{a} AND #{b} " +
547
+ "THEN #{self.class.qualified_right_column_literal} + #{d} - #{b} " +
548
+ "WHEN #{self.class.qualified_right_column_literal} BETWEEN #{c} AND #{d} " +
549
+ "THEN #{self.class.qualified_right_column_literal} + #{a} - #{c} " +
550
+ "ELSE #{self.class.qualified_right_column_literal} END), " +
551
+ "#{self.class.qualified_parent_column_literal} = (CASE " +
552
+ "WHEN #{self.primary_key} = #{self.id} THEN #{new_parent} " +
553
+ "ELSE #{self.class.qualified_parent_column_literal} END)"
554
+ )
555
+ target.refresh if target
556
+ self.refresh
557
+ #TODO: add after_move
558
+ end
559
+ end
560
+ end
561
+ end
562
+ end
563
+ end
@@ -0,0 +1,617 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Sequel Nested Set" do
4
+
5
+ describe "without scope" do
6
+
7
+ before(:each) do
8
+ prepare_nested_set_data
9
+ @root = Client.filter(:name => 'Top Level').first
10
+ @node1 = Client.filter(:name => 'Child 1').first
11
+ @node2 = Client.filter(:name => 'Child 2').first
12
+ @node2_1 = Client.filter(:name => 'Child 2.1').first
13
+ @node3 = Client.filter(:name => 'Child 3').first
14
+ @root2 = Client.filter(:name => 'Top Level 2').first
15
+ end
16
+
17
+ describe "ClassMethods" do
18
+
19
+ it "should have nested_set_options" do
20
+ Client.should respond_to(:nested_set_options)
21
+ end
22
+
23
+ it "should have default options :left_column, :right_column, :parent_column, :dependent and :scope" do
24
+ Client.nested_set_options[:left_column].should == :lft
25
+ Client.nested_set_options[:right_column].should == :rgt
26
+ Client.nested_set_options[:parent_column].should == :parent_id
27
+ Client.nested_set_options[:dependent].should == :delete_all
28
+ Client.nested_set_options[:scope].should be_nil
29
+ end
30
+
31
+ it "should have qualified column methods" do
32
+ Client.qualified_parent_column.should == :clients__parent_id
33
+ Client.qualified_left_column.should == :clients__lft
34
+ Client.qualified_right_column.should == :clients__rgt
35
+ end
36
+
37
+ it "should have roots that contains all root nodes" do
38
+ roots = Client.roots.all
39
+ roots.should == Client.filter(:parent_id => nil).all
40
+ roots.should == [@root, @root2]
41
+ end
42
+
43
+ it "should have root that will be root? => true" do
44
+ Client.roots.first.root?.should be_true
45
+ end
46
+
47
+ it "should have all leaves" do
48
+ leaves = Client.leaves.all
49
+ leaves.should == Client.nested.filter(:rgt - :lft => 1).all
50
+ leaves.should == [@node1, @node2_1, @node3, @root2]
51
+ end
52
+
53
+ it "should have root" do
54
+ Client.root.should == @root
55
+ end
56
+
57
+ it "should have to_text method that returns whole tree from all root nodes as text" do
58
+ Client.to_text.should == "* Client (nil, 1, 10)\n** Client (1, 2, 3)\n** Client (1, 4, 7)\n*** Client (3, 5, 6)\n** Client (1, 8, 9)\n* Client (nil, 11, 12)"
59
+ end
60
+
61
+ it "should have to_text method that returns whole tree from all root nodes as text and should be able to pass block" do
62
+ Client.to_text { |node| node.name }.should == "* Top Level (nil, 1, 10)\n** Child 1 (1, 2, 3)\n** Child 2 (1, 4, 7)\n*** Child 2.1 (3, 5, 6)\n** Child 3 (1, 8, 9)\n* Top Level 2 (nil, 11, 12)"
63
+ end
64
+
65
+ it "should have to_nested_a method that returns nested array of all nodes from roots to leaves" do
66
+ Client.to_nested_a.should == [[@root, [@node1], [@node2, [@node2_1]], [@node3]], [@root2]]
67
+ end
68
+
69
+ it "should have to_nested_a method that can pass block with node and level" do
70
+ Client.to_nested_a { |node, level| level }.should == [[0, [1], [1, [2]], [1]], [0]]
71
+ end
72
+ end
73
+
74
+ describe "InstanceMethods" do
75
+
76
+ it "should have nested_set_options" do
77
+ @root.class.should respond_to(:nested_set_options)
78
+ end
79
+
80
+ it "should have parent, left, right getter based on nested set config" do
81
+ node = Client.new.update_all(:parent_id => nil, :lft => 1, :rgt => 2)
82
+ node.left.should == node[node.class.nested_set_options[:left_column]]
83
+ node.right.should == node[node.class.nested_set_options[:right_column]]
84
+ node.parent_id.should == node[node.class.nested_set_options[:parent_column]]
85
+ end
86
+
87
+ it "should have parent, left, right setter based on nested set config" do
88
+ node = Client.new
89
+ node.send(:left=, 1)
90
+ node.send(:right=, 2)
91
+ node.send(:parent_id=, 69)
92
+ node.left.should == node[node.class.nested_set_options[:left_column]]
93
+ node.right.should == node[node.class.nested_set_options[:right_column]]
94
+ node.parent_id.should == node[node.class.nested_set_options[:parent_column]]
95
+ end
96
+
97
+ it "should parent, left and right setters be protected methods" do
98
+ Client.new.protected_methods.include?("left=").should be_true
99
+ Client.new.protected_methods.include?("right=").should be_true
100
+ Client.new.protected_methods.include?("parent_id=").should be_true
101
+ end
102
+
103
+ it "shoud have faild on new when passing keys configured as right_column, left_column, parent_column" do
104
+ lambda { Client.new(Client.nested_set_options[:left_column] => 1) }.should raise_error(Sequel::Error)
105
+ lambda { Client.new(Client.nested_set_options[:right_column] => 2) }.should raise_error(Sequel::Error)
106
+ lambda { Client.new(Client.nested_set_options[:parent_column] => nil) }.should raise_error(Sequel::Error)
107
+ end
108
+
109
+ it "Client.new with {:left => 1, :right => 2, :parent_id => nil} should raise NoMethodError exception" do
110
+ lambda { Client.new({:left => 1, :right => 2, :parent_id => nil}) }.should raise_error(Sequel::Error)
111
+ end
112
+
113
+ it "should have nested_set_options equal to Model.nested_set_options" do
114
+ @root.nested_set_options.should == Client.nested_set_options
115
+ end
116
+
117
+ it "should have nodes that have common root" do
118
+ @node1.root.should == @root
119
+ end
120
+
121
+ it "should have nodes that have their parent" do
122
+ @node2_1.parent.should == @node2
123
+ end
124
+
125
+ it "should have leaf that will be true leaf?" do
126
+ @root.leaf?.should_not be_true
127
+ @node2_1.leaf?.should be_true
128
+ end
129
+
130
+ it "should have child that will be true child?" do
131
+ @root.child?.should_not be_true
132
+ @node2_1.child?.should be_true
133
+ end
134
+
135
+ it "should have <=> method" do
136
+ @root.should respond_to(:<=>)
137
+ end
138
+
139
+ it "Should order by left column" do
140
+ (@node1 <=> @node2).should == -1
141
+ end
142
+
143
+ it "should have level of node" do
144
+ @root.level.should == 0
145
+ @node1.level.should == 1
146
+ @node2.level.should == 1
147
+ @node2_1.level.should == 2
148
+ end
149
+
150
+ it "should have parent relation" do
151
+ @node2_1.parent.should == @node2
152
+ end
153
+
154
+ it "should have self_and_sibling that have self node and all its siblings" do
155
+ @root.self_and_siblings.all.should == [@root, @root2]
156
+ @node1.self_and_siblings.all.should == [@node1, @node2, @node3]
157
+ end
158
+
159
+ it "should have siblings of node withot itself" do
160
+ @root.siblings.all.should == [@root2]
161
+ @node1.siblings.all.should == [@node2, @node3]
162
+ end
163
+
164
+ it "should have self_and_ancestors that have self node and all its ancestors" do
165
+ @root.self_and_ancestors.all.should == [@root]
166
+ @node1.self_and_ancestors.all.should == [@root, @node1]
167
+ end
168
+
169
+ it "should have ancestors of node withot itself" do
170
+ @root.ancestors.all.should == []
171
+ @node1.ancestors.all.should == [@root]
172
+ end
173
+
174
+ it "should have self_and_descendants that have self node and all its descendents" do
175
+ @root.self_and_descendants.all.should == [@root, @node1, @node2, @node2_1, @node3]
176
+ @node2.self_and_descendants.all.should == [@node2, @node2_1]
177
+ @node2_1.self_and_descendants.all.should == [@node2_1]
178
+ end
179
+
180
+ it "should have descendents that are children and nested children wihout itself" do
181
+ @root.descendants.all.should == [@node1, @node2, @node2_1, @node3]
182
+ @node2.descendants.all.should == [@node2_1]
183
+ @node2_1.descendants.all.should == []
184
+ end
185
+
186
+ it "should have children that returns set of only node immediate children" do
187
+ @root.children.all.should == [@node1, @node2, @node3]
188
+ @node2.children.all.should == [@node2_1]
189
+ @node2_1.children.all.should == []
190
+ end
191
+
192
+ it "should have leaves that are set of all of node nested children which do not have children" do
193
+ @root.leaves.all.should == [@node1, @node2_1, @node3]
194
+ @node2.leaves.all.should == [@node2_1]
195
+ @node2_1.leaves.all.should == []
196
+ end
197
+
198
+ it "should be able to get left sibling" do
199
+ @node2.left_sibling.should == @node1
200
+ @node3.left_sibling.should == @node2
201
+ @node1.left_sibling.should be_nil
202
+ end
203
+
204
+ it "should be able to get proper right sibling" do
205
+ @node1.right_sibling.should == @node2
206
+ @node2.right_sibling.should == @node3
207
+ @node3.right_sibling.should be_nil
208
+ end
209
+
210
+ it "should have node_x.is_or_is_descendant_of?(node_y) that will return proper boolean value" do
211
+ @node1.is_or_is_descendant_of?(@root).should be_true
212
+ @node2_1.is_or_is_descendant_of?(@root).should be_true
213
+ @node2_1.is_or_is_descendant_of?(@node2).should be_true
214
+ @node2.is_or_is_descendant_of?(@node2_1).should be_false
215
+ @node2.is_or_is_descendant_of?(@node1).should be_false
216
+ @node1.is_or_is_descendant_of?(@node1).should be_true
217
+ end
218
+
219
+ it "should have node_x.is_ancestor_of?(node_y) that will return proper boolean value" do
220
+ @node1.is_descendant_of?(@root).should be_true
221
+ @node2_1.is_descendant_of?(@root).should be_true
222
+ @node2_1.is_descendant_of?(@node2).should be_true
223
+ @node2.is_descendant_of?(@node2_1).should be_false
224
+ @node2.is_descendant_of?(@node1).should be_false
225
+ @node1.is_descendant_of?(@node1).should be_false
226
+ end
227
+
228
+ it "should have node_x.is_ancestor_of?(node_y) that will return proper boolean value" do
229
+ @root.is_ancestor_of?(@node1).should be_true
230
+ @root.is_ancestor_of?(@node2_1).should be_true
231
+ @node2.is_ancestor_of?(@node2_1).should be_true
232
+ @node2_1.is_ancestor_of?(@node2).should be_false
233
+ @node1.is_ancestor_of?(@node2).should be_false
234
+ @node1.is_ancestor_of?(@node1).should be_false
235
+ end
236
+
237
+ it "should have node_x.is_or_is_ancestor_of?(node_y) that will return proper boolean value" do
238
+ @root.is_or_is_ancestor_of?(@node1).should be_true
239
+ @root.is_or_is_ancestor_of?(@node2_1).should be_true
240
+ @node2.is_or_is_ancestor_of?(@node2_1).should be_true
241
+ @node2_1.is_or_is_ancestor_of?(@node2).should be_false
242
+ @node1.is_or_is_ancestor_of?(@node2).should be_false
243
+ @node1.is_or_is_ancestor_of?(@node1).should be_true
244
+ end
245
+
246
+ it "should have node2 with left sibling as node1 and node3 with left sibling node2" do
247
+ @node2.left_sibling.should == @node1
248
+ @node3.left_sibling.should == @node2
249
+ end
250
+
251
+ it "should have root without left sibling" do
252
+ @root.left_sibling.should be_nil
253
+ end
254
+
255
+ it "should have node2_1 without left sibling" do
256
+ @node2_1.left_sibling.should be_nil
257
+ end
258
+
259
+ it "should have node1 without left sibling" do
260
+ @node1.left_sibling.should be_nil
261
+ end
262
+
263
+ it "should have node2 with right sibling as node3 and node1 with right sibling node2" do
264
+ @node2.right_sibling.should == @node3
265
+ @node1.right_sibling.should == @node2
266
+ end
267
+
268
+ it "should have root with right sibling as root2 and root2 with without right sibling" do
269
+ @root.right_sibling.should == @root2
270
+ @root2.right_sibling.should be_nil
271
+ end
272
+
273
+ it "should have node2_1 without right sibling" do
274
+ @node2_1.right_sibling.should be_nil
275
+ end
276
+
277
+ it "should have node3 without right sibling" do
278
+ @node3.right_sibling.should be_nil
279
+ end
280
+
281
+ it "should have to_text method that returns whole tree of instance node as text" do
282
+ @root.to_text.should == "* Client (nil, 1, 10)\n** Client (1, 2, 3)\n** Client (1, 4, 7)\n*** Client (3, 5, 6)\n** Client (1, 8, 9)"
283
+ end
284
+
285
+ it "should have to_text method that returns whole tree of instance node as text and should be able to pass block" do
286
+ @root.to_text { |node| node.name }.should == "* Top Level (nil, 1, 10)\n** Child 1 (1, 2, 3)\n** Child 2 (1, 4, 7)\n*** Child 2.1 (3, 5, 6)\n** Child 3 (1, 8, 9)"
287
+ end
288
+
289
+ it "should node2 be able to move to the left" do
290
+ @node2.move_left
291
+ @node2.left_sibling.should be_nil
292
+ @node2.right_sibling.should == @node1.refresh
293
+ Client.valid?.should be_true
294
+ end
295
+
296
+ it "should node2 be able to move to the right" do
297
+ @node2.move_right
298
+ @node2.right_sibling.should be_nil
299
+ @node2.left_sibling.should == @node3.refresh
300
+ Client.valid?.should be_true
301
+ end
302
+
303
+ it "should node3 be able to move to the left of node1" do
304
+ @node3.move_to_left_of(@node1)
305
+ @node3.left_sibling.should be_nil
306
+ @node3.right_sibling.should == @node1.refresh
307
+ Client.valid?.should be_true
308
+ end
309
+
310
+ it "should node1 be able to move to the right of node1" do
311
+ @node1.move_to_right_of(@node3)
312
+ @node1.right_sibling.should be_nil
313
+ @node1.left_sibling.should == @node3.refresh
314
+ Client.valid?.should be_true
315
+ end
316
+
317
+ it "should node2 be able to became root" do
318
+ @node2.move_to_root
319
+ @node2.parent.should be_nil
320
+ @node2.level.should == 0
321
+ @node2_1.level.should == 1
322
+ @node2.left == 1
323
+ @node2.right == 4
324
+ Client.valid?.should be_true
325
+ end
326
+
327
+ it "should node1 be able to move to child of node3" do
328
+ @node1.move_to_child_of(@node3)
329
+ @node1.parent_id.should == @node3.id
330
+ Client.valid?.should be_true
331
+ end
332
+
333
+ it "should be able to move new node to the end of root children" do
334
+ child = Client.create(:name => 'New Child')
335
+ child.move_to_child_of(@root)
336
+ @root.children.last.should == child
337
+ Client.valid?.should be_true
338
+ end
339
+
340
+ it "should be able to move node2 as child of node1" do
341
+ @node2.left.should == 4
342
+ @node2.right.should == 7
343
+ @node1.left.should == 2
344
+ @node1.right.should == 3
345
+ @node2.move_to_child_of(@node1)
346
+ @node2.parent_id.should == @node1.id
347
+ Client.valid?.should be_true
348
+ @node2.left.should == 3
349
+ @node2.right.should == 6
350
+ @node1.left.should == 2
351
+ @node1.right.should == 7
352
+ end
353
+
354
+ it "should be able to move root node to child of new node" do
355
+ @root2.left.should == 11
356
+ @root2.right.should == 12
357
+
358
+ root3 = Client.create(:name => 'New Root')
359
+ root3.left.should == 13
360
+ root3.right.should == 14
361
+
362
+ @root2.move_to_child_of(root3)
363
+
364
+ Client.valid?.should be_true
365
+ @root2.parent_id.should == root3.id
366
+
367
+ @root2.left.should == 12
368
+ @root2.right.should == 13
369
+
370
+ root3.left.should == 11
371
+ root3.right.should == 14
372
+ end
373
+
374
+ it "should be able to move root node to child of new node" do
375
+ @root.left.should == 1
376
+ @root.right.should == 10
377
+ @node2_1.left.should == 5
378
+ @node2_1.right.should == 6
379
+
380
+ root3 = Client.create(:name => 'New Root')
381
+ @root.move_to_child_of(root3)
382
+ Client.valid?.should be_true
383
+ @root.parent_id.should == root3.id
384
+
385
+ @root.left.should == 4
386
+ @root.right.should == 13
387
+
388
+ @node2_1.refresh
389
+ @node2_1.left.should == 8
390
+ @node2_1.right.should == 9
391
+ end
392
+
393
+ it "should be able to rebuild whole tree" do
394
+ node1 = Client.create(:name => 'Node-1')
395
+ node2 = Client.create(:name => 'Node-2')
396
+ node3 = Client.create(:name => 'Node-3')
397
+
398
+ node2.move_to_child_of node1
399
+ node3.move_to_child_of node1
400
+
401
+ output = Client.roots.last.to_text
402
+ Client.update('lft = null, rgt = null')
403
+ Client.rebuild!
404
+
405
+ Client.roots.last.to_text.should == output
406
+ end
407
+
408
+ it "should be invalid which lft = null" do
409
+ Client.valid?.should be_true
410
+ Client.update("lft = NULL")
411
+ Client.valid?.should be_false
412
+ end
413
+
414
+ it "should be invalid which rgt = null" do
415
+ Client.valid?.should be_true
416
+ Client.update("rgt = NULL")
417
+ Client.valid?.should be_false
418
+ end
419
+
420
+ it "should be valid with mising intermediate node" do
421
+ Client.valid?.should be_true
422
+ @node2.destroy
423
+ Client.valid?.should be_true
424
+ end
425
+
426
+ it "should be invalid with overlapping right nodes" do
427
+ Client.valid?.should be_true
428
+ @root2[:lft] = 0
429
+ @root2.save
430
+ Client.valid?.should be_false
431
+ end
432
+
433
+ it "should be able to rebild" do
434
+ Client.valid?.should be_true
435
+ before_text = Client.root.to_text
436
+ Client.update('lft = NULL, rgt = NULL')
437
+ Client.rebuild!
438
+ Client.valid?.should be_true
439
+ before_text.should == Client.root.to_text
440
+ end
441
+
442
+ it "shold be able to move for sibbling" do
443
+ @node2.move_possible?(@node1).should be_true
444
+ end
445
+
446
+ it "shold not be able to move for itself" do
447
+ @root.move_possible?(@root).should be_false
448
+ end
449
+
450
+ it "should not be able to move for parent" do
451
+ @root.descendants.each do |descendant_node|
452
+ @root.move_possible?(descendant_node).should be_false
453
+ descendant_node.move_possible?(@root).should be_true
454
+ end
455
+ end
456
+
457
+ it "should be correct is_or_is_ancestor_of?" do
458
+ [@node1, @node2, @node2_1, @node3].each do |node|
459
+ @root.is_or_is_ancestor_of?(node).should be_true
460
+ end
461
+ @root.is_or_is_ancestor_of?(@root2).should be_false
462
+ end
463
+
464
+ it "should be invalid left_and_rights_valid? for nil lefts" do
465
+ Client.left_and_rights_valid?.should be_true
466
+ @node2[:lft] = nil
467
+ @node2.save
468
+ Client.left_and_rights_valid?.should be_false
469
+ end
470
+
471
+ it "should be invalid left_and_rights_valid? for nil rights" do
472
+ Client.left_and_rights_valid?.should be_true
473
+ @node2[:rgt] = nil
474
+ @node2.save
475
+ Client.left_and_rights_valid?.should be_false
476
+ end
477
+
478
+ it "should return true for left_and_rights_valid? when node lft is equal for root lft" do
479
+ Client.left_and_rights_valid?.should be_true
480
+ @node2[:lft] = @root[:lft]
481
+ @node2.save
482
+ Client.left_and_rights_valid?.should be_false
483
+ end
484
+
485
+ it "should return true for left_and_rights_valid? when node rgt is equal for root rgt" do
486
+ Client.left_and_rights_valid?.should be_true
487
+ @node2[:rgt] = @root[:rgt]
488
+ @node2.save
489
+ Client.left_and_rights_valid?.should be_false
490
+ end
491
+
492
+ it "should be valid after moving dirty nodes" do
493
+ n1 = Client.create
494
+ n2 = Client.create
495
+ n3 = Client.create
496
+ n4 = Client.create
497
+
498
+ n2.move_to_child_of(n1)
499
+ Client.valid?.should be_true
500
+
501
+ n3.move_to_child_of(n1)
502
+ Client.valid?.should be_true
503
+
504
+ n4.move_to_child_of(n2)
505
+ Client.valid?.should be_true
506
+ end
507
+
508
+ it "should have to_nested_a method that returns nested array of all nodes from roots to leaves" do
509
+ @root.to_nested_a.should == [@root, [@node1], [@node2, [@node2_1]], [@node3]]
510
+ end
511
+
512
+ it "should have to_nested_a method that can pass block with node and level" do
513
+ @root.to_nested_a { |node, level| level }.should == [0, [1], [1, [2]], [1]]
514
+ end
515
+ end
516
+ end
517
+
518
+ describe "wiht scope" do
519
+ describe "ClassMethods" do
520
+ it "should be no duplicates for columns accross different scopes" do
521
+
522
+ end
523
+
524
+ it "should have all roots valid accross different scopes" do
525
+
526
+ end
527
+
528
+ it "should have multi scope" do
529
+
530
+ end
531
+
532
+ it "should be able to rebuild! accross different scopes" do
533
+
534
+ end
535
+
536
+ it "should have same_scope? true for nodes in the same scope" do
537
+
538
+ end
539
+
540
+ it "should have equal nodes in the same scope" do
541
+
542
+ end
543
+
544
+ it "should @root and @node be in same scope" do
545
+ # @root.same_scope?(@node).should be_true
546
+ end
547
+
548
+ it "should @root and @root_in_other_scope be in different scope" do
549
+
550
+ end
551
+
552
+ # def test_multi_scoped_no_duplicates_for_columns?
553
+ # assert_nothing_raised do
554
+ # Note.no_duplicates_for_columns?
555
+ # end
556
+ # end
557
+ #
558
+ # def test_multi_scoped_all_roots_valid?
559
+ # assert_nothing_raised do
560
+ # Note.all_roots_valid?
561
+ # end
562
+ # end
563
+ #
564
+ # def test_multi_scoped
565
+ # note1 = Note.create!(:body => "A", :notable_id => 2, :notable_type => 'Category')
566
+ # note2 = Note.create!(:body => "B", :notable_id => 2, :notable_type => 'Category')
567
+ # note3 = Note.create!(:body => "C", :notable_id => 2, :notable_type => 'Default')
568
+ #
569
+ # assert_equal [note1, note2], note1.self_and_siblings
570
+ # assert_equal [note3], note3.self_and_siblings
571
+ # end
572
+ #
573
+ # def test_multi_scoped_rebuild
574
+ # root = Note.create!(:body => "A", :notable_id => 3, :notable_type => 'Category')
575
+ # child1 = Note.create!(:body => "B", :notable_id => 3, :notable_type => 'Category')
576
+ # child2 = Note.create!(:body => "C", :notable_id => 3, :notable_type => 'Category')
577
+ #
578
+ # child1.move_to_child_of root
579
+ # child2.move_to_child_of root
580
+ #
581
+ # Note.update_all('lft = null, rgt = null')
582
+ # Note.rebuild!
583
+ #
584
+ # assert_equal Note.roots.find_by_body('A'), root
585
+ # assert_equal [child1, child2], Note.roots.find_by_body('A').children
586
+ # end
587
+ #
588
+ # def test_same_scope_with_multi_scopes
589
+ # assert_nothing_raised do
590
+ # notes(:scope1).same_scope?(notes(:child_1))
591
+ # end
592
+ # assert notes(:scope1).same_scope?(notes(:child_1))
593
+ # assert notes(:child_1).same_scope?(notes(:scope1))
594
+ # assert !notes(:scope1).same_scope?(notes(:scope2))
595
+ # end
596
+ #
597
+ # def test_quoting_of_multi_scope_column_names
598
+ # assert_equal ["\"notable_id\"", "\"notable_type\""], Note.quoted_scope_column_names
599
+ # end
600
+ #
601
+ # def test_equal_in_same_scope
602
+ # assert_equal notes(:scope1), notes(:scope1)
603
+ # assert_not_equal notes(:scope1), notes(:child_1)
604
+ # end
605
+ #
606
+ # def test_equal_in_different_scopes
607
+ # assert_not_equal notes(:scope1), notes(:scope2)
608
+ # end
609
+ end
610
+
611
+ describe "InstanceMethods" do
612
+
613
+ end
614
+ end
615
+
616
+ end
617
+
@@ -0,0 +1 @@
1
+ -x gems,spec
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --loadby
3
+ mtime
4
+ --backtrace
5
+ --format
6
+ specdoc
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'sequel'
4
+ require 'logger'
5
+ require 'ruby-debug'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/sequel_nested_set'
8
+
9
+ DB = Sequel.sqlite # memory database
10
+ DB.logger = Logger.new('log/db.log')
11
+
12
+ class Client < Sequel::Model
13
+ is :nested_set
14
+ set_schema do
15
+ primary_key :id
16
+ column :name, :text
17
+ column :parent_id, :integer
18
+ column :lft, :integer
19
+ column :rgt, :integer
20
+ end
21
+ end
22
+
23
+ Client.create_table
24
+
25
+ def prepare_nested_set_data
26
+ Client.drop_table if Client.table_exists?
27
+ Client.create_table!
28
+ DB[:clients] << {"name"=>"Top Level 2", "lft"=>11, "id"=>6, "rgt"=>12}
29
+ DB[:clients] << {"name"=>"Child 2.1", "lft"=>5, "id"=>4, "parent_id"=>3, "rgt"=>6}
30
+ DB[:clients] << {"name"=>"Child 1", "lft"=>2, "id"=>2, "parent_id"=>1, "rgt"=>3}
31
+ DB[:clients] << {"name"=>"Top Level", "lft"=>1, "id"=>1, "rgt"=>10}
32
+ DB[:clients] << {"name"=>"Child 2", "lft"=>4, "id"=>3, "parent_id"=>1, "rgt"=>7}
33
+ DB[:clients] << {"name"=>"Child 3", "lft"=>8, "id"=>5, "parent_id"=>1, "rgt"=>9}
34
+ end
35
+
36
+ prepare_nested_set_data
37
+
38
+
39
+
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pkondzior-sequel_nested_set
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.9
5
+ platform: ruby
6
+ authors:
7
+ - "Pawe\xC5\x82 Kondzior"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-11 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sequel
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.8.0
23
+ version:
24
+ description: Nested set implementation, ported from the Awesome Nested Set Active Record plugin.
25
+ email: kondzior.p@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - TODO
32
+ - COPYING
33
+ - README
34
+ files:
35
+ - lib/sequel_nested_set.rb
36
+ - TODO
37
+ - COPYING
38
+ - README
39
+ has_rdoc: true
40
+ homepage: http://sequelns.rubyforge.org/
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --quiet
44
+ - --title
45
+ - Sequel Nested Set
46
+ - --opname
47
+ - index.html
48
+ - --line-numbers
49
+ - --main
50
+ - README
51
+ - --inline-source
52
+ - --charset
53
+ - utf8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.2.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: Nested set implementation for Sequel Models
75
+ test_files:
76
+ - spec/nested_set_spec.rb
77
+ - spec/rcov.opts
78
+ - spec/spec.opts
79
+ - spec/spec_helper.rb