module UniverseCompiler module Entity module Inheritance extend UniverseCompiler::Entity::FieldBinder field_accessor :extends attr_reader :compiled def apply_inheritance return unless compiled.nil? unless extends.nil? valid_for_inheritance? raise_error: true @fields = fields_inheritance_result end ensure @compiled = true end def valid_for_inheritance?(raise_error: false) is_valid = true unless extends.nil? is_valid = false if extends.respond_to? :type and extends.respond_to? :name if extends.respond_to? :fields or extends.is_a? UniverseCompiler::Entity::Reference is_valid = self.type == extends.type end end end return true if is_valid false_or_raise "Invalid entity '#{to_composite_key}' ! Inheritance is invalid !", raise_error: raise_error end private def fields_inheritance_result stack = inheritance_stack(self).reverse return stack.first if stack.size == 1 merge_engine = SuperStack::Manager.new merge_engine.merge_policy = SuperStack::MergePolicies::InheritanceMergePolicy stack.each { |entity| merge_engine << entity.fields.dup } merge_engine[] end def inheritance_stack(entity, stack=[], visited=[]) if visited.include? entity display_as_path = display_path(*visited.map(&:to_composite_key), entity.to_composite_key) raise UniverseCompiler::Error, "Circular reference detected for '#{to_composite_key}' (#{display_as_path}) !" end visited << entity stack << entity return stack unless entity.extends parent = entity.extends if parent.nil? display_as_path = display_path(*visited.map(&:to_composite_key), entity.extends.to_composite_key) err_msg = "Invalid inheritance defined in '#{entity.to_composite_key}' extending '#{entity.to_composite_key}' (#{display_as_path}) !" raise UniverseCompiler::Error, err_msg end stack = inheritance_stack parent, stack, visited stack end def inherited_fields_stack(entity, stack=[], visited=[]) if visited.include? entity raise UniverseCompiler::Error, "Circular reference detected for '#{self.to_composite_key}' (#{display_path(*visited.map(&:to_composite_key), entity.to_composite_key)}) !" end visited << entity stack << entity.fields return stack unless entity.extends parent = entity.extends if parent.nil? raise UniverseCompiler::Error, "Invalid inheritance defined in '#{entity.to_composite_key}' extending '#{entity.to_composite_key}' (#{display_path(*visited.map(&:composite_key), entity.extends.to_composite_key)}) !" end stack = inherited_fields_stack parent, stack, visited stack end def display_path(*names) names.join ' -> ' end end end end