module Ancestry::InstanceMethods
Public Instance Methods
ancestor_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 152 def ancestor_of?(node) node.ancestor_ids.include?(self.id) end
ancestors(depth_options = {})
click to toggle source
# File lib/ancestry/instance_methods.rb, line 123 def ancestors depth_options = {} return self.ancestry_base_class.none unless has_parent? self.ancestry_base_class.scope_depth(depth_options, depth).ordered_by_ancestry.ancestors_of(self) end
ancestry_callbacks_disabled?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 304 def ancestry_callbacks_disabled? defined?(@disable_ancestry_callbacks) && @disable_ancestry_callbacks end
ancestry_changed?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 99 def ancestry_changed? column = self.ancestry_base_class.ancestry_column.to_s # These methods return nil if there are no changes. # This was fixed in a refactoring in rails 6.0: https://github.com/rails/rails/pull/35933 !!(will_save_change_to_attribute?(column) || saved_change_to_attribute?(column)) 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 errors.add(:base, I18n.t("ancestry.exclude_self", class_name: self.class.name.humanize)) if ancestor_ids.include? self.id end
apply_orphan_strategy()
click to toggle source
Apply orphan strategy (before destroy - no changes)
# File lib/ancestry/instance_methods.rb, line 24 def apply_orphan_strategy if !ancestry_callbacks_disabled? && !new_record? case self.ancestry_base_class.orphan_strategy when :rootify # make all children root if orphan strategy is rootify unscoped_descendants.each do |descendant| descendant.without_ancestry_callbacks do descendant.update_attribute :ancestor_ids, descendant.ancestor_ids - path_ids end end when :destroy # destroy all descendants if orphan strategy is destroy unscoped_descendants.each do |descendant| descendant.without_ancestry_callbacks do descendant.destroy end end when :adopt # make child elements of this node, child of its parent descendants.each do |descendant| descendant.without_ancestry_callbacks do descendant.update_attribute :ancestor_ids, (descendant.ancestor_ids.delete_if { |x| x == self.id }) end end when :restrict # throw an exception if it has children raise Ancestry::AncestryException.new(I18n.t("ancestry.cannot_delete_descendants")) unless is_childless? end end end
cache_depth()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 148 def cache_depth write_attribute self.ancestry_base_class.depth_cache_column, depth end
child_ids()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 214 def child_ids children.pluck(self.ancestry_base_class.primary_key) end
child_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 228 def child_of?(node) self.parent_id == node.id end
children()
click to toggle source
Children
# File lib/ancestry/instance_methods.rb, line 210 def children self.ancestry_base_class.children_of(self) end
decrease_parent_counter_cache()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 68 def decrease_parent_counter_cache # @_trigger_destroy_callback comes from activerecord, which makes sure only once decrement when concurrent deletion. # but @_trigger_destroy_callback began after rails@5.1.0.alpha. # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/persistence.rb#L340 # https://github.com/rails/rails/pull/14735 # https://github.com/rails/rails/pull/27248 return if defined?(@_trigger_destroy_callback) && !@_trigger_destroy_callback return if ancestry_callbacks_disabled? self.ancestry_base_class.decrement_counter counter_cache_column, parent_id end
depth()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 144 def depth ancestor_ids.size end
descendant_ids(depth_options = {})
click to toggle source
# File lib/ancestry/instance_methods.rb, line 263 def descendant_ids depth_options = {} descendants(depth_options).pluck(self.ancestry_base_class.primary_key) end
descendant_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 267 def descendant_of?(node) ancestor_ids.include?(node.id) end
descendants(depth_options = {})
click to toggle source
Descendants
# File lib/ancestry/instance_methods.rb, line 259 def descendants depth_options = {} self.ancestry_base_class.ordered_by_ancestry.scope_depth(depth_options, depth).descendants_of(self) end
has_children?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 218 def has_children? self.children.exists? end
Also aliased as: children?
has_parent?()
click to toggle source
Ancestors
# File lib/ancestry/instance_methods.rb, line 94 def has_parent? ancestor_ids.present? end
Also aliased as: ancestors?
has_siblings?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 243 def has_siblings? self.siblings.count > 1 end
Also aliased as: siblings?
increase_parent_counter_cache()
click to toggle source
Counter Cache
# File lib/ancestry/instance_methods.rb, line 64 def increase_parent_counter_cache self.ancestry_base_class.increment_counter counter_cache_column, parent_id end
indirect_ids(depth_options = {})
click to toggle source
# File lib/ancestry/instance_methods.rb, line 277 def indirect_ids depth_options = {} indirects(depth_options).pluck(self.ancestry_base_class.primary_key) end
indirect_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 281 def indirect_of?(node) ancestor_ids[0..-2].include?(node.id) end
indirects(depth_options = {})
click to toggle source
Indirects
# File lib/ancestry/instance_methods.rb, line 273 def indirects depth_options = {} self.ancestry_base_class.ordered_by_ancestry.scope_depth(depth_options, depth).indirects_of(self) end
is_childless?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 223 def is_childless? !has_children? end
Also aliased as: childless?
is_only_child?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 248 def is_only_child? !has_siblings? end
Also aliased as: only_child?
is_root?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 199 def is_root? !has_parent? end
Also aliased as: root?
parent()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 173 def parent if has_parent? unscoped_where do |scope| scope.find_by scope.primary_key => parent_id end end end
parent=(parent)
click to toggle source
currently parent= does not work in after save callbacks assuming that parent hasn't changed
# File lib/ancestry/instance_methods.rb, line 160 def parent= parent self.ancestor_ids = parent ? parent.path_ids : [] end
parent_id()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 168 def parent_id ancestor_ids.last if has_parent? end
parent_id=(new_parent_id)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 164 def parent_id= new_parent_id self.parent = new_parent_id.present? ? unscoped_find(new_parent_id) : nil end
parent_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 181 def parent_of?(node) self.id == node.parent_id end
path(depth_options = {})
click to toggle source
# File lib/ancestry/instance_methods.rb, line 140 def path depth_options = {} self.ancestry_base_class.scope_depth(depth_options, depth).ordered_by_ancestry.inpath_of(self) end
path_ids()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 128 def path_ids ancestor_ids + [id] end
path_ids_before_last_save()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 132 def path_ids_before_last_save ancestor_ids_before_last_save + [id] end
path_ids_in_database()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 136 def path_ids_in_database ancestor_ids_in_database + [id] end
root()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 191 def root if has_parent? unscoped_where { |scope| scope.find_by(scope.primary_key => root_id) } || self else self end end
root_id()
click to toggle source
Root
# File lib/ancestry/instance_methods.rb, line 187 def root_id has_parent? ? ancestor_ids.first : id end
root_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 204 def root_of?(node) self.id == node.root_id end
sane_ancestor_ids?()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 106 def sane_ancestor_ids? current_context, self.validation_context = validation_context, nil errors.clear attribute = ancestry_base_class.ancestry_column ancestry_value = send(attribute) return true unless ancestry_value self.class.validators_on(attribute).each do |validator| validator.validate_each(self, attribute, ancestry_value) end ancestry_exclude_self errors.none? ensure self.validation_context = current_context end
sibling_ids()
click to toggle source
NOTE: includes self
# File lib/ancestry/instance_methods.rb, line 239 def sibling_ids siblings.pluck(self.ancestry_base_class.primary_key) end
sibling_of?(node)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 253 def sibling_of?(node) self.ancestor_ids == node.ancestor_ids end
siblings()
click to toggle source
Siblings
# File lib/ancestry/instance_methods.rb, line 234 def siblings self.ancestry_base_class.siblings_of(self) end
subtree(depth_options = {})
click to toggle source
Subtree
# File lib/ancestry/instance_methods.rb, line 287 def subtree depth_options = {} self.ancestry_base_class.ordered_by_ancestry.scope_depth(depth_options, depth).subtree_of(self) end
subtree_ids(depth_options = {})
click to toggle source
# File lib/ancestry/instance_methods.rb, line 291 def subtree_ids depth_options = {} subtree(depth_options).pluck(self.ancestry_base_class.primary_key) end
touch_ancestors_callback()
click to toggle source
Touch each of this record's ancestors (after save)
# File lib/ancestry/instance_methods.rb, line 52 def touch_ancestors_callback if !ancestry_callbacks_disabled? && self.ancestry_base_class.touch_ancestors # Touch each of the old *and* new ancestors unscoped_current_and_previous_ancestors.each do |ancestor| ancestor.without_ancestry_callbacks do ancestor.touch end end end end
update_descendants_with_new_ancestry()
click to toggle source
Update descendants with new ancestry (after update)
# File lib/ancestry/instance_methods.rb, line 9 def update_descendants_with_new_ancestry # If enabled and node is existing and ancestry was updated and the new ancestry is sane ... if !ancestry_callbacks_disabled? && !new_record? && ancestry_changed? && sane_ancestor_ids? # ... for each descendant ... unscoped_descendants_before_save.each do |descendant| # ... replace old ancestry with new ancestry descendant.without_ancestry_callbacks do new_ancestor_ids = path_ids + (descendant.ancestor_ids - path_ids_before_last_save) descendant.update_attribute(:ancestor_ids, new_ancestor_ids) end end end end
update_parent_counter_cache()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 80 def update_parent_counter_cache changed = saved_change_to_attribute?(self.ancestry_base_class.ancestry_column) return unless changed if parent_id_was = parent_id_before_last_save self.ancestry_base_class.decrement_counter counter_cache_column, parent_id_was end parent_id && increase_parent_counter_cache end
without_ancestry_callbacks() { || ... }
click to toggle source
Callback disabling
# File lib/ancestry/instance_methods.rb, line 297 def without_ancestry_callbacks @disable_ancestry_callbacks = true yield ensure @disable_ancestry_callbacks = false end
Private Instance Methods
unscoped_current_and_previous_ancestors()
click to toggle source
works with after save context (hence before_last_save)
# File lib/ancestry/instance_methods.rb, line 322 def unscoped_current_and_previous_ancestors unscoped_where do |scope| scope.where scope.primary_key => (ancestor_ids + ancestor_ids_before_last_save).uniq end end
unscoped_descendants()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 309 def unscoped_descendants unscoped_where do |scope| scope.where self.ancestry_base_class.descendant_conditions(self) end end
unscoped_descendants_before_save()
click to toggle source
# File lib/ancestry/instance_methods.rb, line 315 def unscoped_descendants_before_save unscoped_where do |scope| scope.where self.ancestry_base_class.descendant_before_save_conditions(self) end end
unscoped_find(id)
click to toggle source
# File lib/ancestry/instance_methods.rb, line 328 def unscoped_find id unscoped_where do |scope| scope.find id end end
unscoped_where() { |scope| ... }
click to toggle source
# File lib/ancestry/instance_methods.rb, line 334 def unscoped_where self.ancestry_base_class.unscoped_where do |scope| yield scope end end