Skip to content

Commit

Permalink
add binding to determine which parent method to use
Browse files Browse the repository at this point in the history
based on the value of default_relationship_type we need to either
set an ancestry parent or a relationship parent
  • Loading branch information
d-m-u committed Jun 13, 2020
1 parent 8815eec commit 3744bce
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 69 deletions.
210 changes: 157 additions & 53 deletions app/models/mixins/relationship_mixin.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'memoist'
require 'ancestry'

module RelationshipMixin
extend ActiveSupport::Concern
Expand Down Expand Up @@ -54,6 +55,34 @@ def clear_relationships_cache(*args)
@association_cache.delete(:all_relationships)
end

# this allows us to call similarly named ancestry or mixin methods
# depending on whether default_relationship_type is set to :genealogy or not

def with_relationship_type(rel)
old_rel, @rel = @rel, rel
rel_changed = rel && (relationship_type != rel)
self.relationship_type = rel unless rel.nil?
begin
yield
ensure
if rel_changed
relationship_types.pop
clear_relationships_cache(:except => :relationships_of)
end
end
ensure
@rel = old_rel
end

def call_ancestry_method(method_name, *args)
bound_method = ((@@ancestry_method ||= {})[method_name] ||= ::Ancestry::InstanceMethods.instance_method(method_name)).bind(self)
if !args.first.nil? # ? !args.empty?
bound_method.call(*args)
else
bound_method.call
end
end

#
# relationship_type scoping methods
#
Expand All @@ -74,22 +103,6 @@ def relationship_type=(rel)
rel
end

def with_relationship_type(rel)
raise _("no block given") unless block_given?

rel_changed = rel && (relationship_type != rel)
self.relationship_type = rel unless rel.nil?

begin
yield(self)
ensure
if rel_changed
relationship_types.pop
clear_relationships_cache(:except => :relationships_of)
end
end
end

