module Mongoid::Ancestry

Constants

VERSION

Public Instance Methods

ancestor_conditions() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 86
def ancestor_conditions
  { :_id.in => ancestor_ids }
end
ancestor_ids() click to toggle source

Ancestors

# File lib/mongoid-ancestry/instance_methods.rb, line 82
def ancestor_ids
  read_attribute(self.base_class.ancestry_field).to_s.split('/').map { |id| cast_primary_key(id) }
end
ancestors(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 90
def ancestors depth_options = {}
  self.base_class.scope_depth(depth_options, depth).where(ancestor_conditions)
end
ancestry_callbacks_disabled?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 227
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/mongoid-ancestry/instance_methods.rb, line 5
def ancestry_exclude_self
  if ancestor_ids.include? id
    errors.add(:base, "#{self.class.name.humanize} cannot be a descendant of itself.")
  end
end
apply_orphan_strategy() click to toggle source

Apply orphan strategy

# File lib/mongoid-ancestry/instance_methods.rb, line 40
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 al children root if orphan strategy is rootify
      if self.base_class.orphan_strategy == :rootify
        descendants.each do |descendant|
          descendant.without_ancestry_callbacks do
            val = \
              unless descendant.ancestry == child_ancestry
                descendant.read_attribute(descendant.class.ancestry_field).gsub(/^#{child_ancestry}\//, '')
              end
            descendant.update_attribute descendant.class.ancestry_field, val
          end
        end
        # ... destroy all descendants if orphan strategy is destroy
      elsif self.base_class.orphan_strategy == :destroy
        descendants.all.each do |descendant|
          descendant.without_ancestry_callbacks { descendant.destroy }
        end
        # ... throw an exception if it has children and orphan strategy is restrict
      elsif self.base_class.orphan_strategy == :restrict
        raise Error.new('Cannot delete record because it has descendants.') unless is_childless?
      end
    end
  end
end
cache_depth() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 110
def cache_depth
  write_attribute self.base_class.depth_cache_field, depth
end
child_ancestry() click to toggle source

The ancestry value for this record’s children

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

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

Children

# File lib/mongoid-ancestry/instance_methods.rb, line 146
def child_conditions
  {self.base_class.ancestry_field => child_ancestry}
end
child_ids() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 154
def child_ids
  children.only(:_id).map(&:id)
end
children() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 150
def children
  self.base_class.where(child_conditions)
end
depth() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 106
def depth
  ancestor_ids.size
end
descendant_conditions() click to toggle source

Descendants

# File lib/mongoid-ancestry/instance_methods.rb, line 188
def descendant_conditions
  [
    { self.base_class.ancestry_field => /^#{child_ancestry}\// },
    { self.base_class.ancestry_field => child_ancestry }
  ]
end
descendant_ids(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 199
def descendant_ids depth_options = {}
  descendants(depth_options).only(:_id).map(&:id)
end
descendants(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 195
def descendants depth_options = {}
  self.base_class.scope_depth(depth_options, depth).any_of(descendant_conditions)
end
has_children?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 158
def has_children?
  self.children.present?
end
has_siblings?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 179
def has_siblings?
  self.siblings.count > 1
end
is_childless?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 162
def is_childless?
  !has_children?
end
is_only_child?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 183
def is_only_child?
  !has_siblings?
end
is_root?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 141
def is_root?
  read_attribute(self.base_class.ancestry_field).blank?
end
parent() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 128
def parent
  parent_id.blank? ? nil : self.base_class.find(parent_id)
end
parent=(parent) click to toggle source

Parent

# File lib/mongoid-ancestry/instance_methods.rb, line 115
def parent= parent
  write_attribute(self.base_class.ancestry_field, parent.blank? ? nil : parent.child_ancestry)
end
parent_id() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 123
def parent_id
  parent_id = read_attribute(self.base_class.ancestry_field).to_s.split('/').last
  return cast_primary_key(parent_id) if parent_id
end
parent_id=(parent_id) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 119
def parent_id= parent_id
  self.parent = parent_id.blank? ? nil : self.base_class.find(parent_id)
end
path(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 102
def path depth_options = {}
  self.base_class.scope_depth(depth_options, depth).where(path_conditions)
end
path_conditions() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 98
def path_conditions
  { :_id.in => path_ids }
end
path_ids() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 94
def path_ids
  ancestor_ids + [id]
end
root() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 137
def root
  (root_id == id) ? self : self.base_class.find(root_id)
end
root_id() click to toggle source

Root

# File lib/mongoid-ancestry/instance_methods.rb, line 133
def root_id
  ancestor_ids.empty? ? id : ancestor_ids.first
end
sibling_conditions() click to toggle source

Siblings

# File lib/mongoid-ancestry/instance_methods.rb, line 167
def sibling_conditions
  {self.base_class.ancestry_field => read_attribute(self.base_class.ancestry_field)}
end
sibling_ids() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 175
def sibling_ids
  siblings.only(:_id).map(&:id)
end
siblings() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 171
def siblings
  self.base_class.where sibling_conditions
end
subtree(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 212
def subtree depth_options = {}
  self.base_class.scope_depth(depth_options, depth).any_of(subtree_conditions)
end
subtree_conditions() click to toggle source

Subtree

# File lib/mongoid-ancestry/instance_methods.rb, line 204
def subtree_conditions
  [
    { :_id => id },
    { self.base_class.ancestry_field => /^#{child_ancestry}\// },
    { self.base_class.ancestry_field => child_ancestry }
  ]
end
subtree_ids(depth_options = {}) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 216
def subtree_ids depth_options = {}
  subtree(depth_options).only(:_id).map(&:id)
end
touch_parent() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 35
def touch_parent
  parent.touch
end
update_descendants_with_new_ancestry() click to toggle source

Update descendants with new ancestry

# File lib/mongoid-ancestry/instance_methods.rb, line 12
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_field.to_s) && !new_record? && valid?
      # ... for each descendant ...
      descendants.each do |descendant|
        # ... replace old ancestry with new ancestry
        descendant.without_ancestry_callbacks do
          for_replace = \
            if read_attribute(self.class.ancestry_field).blank?
              id.to_s
            else
              "#{read_attribute self.class.ancestry_field}/#{id}"
            end
          new_ancestry = descendant.read_attribute(descendant.class.ancestry_field).gsub(/^#{self.child_ancestry}/, for_replace)
          descendant.update_attribute(self.base_class.ancestry_field, new_ancestry)
        end
      end
    end
  end
end
without_ancestry_callbacks() { || ... } click to toggle source

Callback disabling

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

Private Instance Methods

bson_objectid_from_string(key) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 251
def bson_objectid_from_string(key)
  if Mongoid.mongoid3?
    Moped::BSON::ObjectId.from_string(key)
  else
    BSON::ObjectId.from_string(key)
  end
end
cast_primary_key(key) click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 233
def cast_primary_key(key)
  if primary_key_type == Integer
    key.to_i
  elsif is_primary_key_type_bson_objectid? && key =~ /[a-z0-9]{24}/
    bson_objectid_from_string(key)
  else
    key
  end
end
is_primary_key_type_bson_objectid?() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 243
def is_primary_key_type_bson_objectid?
  if Mongoid.mongoid3?
    primary_key_type == Moped::BSON::ObjectId
  else
    primary_key_type == BSON::ObjectId
  end
end
primary_key_type() click to toggle source
# File lib/mongoid-ancestry/instance_methods.rb, line 259
def primary_key_type
  @primary_key_type ||= self.base_class.fields['_id'].options[:type]
end