[Pkg-puppet-devel] [facter] 173/352: (FACT-237) Implement deep_merge for composing facts

Stig Sandbeck Mathisen ssm at debian.org
Sun Apr 6 22:21:43 UTC 2014


This is an automated email from the git hooks/post-receive script.

ssm pushed a commit to branch master
in repository facter.

commit 733d2af2abad516df124b7c0cf43eb2124825867
Author: Adrien Thebo <git at somethingsinistral.net>
Date:   Mon Jan 6 10:38:09 2014 -0800

    (FACT-237) Implement deep_merge for composing facts
---
 lib/facter/util/values.rb     | 37 +++++++++++++++++++
 spec/unit/util/values_spec.rb | 85 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/lib/facter/util/values.rb b/lib/facter/util/values.rb
index d691987..938f6fe 100644
--- a/lib/facter/util/values.rb
+++ b/lib/facter/util/values.rb
@@ -4,6 +4,43 @@ module Facter
     module Values
       module_function
 
+      # Perform a deep merge of two nested data structures.
+      #
+      # @param left [Object]
+      # @param right [Object]
+      # @param path [Array<String>] The traversal path followed when merging nested hashes
+      #
+      # @return [Object] The merged data structure.
+      def deep_merge(left, right, path = [], &block)
+        ret = nil
+
+        if left.is_a? Hash and right.is_a? Hash
+          ret = left.merge(right) do |key, left_val, right_val|
+            path.push(key)
+            merged = deep_merge(left_val, right_val, path)
+            path.pop
+            merged
+          end
+        elsif left.is_a? Array and right.is_a? Array
+          ret = left.dup.concat(right)
+        elsif right.nil?
+          ret = left
+        elsif left.nil?
+          ret = right
+        elsif left.nil? and right.nil?
+          ret = nil
+        else
+          msg = "Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}"
+          if not path.empty?
+            msg << " at root"
+            msg << path.map { |part| "[#{part.inspect}]" }.join
+          end
+          raise ArgumentError, msg
+        end
+
+        ret
+      end
+
       def convert(value)
         value = value.to_s if value.is_a?(Symbol)
         value = value.downcase if value.is_a?(String)
diff --git a/spec/unit/util/values_spec.rb b/spec/unit/util/values_spec.rb
new file mode 100644
index 0000000..570386a
--- /dev/null
+++ b/spec/unit/util/values_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+require 'facter/util/values'
+
+describe Facter::Util::Values do
+  describe 'deep_merge' do
+    it "non-destructively concatenates arrays" do
+      first = %w[foo bar]
+      second = %w[baz quux]
+
+      expect(described_class.deep_merge(first, second)).to eq %w[foo bar baz quux]
+      expect(first).to eq %w[foo bar]
+      expect(second).to eq %w[baz quux]
+    end
+
+    it "returns the left value if the right value is nil" do
+      expect(described_class.deep_merge("left", nil)).to eq "left"
+    end
+
+    it "returns the right value if the left value is nil" do
+      expect(described_class.deep_merge(nil, "right")).to eq "right"
+    end
+
+    it "returns nil if both values are nil" do
+      expect(described_class.deep_merge(nil, nil)).to be_nil
+    end
+
+    describe "with hashes" do
+      it "merges when keys do not overlap" do
+
+        first = {:foo => 'bar'}
+        second = {:baz => 'quux'}
+
+        expect(described_class.deep_merge(first, second)).to eq(:foo => 'bar', :baz => 'quux')
+        expect(first).to eq(:foo => 'bar')
+        expect(second).to eq(:baz => 'quux')
+      end
+
+      it "concatenates arrays when both keys are arrays" do
+        first = {:foo => %w[bar]}
+        second = {:foo => %w[baz quux]}
+
+        expect(described_class.deep_merge(first, second)).to eq(:foo => %w[bar baz quux])
+        expect(first).to eq(:foo => %w[bar])
+        expect(second).to eq(:foo => %w[baz quux])
+      end
+
+      it "merges hashes when both keys are hashes" do
+        first = {:foo => {:pb => 'lead', :ag => 'silver'}}
+        second = {:foo => {:au => 'gold', :na => 'sodium'}}
+
+        expect(described_class.deep_merge(first, second)).to eq(
+          :foo => {
+            :pb => 'lead',
+            :ag => 'silver',
+            :au => 'gold',
+            :na => 'sodium'
+          }
+        )
+      end
+
+      it "prints the data structure path if an error is raised" do
+        first = {:foo => {:bar => {:baz => {:quux => true}}}}
+        second = {:foo => {:bar => {:baz => {:quux => false}}}}
+
+        expect {
+          described_class.deep_merge(first, second)
+        }.to raise_error(ArgumentError, /Cannot merge .*at .*foo.*bar.*baz.*quux/)
+      end
+    end
+
+    describe "with unmergable scalar values" do
+      [
+        [true, false],
+        [1, 2],
+        ['up', 'down']
+      ].each do |(left, right)|
+        it "raises an error when merging #{left}:#{left.class} and #{right}:#{right.class}" do
+          expect {
+            described_class.deep_merge(left, right)
+          }.to raise_error(ArgumentError, /Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}/)
+        end
+      end
+    end
+  end
+end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-puppet/facter.git



More information about the Pkg-puppet-devel mailing list