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 14, 2020
1 parent 8815eec commit c55a649
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ def vm_and_miq_template_ancestry_save_block
vms_inventory_collection.model_class
.select([:id])
.where(:id => vms_genealogy_parents.keys).find_each do |vm|
parent = parent_miq_templates[vms_genealogy_parents[vm.id]]
vm.with_relationship_type('genealogy') { vm.parent = parent }
vm.with_relationship_type('genealogy') { vm.update!(:parent => parent_miq_templates[vms_genealogy_parents[vm.id]]) }
end
end

Expand All @@ -117,8 +116,7 @@ def vm_and_miq_template_ancestry_save_block
miq_templates_inventory_collection.model_class
.select([:id])
.where(:id => miq_template_genealogy_parents.keys).find_each do |miq_template|
parent = parent_vms[miq_template_genealogy_parents[miq_template.id]]
miq_template.with_relationship_type('genealogy') { miq_template.parent = parent }
miq_template.with_relationship_type('genealogy') { miq_template.update!(:parent => parent_vms[miq_template_genealogy_parents[miq_template.id]]) }
end
end
end
Expand Down
100 changes: 73 additions & 27 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,22 @@ 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 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.flatten != [] && args.flatten != [{}] # ? !args.empty?
bound_method.call(*args)
else
bound_method.call
end
end

def use_ancestry?
relationship_type == :genealogy
end

#
# relationship_type scoping methods
#
Expand Down Expand Up @@ -150,25 +167,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)
return call_ancestry_method(:parent) if use_ancestry?

rels = parent_rels(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1

rels.first.try(:resource)
end

# Returns the class/id pair of the parent of the record, nil for a root node
def parent_id(*args)
return call_ancestry_method(:parent_id) if use_ancestry?

rels = parent_ids(*args).take(2)
raise _("Multiple parents found.") if rels.length > 1

rels.first
end

# Returns the relationship of the root of the tree the record is in
def root_rel(*_args)
def root_rel
rel = relationship.try!(:root)
# micro-optimization: if the relationship is us, "load" the resource
rel.resource = self if rel && rel.resource_id == id && rel.resource_type == self.class.base_class.name.to_s
Expand All @@ -177,16 +201,22 @@ def root_rel(*_args)

# Returns the root of the tree the record is in, self for a root node
def root(*args)
return call_ancestry_method(:root) if use_ancestry?

Relationship.resource(root_rel(*args))
end

# Returns the id of the root of the tree the record is in
def root_id(*args)
return call_ancestry_method(:root_id) if use_ancestry?

Relationship.resource_pair(root_rel(*args))
end

# Returns true if the record is a root node, false otherwise
def is_root?(*_args)
def is_root?
return call_ancestry_method(:is_root?) if use_ancestry?

rel = relationship # TODO: Handle a node that is a root and a node at the same time
rel.nil? ? true : rel.is_root?
end
Expand All @@ -210,6 +240,8 @@ def ancestor_rels(*args)
# Returns a list of ancestor records, starting with the root record and ending
# with the parent record
def ancestors(*args)
return call_ancestry_method(:ancestors, args) if use_ancestry?

Relationship.resources(ancestor_rels(*args))
end

Expand All @@ -236,12 +268,16 @@ 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)
return call_ancestry_method(:path, args) if use_ancestry?

Relationship.resources(path_rels(*args)) # TODO: Prevent preload of self which is in the list
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)
return call_ancestry_method(:path_ids) if use_ancestry?

Relationship.resource_pairs(path_rels(*args))
end

Expand All @@ -259,11 +295,15 @@ def child_rels(*args)

# Returns a list of child records
def children(*args)
return call_ancestry_method(:children) if use_ancestry?

Relationship.resources(child_rels(*args))
end

# Returns a list of child class/id pairs
def child_ids(*args)
return call_ancestry_method(:child_ids) if use_ancestry?

Relationship.resource_pairs(child_rels(*args))
end

Expand All @@ -273,12 +313,16 @@ def child_count(*args)
end

# Returns true if the record has any children, false otherwise
def has_children?(*_args)
def has_children?
return call_ancestry_method(:has_children?) if use_ancestry?

relationships.any?(&:has_children?)
end

# Returns true if the record has no children, false otherwise
def is_childless?(*_args)
def is_childless?
return call_ancestry_method(:is_childless?) if use_ancestry?

relationships.all?(&:is_childless?)
end

