Module: TinySupport::ActiveRecord::NestedSet::Model

Extended by:
ActiveSupport::Concern
Defined in:
lib/tiny_support/active_record/nested_set.rb

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#ancestorsObject

Returns an array of all parents



396
397
398
# File 'lib/tiny_support/active_record/nested_set.rb', line 396

def ancestors
  without_self self_and_ancestors
end

#child?Boolean

Returns true is this is a child node

Returns:

  • (Boolean)


371
372
373
# File 'lib/tiny_support/active_record/nested_set.rb', line 371

def child?
  !root?
end

#descendantsObject

Returns a set of all of its children and nested children



430
431
432
# File 'lib/tiny_support/active_record/nested_set.rb', line 430

def descendants
  without_self self_and_descendants
end

#is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


442
443
444
# File 'lib/tiny_support/active_record/nested_set.rb', line 442

def is_ancestor_of?(other)
  self.left < other.left && other.left < self.right && same_scope?(other)
end

#is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


434
435
436
# File 'lib/tiny_support/active_record/nested_set.rb', line 434

def is_descendant_of?(other)
  other.left < self.left && self.left < other.right && same_scope?(other)
end

#is_or_is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


446
447
448
# File 'lib/tiny_support/active_record/nested_set.rb', line 446

def is_or_is_ancestor_of?(other)
  self.left <= other.left && other.left < self.right && same_scope?(other)
end

#is_or_is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


438
439
440
# File 'lib/tiny_support/active_record/nested_set.rb', line 438

def is_or_is_descendant_of?(other)
  other.left <= self.left && self.left < other.right && same_scope?(other)
end

#leaf?Boolean

Returns true if this is the end of a branch.

Returns:

  • (Boolean)


366
367
368
# File 'lib/tiny_support/active_record/nested_set.rb', line 366

def leaf?
  persisted? && right.to_i - left.to_i == 1
end

#leavesObject

Returns a set of all of its nested children which do not have children



411
412
413
# File 'lib/tiny_support/active_record/nested_set.rb', line 411

def leaves
  descendants.where("#{quoted_right_column_full_name} - #{quoted_left_column_full_name} = 1")
end

#leftObject

Value of the left column



351
352
353
# File 'lib/tiny_support/active_record/nested_set.rb', line 351

def left
  self[left_column_name]
end

#left_siblingObject

Find the first sibling to the left



458
459
460
461
# File 'lib/tiny_support/active_record/nested_set.rb', line 458

def left_sibling
  siblings.where(["#{quoted_left_column_full_name} < ?", left]).
    order("#{quoted_left_column_full_name} DESC").last
end

#levelObject

Returns the level of this object in the tree root level is 0



417
418
419
# File 'lib/tiny_support/active_record/nested_set.rb', line 417

def level
  parent_id.nil? ? 0 : compute_level
end

#move_leftObject

Shorthand method for finding the left sibling and moving to the left of it.



469
470
471
# File 'lib/tiny_support/active_record/nested_set.rb', line 469

def move_left
  move_to_left_of left_sibling
end

#move_possible?(target) ⇒ Boolean

Returns:

  • (Boolean)


531
532
533
534
535
536
537
# File 'lib/tiny_support/active_record/nested_set.rb', line 531

def move_possible?(target)
  self != target && # Can't target self
    same_scope?(target) && # can't be in different scopes
    # !(left..right).include?(target.left..target.right) # this needs tested more
    # detect impossible move
    !((left <= target.left && right >= target.left) or (left <= target.right && right >= target.right))
end

#move_rightObject

Shorthand method for finding the right sibling and moving to the right of it.



474
475
476
# File 'lib/tiny_support/active_record/nested_set.rb', line 474

def move_right
  move_to_right_of right_sibling
end

#move_to_child_of(node) ⇒ Object

Move the node to the child of another node (you can pass id only)



489
490
491
# File 'lib/tiny_support/active_record/nested_set.rb', line 489

def move_to_child_of(node)
  move_to node, :child
end

#move_to_child_with_index(node, index) ⇒ Object

Move the node to the child of another node with specify index (you can pass id only)



494
495
496
497
498
499
500
501
502
# File 'lib/tiny_support/active_record/nested_set.rb', line 494

def move_to_child_with_index(node, index)
  if node.children.empty?
    move_to_child_of(node)
  elsif node.children.count == index
    move_to_right_of(node.children.last)
  else
    move_to_left_of(node.children[index])
  end
end

#move_to_left_of(node) ⇒ Object

Move the node to the left of another node (you can pass id only)



479
480
481
# File 'lib/tiny_support/active_record/nested_set.rb', line 479

def move_to_left_of(node)
  move_to node, :left
end

#move_to_ordered_child_of(parent, order_attribute, ascending = true) ⇒ Object

Order children in a nested set by an attribute Can order by any attribute class that uses the Comparable mixin, for example a string or integer Usage example when sorting categories alphabetically: @new_category.move_to_ordered_child_of(@root, “name”)



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/tiny_support/active_record/nested_set.rb', line 512

