2017-09-19 11:11:09 +00:00
|
|
|
module GroupDescendant
|
2017-09-22 15:36:39 +00:00
|
|
|
def hierarchy(hierarchy_top = nil)
|
|
|
|
expand_hierarchy_for_child(self, self, hierarchy_top)
|
2017-09-06 14:29:52 +00:00
|
|
|
end
|
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
def expand_hierarchy_for_child(child, hierarchy, hierarchy_top)
|
|
|
|
if child.parent.nil? && hierarchy_top.present?
|
2017-09-06 14:29:52 +00:00
|
|
|
raise ArgumentError.new('specified base is not part of the tree')
|
|
|
|
end
|
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
if child.parent && child.parent != hierarchy_top
|
2017-09-19 11:11:09 +00:00
|
|
|
expand_hierarchy_for_child(child.parent,
|
|
|
|
{ child.parent => hierarchy },
|
2017-09-22 15:36:39 +00:00
|
|
|
hierarchy_top)
|
2017-09-06 14:29:52 +00:00
|
|
|
else
|
2017-09-19 11:11:09 +00:00
|
|
|
hierarchy
|
2017-09-06 14:29:52 +00:00
|
|
|
end
|
|
|
|
end
|
2017-09-07 17:08:56 +00:00
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
def merge_hierarchy(other_element, hierarchy_top = nil)
|
|
|
|
GroupDescendant.build_hierarchy([self, other_element], hierarchy_top)
|
2017-09-07 17:08:56 +00:00
|
|
|
end
|
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
def self.build_hierarchy(descendants, hierarchy_top = nil)
|
|
|
|
descendants = Array.wrap(descendants)
|
|
|
|
return if descendants.empty?
|
2017-09-07 17:08:56 +00:00
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
unless descendants.all? { |hierarchy| hierarchy.is_a?(GroupDescendant) }
|
2017-09-07 17:08:56 +00:00
|
|
|
raise ArgumentError.new('element is not a hierarchy')
|
|
|
|
end
|
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
first_descendant, *other_descendants = descendants
|
|
|
|
merged = first_descendant.hierarchy(hierarchy_top)
|
2017-09-07 17:08:56 +00:00
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
other_descendants.each do |descendant|
|
|
|
|
next_descendant = descendant.hierarchy(hierarchy_top)
|
|
|
|
merged = merge_hash_tree(merged, next_descendant)
|
2017-09-07 17:08:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
merged
|
|
|
|
end
|
|
|
|
|
2017-09-22 15:36:39 +00:00
|
|
|
def self.merge_hash_tree(first_child, second_child)
|
2017-09-07 17:08:56 +00:00
|
|
|
# When the first is an array, we need to go over every element to see if
|
2017-09-12 17:03:12 +00:00
|
|
|
# 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)
|
2017-09-07 17:08:56 +00:00
|
|
|
# If both of them are hashes, we can deep_merge with the same logic
|
2017-09-12 17:03:12 +00:00
|
|
|
#
|
|
|
|
# Handled cases:
|
|
|
|
# [Hash, Hash]
|
2017-09-07 17:08:56 +00:00
|
|
|
elsif first_child.is_a?(Hash) && second_child.is_a?(Hash)
|
2017-09-22 15:36:39 +00:00
|
|
|
first_child.deep_merge(second_child) { |key, first, second| merge_hash_tree(first, second) }
|
2017-09-12 17:03:12 +00:00
|
|
|
# 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
|
2017-09-19 11:11:09 +00:00
|
|
|
# [Hash, GroupDescendant]
|
2017-09-10 15:20:27 +00:00
|
|
|
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
|
2017-09-19 11:11:09 +00:00
|
|
|
# If one or both elements are a GroupDescendant, we wrap create an array
|
2017-09-12 17:03:12 +00:00
|
|
|
# combining them.
|
|
|
|
#
|
|
|
|
# Handled cases:
|
2017-09-19 11:11:09 +00:00
|
|
|
# [GroupDescendant, Array], [GroupDescendant, GroupDescendant], [Array, Array]
|
2017-09-07 17:08:56 +00:00
|
|
|
else
|
|
|
|
Array.wrap(first_child) + Array.wrap(second_child)
|
|
|
|
end
|
|
|
|
end
|
2017-09-12 17:03:12 +00:00
|
|
|
|
|
|
|
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? }
|
2017-09-22 15:36:39 +00:00
|
|
|
array[mergeable_index] = merge_hash_tree(array[mergeable_index], new_hash)
|
2017-09-12 17:03:12 +00:00
|
|
|
else
|
|
|
|
array << new_hash
|
|
|
|
end
|
|
|
|
|
|
|
|
array
|
|
|
|
end
|
2017-09-06 14:29:52 +00:00
|
|
|
end
|