Expand All @@ -291,11 +335,15 @@ def sibling_rels(*args)

# Returns a list of sibling records
def siblings(*args)
return call_ancestry_method(:siblings) if use_ancestry?

Relationship.resources(sibling_rels(*args))
end

# Returns a list of sibling class/id pairs
def sibling_ids(*args)
return call_ancestry_method(:sibling_ids) if use_ancestry?

Relationship.resource_pairs(sibling_rels(*args))
end

Expand All @@ -305,12 +353,16 @@ def sibling_count(*args)
end

# Returns true if the record's parent has more than one child
def has_siblings?(*_args)
def has_siblings?
return call_ancestry_method(:has_siblings?) if use_ancestry?

relationships.any?(&:has_siblings?)
end

# Returns true if the record is the only child of its parent
def is_only_child?(*_args)
def is_only_child?
return call_ancestry_method(:is_only_child?) if use_ancestry?

relationships.all?(&:is_only_child?)
end

Expand All @@ -323,11 +375,15 @@ def descendant_rels(*args)

# Returns a list of descendant records
def descendants(*args)
return call_ancestry_method(:descendants, args) if use_ancestry?

Relationship.resources(descendant_rels(*args))
end

# Returns a list of descendant class/id pairs
def descendant_ids(*args)
return call_ancestry_method(:descendant_ids, args) if use_ancestry?

Relationship.resource_pairs(descendant_rels(*args))
end

Expand Down Expand Up @@ -366,11 +422,15 @@ def subtree_rels(*args)

# Returns a list of all records in the record's subtree
def subtree(*args)
return call_ancestry_method(:subtree, args) if use_ancestry?

Relationship.resources(subtree_rels(*args)) # TODO: Prevent preload of self which is in the list
end

# Returns a list of all class/id pairs in the record's subtree
def subtree_ids(*args)
return call_ancestry_method(:subtree_ids, args) if use_ancestry?

Relationship.resource_pairs(subtree_rels(*args))
end

Expand Down Expand Up @@ -418,7 +478,9 @@ def child_and_grandchild_rels(*args)
end

# Return the depth of the node, root nodes are at depth 0
def depth(*_args)
def depth
return call_ancestry_method(:depth) if use_ancestry?

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
Expand Down Expand Up @@ -462,24 +524,6 @@ def init_relationship(parent_rel = nil)
rel
end

# Returns a String form of the ancestor class/id pairs of the record
# Accepts the usual options, plus the options for Relationship.stringify_*,
# as well as :include_self which defaults to false.
def ancestry(*args)
stringify_options = args.extract_options!
options = stringify_options.slice!(:field_delimiter, :record_delimiter, :exclude_class, :field_method, :include_self)

include_self = stringify_options.delete(:include_self)
field_method = stringify_options[:field_method] || :id

meth = include_self ? :path : :ancestors
meth = :"#{meth.to_s.singularize}_ids" if field_method == :id
rels = send(meth, options)

rels_meth = :"stringify_#{field_method == :id ? "resource_pairs" : "rels"}"
Relationship.send(rels_meth, rels, stringify_options)
end

# Returns a list of all relationships in the tree from the root
def fulltree_rels(*args)
options = args.extract_options!
Expand Down Expand Up @@ -565,20 +609,22 @@ def add_children(*child_objs)
alias_method :add_child, :add_children

def parent=(parent)
return call_ancestry_method(:parent=, parent) if use_ancestry?

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
init_relationship(parent_rel) # TODO: Deal with any multi-instances

parent.clear_relationships_cache
end
end

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

#
# Backward compatibility methods
Expand Down
11 changes: 2 additions & 9 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 Expand Up @@ -894,7 +887,7 @@ def reconnect_events

def set_genealogy_parent(parent)
with_relationship_type('genealogy') do
self.parent = parent
update!(:parent => parent)
end
end

Expand Down
16 changes: 8 additions & 8 deletions lib/patches/ancestry_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ def update_descendants_with_new_ancestry
end
end

def parent=(parent)
super
clear_memoized_instance_variables
end
# def parent=(parent)
# super
# clear_memoized_instance_variables
# end

def parent_id=(parent)
super
clear_memoized_instance_variables
end
# def parent_id=(parent)
# super
# clear_memoized_instance_variables
# end
end

module Ancestry
Expand Down
Loading

0 comments on commit c55a649

Please sign in to comment.