def move_to_ordered_child_of(parent, order_attribute, ascending = true)
  self.move_to_root and return unless parent
  left = nil # This is needed, at least for the tests.
  parent.children.each do |n| # Find the node immediately to the left of this node.
    if ascending
      left = n if n.send(order_attribute) < self.send(order_attribute)
    else
      left = n if n.send(order_attribute) > self.send(order_attribute)
    end
  end
  self.move_to_child_of(parent)
  return unless parent.children.count > 1 # Only need to order if there are multiple children.
  if left # Self has a left neighbor.
    self.move_to_right_of(left)
  else # Self is the left most node.
    self.move_to_left_of(parent.children[0])
  end
end

#move_to_right_of(node) ⇒ Object

Move the node to the left of another node (you can pass id only)



484
485
486
# File 'lib/tiny_support/active_record/nested_set.rb', line 484

def move_to_right_of(node)
  move_to node, :right
end

#move_to_rootObject

Move the node to root nodes



505
506
507
# File 'lib/tiny_support/active_record/nested_set.rb', line 505

def move_to_root
  move_to nil, :root
end

#name_with_levelObject



333
334
335
# File 'lib/tiny_support/active_record/nested_set.rb', line 333

def name_with_level
  "#{'- ' * self.level}#{self.name}"
end

#nested_set_options(options = {}, &block) ⇒ Object



337
338
339
# File 'lib/tiny_support/active_record/nested_set.rb', line 337

def nested_set_options options={}, &block
  self.class.nested_set_options({:items => self}.merge(options), &block)
end

#parent_idObject

Any instance method that returns a collection makes use of Rails 2.1’s named_scope (which is bundled for Rails 2.0), so it can be treated as a finder.

category.self_and_descendants.count
category.ancestors.find(:all, :conditions => "name like '%foo%'")

Value of the parent column



346
347
348
# File 'lib/tiny_support/active_record/nested_set.rb', line 346

def parent_id
  self[parent_column_name]
end

#rightObject

Value of the right column



356
357
358
# File 'lib/tiny_support/active_record/nested_set.rb', line 356

def right
  self[right_column_name]
end

#right_siblingObject

Find the first sibling to the right



464
465
466
# File 'lib/tiny_support/active_record/nested_set.rb', line 464

def right_sibling
  siblings.where(["#{quoted_left_column_full_name} > ?", left]).first
end

#rootObject

Returns root



376
377
378
379
380
381
382
383
384
385
386
# File 'lib/tiny_support/active_record/nested_set.rb', line 376

def root
  if persisted?
    self_and_ancestors.where(parent_column_name => nil).first
  else
    if parent_id && current_parent = nested_set_scope.find(parent_id)
      current_parent.root
    else
      self
    end
  end
end

#root?Boolean

Returns true if this is a root node.

Returns:

  • (Boolean)


361
362
363
# File 'lib/tiny_support/active_record/nested_set.rb', line 361

def root?
  parent_id.nil?
end

#same_scope?(other) ⇒ Boolean

Check if other model is in the same scope

Returns:

  • (Boolean)


451
452
453
454
455
# File 'lib/tiny_support/active_record/nested_set.rb', line 451

def same_scope?(other)
  Array(acts_as_nested_set_options[:scope]).all? do |attr|
    self.send(attr) == other.send(attr)
  end
end

#self_and_ancestorsObject

Returns the array of all parents and self



389
390
391
392
393
# File 'lib/tiny_support/active_record/nested_set.rb', line 389

def self_and_ancestors
  nested_set_scope.where([
    "#{quoted_left_column_full_name} <= ? AND #{quoted_right_column_full_name} >= ?", left, right
  ])
end

#self_and_descendantsObject

Returns a set of itself and all of its nested children



422
423
424
425
426
427
# File 'lib/tiny_support/active_record/nested_set.rb', line 422

def self_and_descendants
  nested_set_scope.where([
    "#{quoted_left_column_full_name} >= ? AND #{quoted_left_column_full_name} < ?", left, right
    # using _left_ for both sides here lets us benefit from an index on that column if one exists
  ])
end

#self_and_siblingsObject

Returns the array of all children of the parent, including self



401
402
403
# File 'lib/tiny_support/active_record/nested_set.rb', line 401

def self_and_siblings
  nested_set_scope.where(parent_column_name => parent_id)
end

#siblingsObject

Returns the array of all children of the parent, except self



406
407
408
# File 'lib/tiny_support/active_record/nested_set.rb', line 406

def siblings
  without_self self_and_siblings
end

#to_textObject



539
540
541
542
543
# File 'lib/tiny_support/active_record/nested_set.rb', line 539

def to_text
  self_and_descendants.map do |node|
    "#{'*'*(node.level+1)} #{node.id} #{node.to_s} (#{node.parent_id}, #{node.left}, #{node.right})"
  end.join("\n")
end