diff --git a/app/models/orchestration_stack.rb b/app/models/orchestration_stack.rb index a74d36ab56f..4c7b333e4aa 100644 --- a/app/models/orchestration_stack.rb +++ b/app/models/orchestration_stack.rb @@ -1,4 +1,6 @@ require 'ancestry' +require 'ancestry_patch' + class OrchestrationStack < ApplicationRecord require_nested :Status diff --git a/app/models/relationship.rb b/app/models/relationship.rb index 23c9846c22c..9babad135e6 100644 --- a/app/models/relationship.rb +++ b/app/models/relationship.rb @@ -1,4 +1,5 @@ require 'ancestry' +require 'ancestry_patch' class Relationship < ApplicationRecord has_ancestry diff --git a/app/models/service.rb b/app/models/service.rb index 0ff733d5789..371969597a9 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -1,4 +1,5 @@ require 'ancestry' +require 'ancestry_patch' class Service < ApplicationRecord DEFAULT_PROCESS_DELAY_BETWEEN_GROUPS = 120 diff --git a/app/models/tenant.rb b/app/models/tenant.rb index 851cb12c25c..b0b3e5f59b6 100644 --- a/app/models/tenant.rb +++ b/app/models/tenant.rb @@ -1,4 +1,5 @@ require 'ancestry' +require 'ancestry_patch' class Tenant < ApplicationRecord HARDCODED_LOGO = "custom_logo.png" diff --git a/lib/patches/ancestry_patch.rb b/lib/patches/ancestry_patch.rb new file mode 100644 index 00000000000..c8888d34676 --- /dev/null +++ b/lib/patches/ancestry_patch.rb @@ -0,0 +1,70 @@ +module AncestryInstanceMethodsPatch + def update_descendants_with_new_ancestry + super + unless ancestry_callbacks_disabled? + clear_memoized_instance_variables + if ancestry_changed? && !new_record? && sane_ancestry? + unscoped_descendants.each(&:clear_memoized_instance_variables) + end + end + end +end + +module Ancestry + module InstanceMethods + prepend AncestryInstanceMethodsPatch + + ANCESTRY_DELIMITER = '/'.freeze + + def parse_ancestry_column(obj) + obj.to_s.split(ANCESTRY_DELIMITER).map! { |id| cast_primary_key(id) } + end + + def ancestor_ids + @_ancestor_ids ||= parse_ancestry_column(read_attribute(ancestry_base_class.ancestry_column)) + end + + def parent_id + return @_parent_id if defined?(@_parent_id) + @_parent_id = if @_ancestor_ids + @_ancestor_ids.empty? ? nil : @_ancestor_ids.last + else + col = read_attribute(ancestry_base_class.ancestry_column) + # Specifically not using `.blank?` here because it is + # slower than doing the below. + if col.nil? || col.empty? # rubocop:disable Rails/Blank + nil + else + rindex = col.rindex(ANCESTRY_DELIMITER) + cast_primary_key(rindex ? col[rindex + 1, col.length] : col) + end + end + end + + def depth + @_depth ||= if @_ancestor_ids + @_ancestor_ids.size + else + col = read_attribute(ancestry_base_class.ancestry_column) + col ? col.count(ANCESTRY_DELIMITER) + 1 : 0 + end + end + + STRING_BASED_KEYS = %i[string uuid text].freeze + def cast_primary_key(key) + if STRING_BASED_KEYS.include?(primary_key_type) + key + else + key.to_i + end + end + + def clear_memoized_instance_variables + @_ancestor_ids = nil + @_depth = nil + + # can't assign to `nil` since `nil` could be a valid result + remove_instance_variable(:@_parent_id) if defined?(@_parent_id) + end + end +end