class PaperTrail::RelatedChanges::Hierarchy

Constants

Node
SelfRelation

Attributes

model[R]

Public Class Methods

build(*args) click to toggle source

Builds downward relational hierarchy with 4 generations

# File lib/paper_trail/related_changes/hierarchy.rb, line 8
def build(*args)
  self.new(*args).send(:build_source)
end
model_type_children(model) click to toggle source

Does filtering of relations

# File lib/paper_trail/related_changes/hierarchy.rb, line 13
def model_type_children(model)
  self.new(model).model_type_children
end
new(model) click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 20
def initialize(model)
  @model = model
end

Public Instance Methods

find_by_id(id) click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 24
def find_by_id(id)
  Query.call(model, id)
end
model_type_children() click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 49
def model_type_children
  @model_type_children ||= model.reflections.each_with_object({}) do |(name, rel), result|
    next if source_reflection(rel).belongs_to? # Only Looking for downward relations. (Children not parents)
    next if name == PaperTrail::Version.table_name
    result[name.to_sym] = source_reflection(rel)
  end
end
search_hierarchy(item_type, source: send(:source), parent: nil) click to toggle source

Find the node that matches the item_type The result has a parent reference instead of a child reference.

# File lib/paper_trail/related_changes/hierarchy.rb, line 30
def search_hierarchy(item_type, source: send(:source), parent: nil)
  parent_without_children = (parent || source).except(:children)
  return [source.except(:children).merge(parent: parent_without_children)] if source[:type] == item_type
  return if source[:children].empty?
  source[:children].flat_map { |child| search_hierarchy(item_type, source: child, parent: source) }.compact
end
shared_relation(versions) click to toggle source

Finds a common root between versions

# File lib/paper_trail/related_changes/hierarchy.rb, line 38
def shared_relation(versions)
  result = versions.map do |version|
    [
      version.item_type, # It can be it's self
      *search_hierarchy(version.item_type).map { |s| s.fetch(:parent, {}).fetch(:type, s[:type]) }
    ].uniq
  end.inject(:&)&.last

  search_hierarchy(result)&.first
end
source() click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 57
def source
  @source ||= build_source
end

Private Instance Methods

build_source() click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 63
def build_source
  top_relation = SelfRelation.new(foreign_key: :id, class_name: model.name, name: model.name)
  root         = Node.new(type: model.name, name: model.name, relation: top_relation, children: [])
  model_type_children.each do |n1, r1|
    next if r1.klass == model

    r1_branch = Node.new(type: r1.klass.name, name: n1, relation: r1, children: [])
    root.children << r1_branch

    self.class.model_type_children(r1.klass).each do |n2, r2|
      next if r2.klass == model

      r2_branch = Node.new(type: r2.klass.name, name: n2, relation: r2, children: [])
      r1_branch.children << r2_branch

      self.class.model_type_children(r2.klass).each do |n3, r3|
        next if r3.klass == model

        r3_branch = Node.new(type: r3.klass.name, name: n3, relation: r3, children: [])
        r2_branch.children << r3_branch
      end
    end
  end
  root
end
include_parent?(name) click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 93
def include_parent?(name)
  include_parent_as_child.include?(name.to_sym)
end
source_reflection(v) click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 89
def source_reflection(v)
  v.through_reflection? ? source_reflection(v.through_reflection) : v
end
whitelisted_child?(name) click to toggle source
# File lib/paper_trail/related_changes/hierarchy.rb, line 97
def whitelisted_child?(name)
  return true unless only_include_children

  only_include_children.include?(name.to_sym)
end