lib/yaml_extend.rb in yaml_extend-1.0.1 vs lib/yaml_extend.rb in yaml_extend-1.0.2
- old
+ new
@@ -11,10 +11,23 @@
module YAML
# default path in the yaml file where the files to inherit from are defined
DEFAULT_INHERITANCE_KEY = 'extends'
@@ext_load_key = nil
+
+ DEEP_MERGE_OPTIONS = [
+ :preserve_unmergeables,
+ :knockout_prefix,
+ :overwrite_arrays,
+ :sort_merged_arrays,
+ :unpack_arrays,
+ :merge_hash_arrays,
+ :extend_existing_arrays,
+ :merge_nil_values,
+ :merge_debug,
+ ]
+
#
# Set a custom inheritance key globally once.
# So you don't need to specify it on every call of ext_load_file()
#
# @param key [String|Array<String>|nil] the key in the yaml file, containing the file strings to extend from. Set nil or call #reset_load_key to reset the key.
@@ -35,50 +48,86 @@
#
# Extended variant of the YAML.load_file method by providing the
# ability to inherit from other YAML file(s)
#
- # @param yaml_path [String] the path to the yaml file to be loaded
- # @param inheritance_key [String|Array] The key used in the yaml file to extend from another YAML file. Use an Array if you want to use a tree structure key like "options.extends" => ['options','extends']
- # @param extend_existing_arrays [Boolean] extend existing arrays instead of replacing them
+ # @param [String] yaml_path the path to the yaml file to be loaded
+ # @param [String|Array] inheritance_key The key used in the yaml file to extend from another YAML file. Use an Array if you want to use a tree structure key like "options.extends" => ['options','extends']
+ # @param [Hash] options to pass, including deep_merge options as well as
+ # @option options [Boolean] :preserve_inheritance_key Preserve inheritance key(s) from resulting yaml, does most time not make sense especially in multiple inheritance - DEFAULT: false
+ #
+ # deep merge options that can be passed by - but the defaults differ from original
+ # https://github.com/danielsdeleo/deep_merge#options
+ #
+ # @option options [Boolean] :preserve_unmergeables Set to true to skip any unmergeable elements from source - DEFAULT: false
+ # @option options [String] :knockout_prefix Set to string value to signify prefix which deletes elements from existing element - DEFAULT: nil
+ # @option options [Boolean] :overwrite_arrays Set to true if you want to avoid merging arrays - DEFAULT: false
+ # @option options [Boolean] :sort_merged_arrays Set to true to sort all arrays that are merged together - DEFAULT: false
+ # @option options [String] :unpack_arrays Set to string value to run "Array::join" then "String::split" against all arrays - DEFAULT: nil
+ # @option options [Boolean] :merge_hash_arrays Set to true to merge hashes within arrays - DEFAULT: false
+ # @option options [Boolean] :extend_existing_arrays Set to true to extend existing arrays, instead of overwriting them - DEFAULT: true
+ # @option options [Boolean] :merge_nil_values Set to true to merge nil hash values, overwriting a possibly non-nil value - DEFAULT: false
+ # @option options [Boolean] :merge_debug Set to true to get console output of merge process for debugging - DEFAULT: false
+ #
+ # @param [Boolean] options Fallback for backward compatiblity: extend existing arrays instead of replacing them (deep_merge)
# @return [Hash] the resulting yaml config
#
- def self.ext_load_file(yaml_path, inheritance_key=nil, extend_existing_arrays=true)
- YAML.ext_load_file_recursive(yaml_path, inheritance_key, extend_existing_arrays, {})
+ def self.ext_load_file(yaml_path, inheritance_key = nil, options = {})
+ YAML.ext_load_file_recursive(yaml_path, inheritance_key, options, {})
end
private
#
# @param config [Hash] a hash to be merged into the result, usually only recursivly called by the method itself
#
- def self.ext_load_file_recursive(yaml_path, inheritance_key, extend_existing_arrays, config)
+ def self.ext_load_file_recursive(yaml_path, inheritance_key, options = {}, config)
+ # backward compatibility to 1.0.1
+ if options == true || options == false
+ options = {extend_existing_arrays: options}
+ end
+ default_options = {
+ preserve_inheritance_key: false,
+ preserve_unmergeables: false,
+ knockout_prefix: nil,
+ overwrite_arrays: false,
+ sort_merged_arrays: false,
+ unpack_arrays: nil,
+ merge_hash_arrays: false,
+ extend_existing_arrays: true,
+ merge_nil_values: false,
+ merge_debug: false,
+ }
+ options = default_options.merge options
private_class_method
if inheritance_key.nil?
inheritance_key = @@ext_load_key || DEFAULT_INHERITANCE_KEY
end
total_config = config.clone
-
+
yaml_path = YAML.make_absolute_path yaml_path
super_config = YAML.load_file(File.open(yaml_path))
super_inheritance_files = yaml_value_by_key inheritance_key, super_config
- delete_yaml_key inheritance_key, super_config # we don't merge the super inheritance keys into the base yaml
-
+ unless options[:preserve_inheritance_key]
+ delete_yaml_key inheritance_key, super_config # we don't merge the super inheritance keys into the base yaml
+ end
+
if super_inheritance_files && super_inheritance_files != ''
super_inheritance_files = [super_inheritance_files] unless super_inheritance_files.is_a? Array # we support strings as well as arrays of type string to extend from
super_inheritance_files.each_with_index do |super_inheritance_file, index|
# Extend a YAML path in an absolute directory
if YAML.absolute_path?(super_inheritance_file)
super_config_path = YAML.make_absolute_path(super_inheritance_file)
- # Extend a YAML path in a relative directory
+ # Extend a YAML path in a relative directory
else
super_config_path = File.dirname(yaml_path) + '/' + super_inheritance_file
end
- total_config = YAML.ext_load_file_recursive(super_config_path, inheritance_key, extend_existing_arrays, total_config)
+ total_config = YAML.ext_load_file_recursive(super_config_path, inheritance_key, options, total_config)
end
end
- total_config.deeper_merge!(super_config, extend_existing_arrays: extend_existing_arrays)
+ deep_merge_options = options.select { |k, v| DEEP_MERGE_OPTIONS.include? k }
+ total_config.deeper_merge!(super_config, deep_merge_options)
end
# some logic to ensure absolute file inheritance as well as
# relative file inheritance in yaml files
def self.make_absolute_path(file_path)
@@ -88,11 +137,11 @@
# [0] is the call from ext_load_file_recursive,
# [1] is inside ext_load_file,
# [2] is the exteranl caller of YAML.ext_load_file
base_path = File.dirname(caller_locations[2].path)
return base_path + '/' + file_path if File.exist? base_path + '/' + file_path # relative path from yaml file
- return Dir.pwd + '/' + file_path if File.exist? Dir.pwd + '/' + file_path # relative path from project
+ return Dir.pwd + '/' + file_path if File.exist? Dir.pwd + '/' + file_path # relative path from project
error_message = "Can not find absolute path of '#{file_path}'"
unless YAML.absolute_path? file_path
error_message += "\nAlso tried:\n- #{base_path + '/' + file_path}\n"\
"- #{Dir.pwd + '/' + file_path}\n"
end
@@ -100,11 +149,11 @@
end
def self.absolute_path?(path)
private_class_method
path.start_with?('/') || # unix like
- (path.length >= 3 && path[1] == ':') # ms windows
+ (path.length >= 3 && path[1] == ':') # ms windows
end
# Return the value of the corresponding key
# @param key [String|Array]
def self.yaml_value_by_key(key, config)
@@ -122,10 +171,10 @@
end
end
def self.valid_key_type?(key)
key.is_a?(Array) || key.is_a?(String) ||
- raise(InvalidKeyTypeError,"Invalid key of type '#{key.class.name}'. Valid types are String and Array.")
+ raise(InvalidKeyTypeError, "Invalid key of type '#{key.class.name}'. Valid types are String and Array.")
end
def self.delete_yaml_key(key, config)
return config.delete(key) if key.is_a? String
cfg_ref = config