Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@vanlanduyt.co>2017-09-19 14:11:09 +0300
committerBob Van Landuyt <bob@vanlanduyt.co>2017-10-04 23:49:41 +0300
commit22aa034427b9392b44d9ecba0a51bb1b6c6616d7 (patch)
tree674a46d4ea73af07ccc3b7f5dd56168d4c5c1997 /app/models/concerns/group_descendant.rb
parentfb7a0f8c335631e1cb8c8f91e135e49528fad70e (diff)
Rename `GroupHierarchy` to `GroupDescendant`
Diffstat (limited to 'app/models/concerns/group_descendant.rb')
-rw-r--r--app/models/concerns/group_descendant.rb95
1 files changed, 95 insertions, 0 deletions
diff --git a/app/models/concerns/group_descendant.rb b/app/models/concerns/group_descendant.rb
new file mode 100644
index 00000000000..89a91f87a46
--- /dev/null
+++ b/app/models/concerns/group_descendant.rb
@@ -0,0 +1,95 @@
+module GroupDescendant
+ def hierarchy(hierarchy_base = nil)
+ expand_hierarchy_for_child(self, self, hierarchy_base)
+ end
+
+ def parent
+ if self.is_a?(Project)
+ namespace
+ else
+ super
+ end
+ end
+
+ def expand_hierarchy_for_child(child, hierarchy, hierarchy_base)
+ if child.parent.nil? && hierarchy_base.present?
+ raise ArgumentError.new('specified base is not part of the tree')
+ end
+
+ if child.parent && child.parent != hierarchy_base
+ expand_hierarchy_for_child(child.parent,
+ { child.parent => hierarchy },
+ hierarchy_base)
+ else
+ hierarchy
+ end
+ end
+
+ def merge_hierarchy(other_element, hierarchy_base = nil)
+ GroupDescendant.merge_hierarchies([self, other_element], hierarchy_base)
+ end
+
+ def self.merge_hierarchies(hierarchies, hierarchy_base = nil)
+ hierarchies = Array.wrap(hierarchies)
+ return if hierarchies.empty?
+
+ unless hierarchies.all? { |hierarchy| hierarchy.is_a?(GroupDescendant) }
+ raise ArgumentError.new('element is not a hierarchy')
+ end
+
+ first_hierarchy, *other_hierarchies = hierarchies
+ merged = first_hierarchy.hierarchy(hierarchy_base)
+
+ other_hierarchies.each do |child|
+ next_hierarchy = child.hierarchy(hierarchy_base)
+ merged = merge_values(merged, next_hierarchy)
+ end
+
+ merged
+ end
+
+ def self.merge_values(first_child, second_child)
+ # When the first is an array, we need to go over every element to see if
+ # we can merge deeper. If no match is found, we add the element to the array
+ #
+ # Handled cases:
+ # [Array, Hash]
+ if first_child.is_a?(Array) && second_child.is_a?(Hash)
+ merge_hash_into_array(first_child, second_child)
+ elsif first_child.is_a?(Hash) && second_child.is_a?(Array)
+ merge_hash_into_array(second_child, first_child)
+ # If both of them are hashes, we can deep_merge with the same logic
+ #
+ # Handled cases:
+ # [Hash, Hash]
+ elsif first_child.is_a?(Hash) && second_child.is_a?(Hash)
+ first_child.deep_merge(second_child) { |key, first, second| merge_values(first, second) }
+ # If only one of them is a hash, and one of them is a GroupHierachy-object
+ # we can check if its already in the hash. If so, we don't need to do anything
+ #
+ # Handled cases
+ # [Hash, GroupDescendant]
+ elsif first_child.is_a?(Hash) && first_child.keys.include?(second_child)
+ first_child
+ elsif second_child.is_a?(Hash) && second_child.keys.include?(first_child)
+ second_child
+ # If one or both elements are a GroupDescendant, we wrap create an array
+ # combining them.
+ #
+ # Handled cases:
+ # [GroupDescendant, Array], [GroupDescendant, GroupDescendant], [Array, Array]
+ else
+ Array.wrap(first_child) + Array.wrap(second_child)
+ end
+ end
+
+ def self.merge_hash_into_array(array, new_hash)
+ if mergeable_index = array.index { |element| element.is_a?(Hash) && (element.keys & new_hash.keys).any? }
+ array[mergeable_index] = merge_values(array[mergeable_index], new_hash)
+ else
+ array << new_hash
+ end
+
+ array
+ end
+end