def relationships_of(rel_type)
if @association_cache.include?(:all_relationships)
all_relationships.select { |r| r.relationship == rel_type }
Expand Down Expand Up @@ -150,21 +163,32 @@ def parent_count(*args)
def parent_rel(*args)
rels = parent_rels(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1

rels.first
end

# Returns the parent of the record, nil for a root node
def parent(*args)
rels = parent_rels(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1
rels.first.try(:resource)
if @relationship_type == :genealogy
call_ancestry_method(:parent, binding)
else
rels = parent_rels(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1

rels.first.try(:resource)
end
end

# Returns the class/id pair of the parent of the record, nil for a root node
def parent_id(*args)
rels = parent_ids(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1
rels.first
if @relationship_type == :genealogy
call_ancestry_method(:parent_id, binding)
else
rels = parent_ids(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1

rels.first
end
end

# Returns the relationship of the root of the tree the record is in
Expand All @@ -177,18 +201,30 @@ def root_rel(*_args)

# Returns the root of the tree the record is in, self for a root node
def root(*args)
Relationship.resource(root_rel(*args))
if @relationship_type == :genealogy
call_ancestry_method(:root, binding)
else
Relationship.resource(root_rel(*args))
end
end

# Returns the id of the root of the tree the record is in
def root_id(*args)
Relationship.resource_pair(root_rel(*args))
if @relationship_type == :genealogy
call_ancestry_method(:root, binding)
else
Relationship.resource_pair(root_rel(*args))
end
end

# Returns true if the record is a root node, false otherwise
def is_root?(*_args)
rel = relationship # TODO: Handle a node that is a root and a node at the same time
rel.nil? ? true : rel.is_root?
if @relationship_type == :genealogy
call_ancestry_method(:is_root?, binding)
else
rel = relationship # TODO: Handle a node that is a root and a node at the same time
rel.nil? ? true : rel.is_root?
end
end

# Returns a relationship for a record that is a root node with no corresponding
Expand All @@ -210,7 +246,11 @@ def ancestor_rels(*args)
# Returns a list of ancestor records, starting with the root record and ending
# with the parent record
def ancestors(*args)
Relationship.resources(ancestor_rels(*args))
if @association_cache.include?(:all_relationships)
all_relationships.select { |r| r.relationship == rel_type }
else
Relationship.resources(ancestor_rels(*args))
end
end

# Returns a list of ancestor class/id pairs, starting with the root class/id
Expand All @@ -236,13 +276,21 @@ def path_rels(*args)
# Returns a list of the path records, starting with the root record and ending
# with the node's own record
def path(*args)
Relationship.resources(path_rels(*args)) # TODO: Prevent preload of self which is in the list
if @relationship_type == :genealogy
call_ancestry_method(:path, binding)
else
Relationship.resources(path_rels(*args)) # TODO: Prevent preload of self which is in the list
end
end

# Returns a list of the path class/id pairs, starting with the root class/id
# and ending with the node's own class/id
def path_ids(*args)
Relationship.resource_pairs(path_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:path_ids, binding)
else
Relationship.resource_pairs(path_rels(*args))
end
end

# Returns the number of records in the path
Expand All @@ -259,12 +307,20 @@ def child_rels(*args)

# Returns a list of child records
def children(*args)
Relationship.resources(child_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:children, binding)
else
Relationship.resources(child_rels(*args))
end
end

# Returns a list of child class/id pairs
def child_ids(*args)
Relationship.resource_pairs(child_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:child_ids, binding)
else
Relationship.resource_pairs(child_rels(*args))
end
end

# Returns the number of child records
Expand All @@ -274,12 +330,20 @@ def child_count(*args)

# Returns true if the record has any children, false otherwise
def has_children?(*_args)
relationships.any?(&:has_children?)
if @relationship_type == :genealogy
call_ancestry_method(:has_children?, binding)
else
relationships.any?(&:has_children?)
end
end

# Returns true if the record has no children, false otherwise
def is_childless?(*_args)
relationships.all?(&:is_childless?)
if @relationship_type == :genealogy
call_ancestry_method(:is_childless?, binding)
else
relationships.all?(&:is_childless?)
end
end

# Returns a list of sibling relationships
Expand All @@ -291,12 +355,20 @@ def sibling_rels(*args)

# Returns a list of sibling records
def siblings(*args)
Relationship.resources(sibling_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:siblings, binding)
else
Relationship.resources(sibling_rels(*args))
end
end

# Returns a list of sibling class/id pairs
def sibling_ids(*args)
Relationship.resource_pairs(sibling_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:sibling_ids, binding)
else
Relationship.resource_pairs(sibling_rels(*args))
end
end

# Returns the number of sibling records
Expand All @@ -306,12 +378,20 @@ def sibling_count(*args)

# Returns true if the record's parent has more than one child
def has_siblings?(*_args)
relationships.any?(&:has_siblings?)
if @relationship_type == :genealogy
call_ancestry_method(:has_siblings?, binding)
else
relationships.any?(&:has_siblings?)
end
end

# Returns true if the record is the only child of its parent
def is_only_child?(*_args)
relationships.all?(&:is_only_child?)
if @relationship_type == :genealogy
call_ancestry_method(:only_child?, binding)
else
relationships.all?(&:is_only_child?)
end
end

# Returns a list of descendant relationships
Expand All @@ -323,12 +403,20 @@ def descendant_rels(*args)

# Returns a list of descendant records
def descendants(*args)
Relationship.resources(descendant_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:descendants, binding)
else
Relationship.resources(descendant_rels(*args))
end
end

# Returns a list of descendant class/id pairs
def descendant_ids(*args)
Relationship.resource_pairs(descendant_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:descendant_ids, binding)
else
Relationship.resource_pairs(descendant_rels(*args))
end
end

# Returns the number of descendant records
Expand Down Expand Up @@ -366,12 +454,20 @@ def subtree_rels(*args)

# Returns a list of all records in the record's subtree
def subtree(*args)
Relationship.resources(subtree_rels(*args)) # TODO: Prevent preload of self which is in the list
if @relationship_type == :genealogy
call_ancestry_method(:subtree, binding)
else
Relationship.resources(subtree_rels(*args)) # TODO: Prevent preload of self which is in the list
end
end

# Returns a list of all class/id pairs in the record's subtree
def subtree_ids(*args)
Relationship.resource_pairs(subtree_rels(*args))
if @relationship_type == :genealogy
call_ancestry_method(:subtree_ids, binding)
else
Relationship.resource_pairs(subtree_rels(*args))
end
end

# Returns the number of records in the record's subtree
Expand Down Expand Up @@ -419,8 +515,12 @@ def child_and_grandchild_rels(*args)

# Return the depth of the node, root nodes are at depth 0
def depth(*_args)
rel = relationship(:raise_on_multiple => true) # TODO: Handle multiple nodes with a way to detect which node you want
rel.nil? ? 0 : rel.depth
if @relationship_type == :genealogy
call_ancestry_method(:depth, binding)
else
rel = relationship(:raise_on_multiple => true) # TODO: Handle multiple nodes with a way to detect which node you want
rel.nil? ? 0 : rel.depth
end
end

#
Expand Down Expand Up @@ -565,20 +665,24 @@ def add_children(*child_objs)
alias_method :add_child, :add_children

def parent=(parent)
if parent.nil?
remove_all_parents
if @relationship_type == :genealogy
call_ancestry_method(:parent=, binding)
else
parent.with_relationship_type(relationship_type) do
parent_rel = parent.init_relationship
init_relationship(parent_rel) # TODO: Deal with any multi-instances

parent.clear_relationships_cache
if parent.nil?
remove_all_parents
else
parent.with_relationship_type(relationship_type) do
parent_rel = parent.init_relationship
init_relationship(parent_rel) # TODO: Deal with any multi-instances

parent.clear_relationships_cache
end
end
end

clear_relationships_cache
clear_relationships_cache
end
end
alias_method :replace_parent, :parent=
alias replace_parent parent=

#
# Backward compatibility methods
Expand Down
9 changes: 1 addition & 8 deletions app/models/vm_or_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ class VmOrTemplate < ApplicationRecord
delegate :connect_lans, :disconnect_lans, :to => :hardware, :allow_nil => true
delegate :queue_name_for_ems_operations, :to => :ext_management_system, :allow_nil => true

after_save :save_genealogy_information

scope :active, -> { where.not(:ems_id => nil) }
scope :with_type, ->(type) { where(:type => type) }
scope :archived, -> { where(:ems_id => nil, :storage_id => nil) }
Expand Down Expand Up @@ -282,6 +280,7 @@ def from_infra_manager?
virtual_column m, :type => :string, :uses => :all_relationships
end

has_ancestry
include RelationshipMixin
self.default_relationship_type = "genealogy"

Expand Down Expand Up @@ -531,12 +530,6 @@ def genealogy_parent=(parent)
@genealogy_parent_object = parent
end

def save_genealogy_information
if defined?(@genealogy_parent_object) && @genealogy_parent_object
with_relationship_type('genealogy') { self.parent = @genealogy_parent_object }
end
end

def os_image_name
name = OperatingSystem.image_name(self)
if name == 'unknown'
Expand Down
Loading

0 comments on commit 3744bce

Please sign in to comment.