pkondzior-sequel_nested_set 0.9.9

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/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