module Ancestry::InstanceMethods

Public Instance Methods

ancestor_conditions() click to toggle source
# File lib/ancestry/instance_methods.rb, line 72
def ancestor_conditions
  {self.base_class.primary_key => ancestor_ids}
end
ancestor_ids() click to toggle source

Ancestors

# File lib/ancestry/instance_methods.rb, line 68
def ancestor_ids
  read_attribute(self.base_class.ancestry_column).to_s.split('/').map { |id| cast_primary_key(id) }
end
ancestors(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 76
def ancestors depth_options = {}
  self.base_class.scope_depth(depth_options, depth).ordered_by_ancestry.scoped :conditions => ancestor_conditions
end
ancestry_callbacks_disabled?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 205
def ancestry_callbacks_disabled?
  !!@disable_ancestry_callbacks
end
ancestry_exclude_self() click to toggle source

Validate that the ancestors don't include itself

# File lib/ancestry/instance_methods.rb, line 4
def ancestry_exclude_self
  add_error_to_base "#{self.class.name.humanize} cannot be a descendant of itself." if ancestor_ids.include? self.id
end
apply_orphan_strategy() click to toggle source

Apply orphan strategy

# File lib/ancestry/instance_methods.rb, line 32
def apply_orphan_strategy
  # Skip this if callbacks are disabled
  unless ancestry_callbacks_disabled?
    # If this isn't a new record ...
    unless new_record?
      # ... make all children root if orphan strategy is rootify
      if self.base_class.orphan_strategy == :rootify
        unscoped_descendants.each do |descendant|
          descendant.without_ancestry_callbacks do
            descendant.update_attribute descendant.class.ancestry_column, (if descendant.ancestry == child_ancestry then nil else descendant.ancestry.gsub(%r^#{child_ancestry}\//, '') end)
          end
        end
      # ... destroy all descendants if orphan strategy is destroy
      elsif self.base_class.orphan_strategy == :destroy
        unscoped_descendants.each do |descendant|
          descendant.without_ancestry_callbacks do
            descendant.destroy
          end
        end
      # ... throw an exception if it has children and orphan strategy is restrict
      elsif self.base_class.orphan_strategy == :restrict
        raise Ancestry::AncestryException.new('Cannot delete record because it has descendants.') unless is_childless?
      end
    end
  end
end
cache_depth() click to toggle source
# File lib/ancestry/instance_methods.rb, line 96
def cache_depth
  write_attribute self.base_class.depth_cache_column, depth
end
child_ancestry() click to toggle source

The ancestry value for this record's children

# File lib/ancestry/instance_methods.rb, line 60
def child_ancestry
  # New records cannot have children
  raise Ancestry::AncestryException.new('No child ancestry for new record. Save record before performing tree operations.') if new_record?

  if self.send("#{self.base_class.ancestry_column}_was").blank? then id.to_s else "#{self.send "#{self.base_class.ancestry_column}_was"}/#{id}" end
end
child_conditions() click to toggle source

Children

# File lib/ancestry/instance_methods.rb, line 131
def child_conditions
  {self.base_class.ancestry_column => child_ancestry}
end
child_ids() click to toggle source
# File lib/ancestry/instance_methods.rb, line 139
def child_ids
  children.all(:select => self.base_class.primary_key).map(&self.base_class.primary_key.to_sym)
end
children() click to toggle source
# File lib/ancestry/instance_methods.rb, line 135
def children
  self.base_class.scoped :conditions => child_conditions
end
depth() click to toggle source
# File lib/ancestry/instance_methods.rb, line 92
def depth
  ancestor_ids.size
end
descendant_conditions() click to toggle source

Descendants

# File lib/ancestry/instance_methods.rb, line 173
def descendant_conditions
  ["#{self.base_class.table_name}.#{self.base_class.ancestry_column} like ? or #{self.base_class.table_name}.#{self.base_class.ancestry_column} = ?", "#{child_ancestry}/%", child_ancestry]
end
descendant_ids(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 181
def descendant_ids depth_options = {}
  descendants(depth_options).all(:select => self.base_class.primary_key).collect(&self.base_class.primary_key.to_sym)
end
descendants(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 177
def descendants depth_options = {}
  self.base_class.ordered_by_ancestry.scope_depth(depth_options, depth).scoped :conditions => descendant_conditions
end
has_children?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 143
def has_children?
  self.children.exists?({})
end
has_siblings?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 164
def has_siblings?
  self.siblings.count > 1
end
is_childless?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 147
def is_childless?
  !has_children?
end
is_only_child?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 168
def is_only_child?
  !has_siblings?
end
is_root?() click to toggle source
# File lib/ancestry/instance_methods.rb, line 126
def is_root?
  read_attribute(self.base_class.ancestry_column).blank?
end
parent() click to toggle source
# File lib/ancestry/instance_methods.rb, line 113
def parent
  if parent_id.blank? then nil else self.base_class.find(parent_id) end
end
parent=(parent) click to toggle source

Parent

# File lib/ancestry/instance_methods.rb, line 101
def parent= parent
  write_attribute(self.base_class.ancestry_column, if parent.blank? then nil else parent.child_ancestry end)
end
parent_id() click to toggle source
# File lib/ancestry/instance_methods.rb, line 109
def parent_id
  if ancestor_ids.empty? then nil else ancestor_ids.last end
end
parent_id=(parent_id) click to toggle source
# File lib/ancestry/instance_methods.rb, line 105
def parent_id= parent_id
  self.parent = if parent_id.blank? then nil else self.base_class.find(parent_id) end
end
path(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 88
def path depth_options = {}
  self.base_class.scope_depth(depth_options, depth).ordered_by_ancestry.scoped :conditions => path_conditions
end
path_conditions() click to toggle source
# File lib/ancestry/instance_methods.rb, line 84
def path_conditions
  {self.base_class.primary_key => path_ids}
end
path_ids() click to toggle source
# File lib/ancestry/instance_methods.rb, line 80
def path_ids
  ancestor_ids + [id]
end
root() click to toggle source
# File lib/ancestry/instance_methods.rb, line 122
def root
  if root_id == id then self else self.base_class.find(root_id) end
end
root_id() click to toggle source

Root

# File lib/ancestry/instance_methods.rb, line 118
def root_id
  if ancestor_ids.empty? then id else ancestor_ids.first end
end
sibling_conditions() click to toggle source

Siblings

# File lib/ancestry/instance_methods.rb, line 152
def sibling_conditions
  {self.base_class.ancestry_column => read_attribute(self.base_class.ancestry_column)}
end
sibling_ids() click to toggle source
# File lib/ancestry/instance_methods.rb, line 160
def sibling_ids
   siblings.all(:select => self.base_class.primary_key).collect(&self.base_class.primary_key.to_sym)
end
siblings() click to toggle source
# File lib/ancestry/instance_methods.rb, line 156
def siblings
  self.base_class.scoped :conditions => sibling_conditions
end
subtree(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 190
def subtree depth_options = {}
  self.base_class.ordered_by_ancestry.scope_depth(depth_options, depth).scoped :conditions => subtree_conditions
end
subtree_conditions() click to toggle source

Subtree

# File lib/ancestry/instance_methods.rb, line 186
def subtree_conditions
  ["#{self.base_class.table_name}.#{self.base_class.primary_key} = ? or #{self.base_class.table_name}.#{self.base_class.ancestry_column} like ? or #{self.base_class.table_name}.#{self.base_class.ancestry_column} = ?", self.id, "#{child_ancestry}/%", child_ancestry]
end
subtree_ids(depth_options = {}) click to toggle source
# File lib/ancestry/instance_methods.rb, line 194
def subtree_ids depth_options = {}
  subtree(depth_options).all(:select => self.base_class.primary_key).collect(&self.base_class.primary_key.to_sym)
end
update_descendants_with_new_ancestry() click to toggle source

Update descendants with new ancestry

# File lib/ancestry/instance_methods.rb, line 9
def update_descendants_with_new_ancestry
  # Skip this if callbacks are disabled
  unless ancestry_callbacks_disabled?
    # If node is valid, not a new record and ancestry was updated ...
    if changed.include?(self.base_class.ancestry_column.to_s) && !new_record? && valid?
      # ... for each descendant ...
      unscoped_descendants.each do |descendant|
        # ... replace old ancestry with new ancestry
        descendant.without_ancestry_callbacks do
          descendant.update_attribute(
            self.base_class.ancestry_column,
            descendant.read_attribute(descendant.class.ancestry_column).gsub(
              %r^#{self.child_ancestry}/,
              if read_attribute(self.class.ancestry_column).blank? then id.to_s else "#{read_attribute self.class.ancestry_column }/#{id}" end
            )
          )
        end
      end
    end
  end
end
without_ancestry_callbacks() { || ... } click to toggle source

Callback disabling

# File lib/ancestry/instance_methods.rb, line 199
def without_ancestry_callbacks
  @disable_ancestry_callbacks = true
  yield
  @disable_ancestry_callbacks = false
end