lib/attr/gather/aggregators/deep_merge.rb in attr-gather-1.4.0 vs lib/attr/gather/aggregators/deep_merge.rb in attr-gather-1.5.1

- old
+ new

@@ -11,15 +11,22 @@ class DeepMerge < Base # Initialize a new DeepMerge aggregator # # @param reverse [Boolean] deep merge results in reverse order # @param merge_input [Boolean] merge the result with the initial input + # @param array_strategy [Symbol] strategy for handling arrays, one of (:concat, :overwrite) # # @api private - def initialize(reverse: false, merge_input: true, **) + def initialize(reverse: false, merge_input: true, array_strategy: :concat, **) + unless ARRAY_STRATEGY.include?(array_strategy) + raise ArgumentError, 'array_strategy must be one of: :concat, :overwrite' + end + @reverse = reverse @merge_input = merge_input + @array_strategy = array_strategy + super end def call(input, execution_results) execution_results = execution_results.reverse_each if reverse? @@ -29,21 +36,40 @@ end end private - def reverse? - @reverse - end + ARRAY_STRATEGY = %i[concat overwrite].freeze + private_constant :ARRAY_STRATEGY + def deep_merge(hash, other) - Hash[hash].merge(other) do |_, orig, new| + hash.to_h.merge(other) do |_, orig, new| if orig.respond_to?(:to_hash) && new.respond_to?(:to_hash) - deep_merge(Hash[orig], Hash[new]) + deep_merge(orig.to_h, new.to_h) + elsif concattable?(orig, new) + orig + new else new end end + end + + def concattable?(orig, new) + return false unless @array_strategy == :concat + + concattable_class?(orig) && concattable_class?(new) + end + + def concattable_class?(obj) + return true if obj.is_a?(Array) + return true if obj.is_a?(Set) + + false + end + + def reverse? + @reverse end end end end end