require 'yaml' class ::Hash def deep_merge(second) merger = proc { |_, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 } merge(second.to_h, &merger) end end module Comfpile class ConfigArtefact < Artefact attr_reader :loaded_config attr_reader :config attr_reader :mtime def initialize(*args, **opts) super(*args, **opts) @loaded_config = {} unless @target == '.' or @target == '' parent_artefact :config_file, File.dirname(@target) end @config_file_artefact = craft_artefact(:sourcefile, File.join(@target, "comf.yml")) wait_on @config_file_artefact add_step do unless @config_file_artefact.nil? @loaded_config = YAML.load_file(@config_file_artefact.file) log "Config file found and loaded for directory #{@target}!" end fail! "Config YAML file is not a hash!" unless @loaded_config.is_a? Hash if @parent_artefact.nil? @config = @loaded_config @mtime = @config_file_artefact.mtime else defaults = @loaded_config['defaults'] || {} @config = defaults.deep_merge(@parent_artefact.config.deep_merge(@loaded_config)) @mtime = [@config_file_artefact.mtime, @parent_artefact.mtime].max end end end def [](key) @config[key] end undef []= end class ConfigLoaderEngine < ArtefactEngine def initialize(core, **options) super(core, **options) end def craft(stage, target, context) return unless stage == :config_file ConfigArtefact.new(@core, self, stage, target, context) end end end