deep_merge-1.1.1/0000755000004100000410000000000012753253502013642 5ustar www-datawww-datadeep_merge-1.1.1/Rakefile0000644000004100000410000000052612753253502015312 0ustar www-datawww-datarequire 'rake/testtask' Rake::TestTask.new do |t| t.libs << FileList['lib/**.rb'] t.test_files = FileList['test/test*.rb'] end task :default => :test begin require 'rubygems' require 'rubygems/package_task' gemspec = eval(IO.read('deep_merge.gemspec')) Gem::PackageTask.new(gemspec).define rescue LoadError #okay, then end deep_merge-1.1.1/CHANGELOG0000644000004100000410000000305312753253502015055 0ustar www-datawww-data2016-08-01 Jason Frey * Ship version 1.1.1 * Fixed release date in the gemspec. 2016-08-01 Jason Frey * Ship version 1.1.0 * Add testing for newer Ruby 2.2, 2.3, head, and jruby-head. 2016-06-14 Michael Sievers * Add extend_existing_arrays option 2016-06-07 Jason Frey * Add overwrite_arrays option 2016-04-08 Dan Deleo * Remove support for old Ruby 1.8 and 1.9 2014-01-21 Dan DeLeo * Ship version 1.0.1 * Update knockout behavior to better handle nil (b7de40b5) 2011-08-15 Dan DeLeo * Document how to use w/ Rails 3 via Bundler 2011-07-28 Dan DeLeo * Use a plain ol' gemspec and Rakefile for gem creation * Ship version 1.0.0 2011-05-23 Joe Van Dyk * Added Changelog 2011-05-18 Joe Van Dyk * Merging empty strings should work if String#blank? is defined. * Use unix line endings * Removing extra whitespace 2010-01-11 Dan DeLeo * fix boolean merging according to mdkent's patch explicitly test for nils w/ #nil? instead of negating. Thanks mdkent! 2009-12-25 Dan DeLeo * miscellaneous cleanup * make rails/active_support compat optional * add jeweler rake task for gemability 2009-12-24 Dan DeLeo * VERSION: Version bump to 0.0.1 * VERSION: Version bump to 0.0.0 2009-11-06 Jonathan Weiss * import deep_merge-1.1.1/deep_merge.gemspec0000644000004100000410000000276012753253502017310 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "deep_merge" s.version = "1.1.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Steve Midgley"] s.date = "2016-08-01" s.description = "Recursively merge hashes." s.email = "dan@kallistec.com" s.extra_rdoc_files = ["LICENSE", "README.md"] s.files = ["CHANGELOG", "LICENSE", "README.md", "Rakefile", "lib/deep_merge.rb", "lib/deep_merge/core.rb", "lib/deep_merge/deep_merge_hash.rb", "lib/deep_merge/rails_compat.rb", "test/test_deep_merge.rb"] s.homepage = "https://github.com/danielsdeleo/deep_merge" s.licenses = ["MIT"] s.require_paths = ["lib"] s.rubygems_version = "1.8.23" s.summary = "Merge Deeply Nested Hashes" s.test_files = ["test/test_deep_merge.rb"] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, ["~> 10.1"]) s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 10.1"]) s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, ["~> 10.1"]) s.add_dependency(%q, [">= 0"]) end end deep_merge-1.1.1/lib/0000755000004100000410000000000012753253502014410 5ustar www-datawww-datadeep_merge-1.1.1/lib/deep_merge.rb0000644000004100000410000000007712753253502017035 0ustar www-datawww-datarequire 'deep_merge/core' require 'deep_merge/deep_merge_hash' deep_merge-1.1.1/lib/deep_merge/0000755000004100000410000000000012753253502016504 5ustar www-datawww-datadeep_merge-1.1.1/lib/deep_merge/deep_merge_hash.rb0000644000004100000410000000170012753253502022126 0ustar www-datawww-datarequire 'deep_merge/core' module DeepMerge module DeepMergeHash # ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX def ko_deep_merge!(source, options = {}) default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end # deep_merge! will merge and overwrite any unmergeables in destination hash def deep_merge!(source, options = {}) default_opts = {:preserve_unmergeables => false} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end # deep_merge will merge and skip any unmergeables in destination hash def deep_merge(source, options = {}) default_opts = {:preserve_unmergeables => true} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end end # DeepMergeHashExt end class Hash include DeepMerge::DeepMergeHash end deep_merge-1.1.1/lib/deep_merge/core.rb0000644000004100000410000002524112753253502017765 0ustar www-datawww-datamodule DeepMerge class InvalidParameter < StandardError; end DEFAULT_FIELD_KNOCKOUT_PREFIX = '--' # Deep Merge core documentation. # deep_merge! method permits merging of arbitrary child elements. The two top level # elements must be hashes. These hashes can contain unlimited (to stack limit) levels # of child elements. These child elements to not have to be of the same types. # Where child elements are of the same type, deep_merge will attempt to merge them together. # Where child elements are not of the same type, deep_merge will skip or optionally overwrite # the destination element with the contents of the source element at that level. # So if you have two hashes like this: # source = {:x => [1,2,3], :y => 2} # dest = {:x => [4,5,'6'], :y => [7,8,9]} # dest.deep_merge!(source) # Results: {:x => [1,2,3,4,5,'6'], :y => 2} # By default, "deep_merge!" will overwrite any unmergeables and merge everything else. # To avoid this, use "deep_merge" (no bang/exclamation mark) # # Options: # Options are specified in the last parameter passed, which should be in hash format: # hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'}) # :preserve_unmergeables DEFAULT: false # Set to true to skip any unmergeable elements from source # :knockout_prefix DEFAULT: nil # Set to string value to signify prefix which deletes elements from existing element # :overwrite_arrays DEFAULT: false # Set to true if you want to avoid merging arrays # :sort_merged_arrays DEFAULT: false # Set to true to sort all arrays that are merged together # :unpack_arrays DEFAULT: nil # Set to string value to run "Array::join" then "String::split" against all arrays # :merge_hash_arrays DEFAULT: false # Set to true to merge hashes within arrays # :merge_debug DEFAULT: false # Set to true to get console output of merge process for debugging # # Selected Options Details: # :knockout_prefix => The purpose of this is to provide a way to remove elements # from existing Hash by specifying them in a special way in incoming hash # source = {:x => ['--1', '2']} # dest = {:x => ['1', '3']} # dest.ko_deep_merge!(source) # Results: {:x => ['2','3']} # Additionally, if the knockout_prefix is passed alone as a string, it will cause # the entire element to be removed: # source = {:x => '--'} # dest = {:x => [1,2,3]} # dest.ko_deep_merge!(source) # Results: {:x => ""} # :unpack_arrays => The purpose of this is to permit compound elements to be passed # in as strings and to be converted into discrete array elements # irsource = {:x => ['1,2,3', '4']} # dest = {:x => ['5','6','7,8']} # dest.deep_merge!(source, {:unpack_arrays => ','}) # Results: {:x => ['1','2','3','4','5','6','7','8'} # Why: If receiving data from an HTML form, this makes it easy for a checkbox # to pass multiple values from within a single HTML element # # :merge_hash_arrays => merge hashes within arrays # source = {:x => [{:y => 1}]} # dest = {:x => [{:z => 2}]} # dest.deep_merge!(source, {:merge_hash_arrays => true}) # Results: {:x => [{:y => 1, :z => 2}]} # # There are many tests for this library - and you can learn more about the features # and usages of deep_merge! by just browsing the test examples def self.deep_merge!(source, dest, options = {}) # turn on this line for stdout debugging text merge_debug = options[:merge_debug] || false overwrite_unmergeable = !options[:preserve_unmergeables] knockout_prefix = options[:knockout_prefix] || nil raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == "" raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable # if present: we will split and join arrays on this char before merging array_split_char = options[:unpack_arrays] || false # request that we avoid merging arrays overwrite_arrays = options[:overwrite_arrays] || false # request that we sort together any arrays when they are merged sort_merged_arrays = options[:sort_merged_arrays] || false # request that arrays of hashes are merged together merge_hash_arrays = options[:merge_hash_arrays] || false # request to extend existing arrays, instead of overwriting them extend_existing_arrays = options[:extend_existing_arrays] || false di = options[:debug_indent] || '' # do nothing if source is nil return dest if source.nil? # if dest doesn't exist, then simply copy source to it if !(dest) && overwrite_unmergeable dest = source; return dest end puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug if source.kind_of?(Hash) puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug source.each do |src_key, src_value| if dest.kind_of?(Hash) puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug if dest[src_key] puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' ')) else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!) puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others) begin src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty) rescue TypeError src_dup = src_value end dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' ')) end elsif dest.kind_of?(Array) && extend_existing_arrays dest.push(source) else # dest isn't a hash, so we overwrite it completely (if permitted) if overwrite_unmergeable puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug dest = overwrite_unmergeables(source, dest, options) end end end elsif source.kind_of?(Array) puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug if overwrite_arrays puts "#{di} overwrite arrays" if merge_debug dest = source else # if we are instructed, join/split any source arrays before processing if array_split_char puts "#{di} split/join on source: #{source.inspect}" if merge_debug source = source.join(array_split_char).split(array_split_char) if dest.kind_of?(Array) dest = dest.join(array_split_char).split(array_split_char) end end # if there's a naked knockout_prefix in source, that means we are to truncate dest if knockout_prefix && source.index(knockout_prefix) dest = clear_or_nil(dest); source.delete(knockout_prefix) end if dest.kind_of?(Array) if knockout_prefix print "#{di} knocking out: " if merge_debug # remove knockout prefix items from both source and dest source.delete_if do |ko_item| retval = false item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item if item != ko_item print "#{ko_item} - " if merge_debug dest.delete(item) dest.delete(ko_item) retval = true end retval end puts if merge_debug end puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug source_all_hashes = source.all? { |i| i.kind_of?(Hash) } dest_all_hashes = dest.all? { |i| i.kind_of?(Hash) } if merge_hash_arrays && source_all_hashes && dest_all_hashes # merge hashes in lists list = [] dest.each_index do |i| list[i] = deep_merge!(source[i] || {}, dest[i], options.merge(:debug_indent => di + ' ')) end list += source[dest.count..-1] if source.count > dest.count dest = list else dest = dest | source end dest.sort! if sort_merged_arrays elsif overwrite_unmergeable puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug dest = overwrite_unmergeables(source, dest, options) end end else # src_hash is not an array or hash, so we'll have to overwrite dest if dest.kind_of?(Array) && extend_existing_arrays dest.push(source) else puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug dest = overwrite_unmergeables(source, dest, options) end end puts "#{di}Returning #{dest.inspect}" if merge_debug dest end # deep_merge! # allows deep_merge! to uniformly handle overwriting of unmergeable entities def self.overwrite_unmergeables(source, dest, options) merge_debug = options[:merge_debug] || false overwrite_unmergeable = !options[:preserve_unmergeables] knockout_prefix = options[:knockout_prefix] || false di = options[:debug_indent] || '' if knockout_prefix && overwrite_unmergeable if source.kind_of?(String) # remove knockout string from source before overwriting dest src_tmp = source.gsub(%r{^#{knockout_prefix}},"") elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) } else src_tmp = source end if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug dest = src_tmp else # if we do find a knockout_prefix, then we just delete dest puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug dest = "" end elsif overwrite_unmergeable dest = source end dest end def self.clear_or_nil(obj) if obj.respond_to?(:clear) obj.clear else obj = nil end obj end end # module DeepMerge deep_merge-1.1.1/lib/deep_merge/rails_compat.rb0000644000004100000410000000166012753253502021511 0ustar www-datawww-datarequire 'deep_merge/core' module DeepMerge module RailsCompat # ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX def ko_deeper_merge!(source, options = {}) default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end # deep_merge! will merge and overwrite any unmergeables in destination hash def deeper_merge!(source, options = {}) default_opts = {:preserve_unmergeables => false} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end # deep_merge will merge and skip any unmergeables in destination hash def deeper_merge(source, options = {}) default_opts = {:preserve_unmergeables => true} DeepMerge::deep_merge!(source, self, default_opts.merge(options)) end end end class Hash include ::DeepMerge::RailsCompat end deep_merge-1.1.1/test/0000755000004100000410000000000012753253502014621 5ustar www-datawww-datadeep_merge-1.1.1/test/test_deep_merge.rb0000644000004100000410000011373612753253502020314 0ustar www-datawww-datarequire 'test/unit' $:.unshift(File.dirname(__FILE__) + '/../lib/') require 'deep_merge' # Assume strings have a blank? method # as they do when ActiveSupport is included. module StringBlank def blank? size == 0 end end class TestDeepMerge < Test::Unit::TestCase def setup end # show that Hash object has deep merge capabilities in form of three methods: # ko_deep_merge! # uses '--' knockout and overwrites unmergeable # deep_merge! # overwrites unmergeable # deep_merge # skips unmergeable def test_hash_deep_merge x = {} assert x.respond_to?('deep_merge!'.to_sym) hash_src = {'id' => [3,4,5]} hash_dest = {'id' => [1,2,3]} assert hash_dest.ko_deep_merge!(hash_src) assert_equal({'id' => [1,2,3,4,5]}, hash_dest) hash_src = {'id' => [3,4,5]} hash_dest = {'id' => [1,2,3]} assert hash_dest.deep_merge!(hash_src) assert_equal({'id' => [1,2,3,4,5]}, hash_dest) hash_src = {'id' => 'xxx'} hash_dest = {'id' => [1,2,3]} assert hash_dest.deep_merge(hash_src) assert_equal({'id' => [1,2,3]}, hash_dest) end FIELD_KNOCKOUT_PREFIX = DeepMerge::DEFAULT_FIELD_KNOCKOUT_PREFIX # tests DeepMerge::deep_merge! function def test_deep_merge # merge tests (moving from basic to more complex) # test merging an hash w/array into blank hash hash_src = {'id' => '2'} hash_dst = {} DeepMerge::deep_merge!(hash_src.dup, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal hash_src, hash_dst # test merging an hash w/array into blank hash hash_src = {'region' => {'id' => ['227', '2']}} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal hash_src, hash_dst # merge from empty hash hash_src = {} hash_dst = {"property" => ["2","4"]} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => ["2","4"]}, hash_dst) # merge to empty hash hash_src = {"property" => ["2","4"]} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => ["2","4"]}, hash_dst) # simple string overwrite hash_src = {"name" => "value"} hash_dst = {"name" => "value1"} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"name" => "value"}, hash_dst) # simple string overwrite of empty hash hash_src = {"name" => "value"} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal(hash_src, hash_dst) # hashes holding array hash_src = {"property" => ["1","3"]} hash_dst = {"property" => ["2","4"]} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal(["2","4","1","3"], hash_dst['property']) # hashes holding array (overwrite) hash_src = {"property" => ["1","3"]} hash_dst = {"property" => ["2","4"]} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_arrays => true}) assert_equal(["1","3"], hash_dst['property']) # hashes holding array (sorted) hash_src = {"property" => ["1","3"]} hash_dst = {"property" => ["2","4"]} DeepMerge::deep_merge!(hash_src, hash_dst, {:sort_merged_arrays => true}) assert_equal(["1","2","3","4"].sort, hash_dst['property']) # hashes holding hashes holding arrays (array with duplicate elements is merged with dest then src hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["3", "2"], "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => ["3","2","1"], "bathroom_count" => ["2", "1", "4+"]}}, hash_dst) # hash holding hash holding array v string (string is overwritten by array) hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst) # hash holding hash holding array v string (string is NOT overwritten by array) hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true}) assert_equal({"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}, hash_dst) # hash holding hash holding string v array (array is overwritten by string) hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}, hash_dst) # hash holding hash holding string v array (array does NOT overwrite string) hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true}) assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst) # hash holding hash holding hash v array (array is overwritten by hash) hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}, hash_dst) # hash holding hash holding hash v array (array is NOT overwritten by hash) hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true}) assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst) # 3 hash layers holding integers (integers are overwritten by source) hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => 2, "queen_bed" => 4}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}, hash_dst) # 3 hash layers holding arrays of int (arrays are merged) hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2","1","4+"]}}, hash_dst) # 1 hash overwriting 3 hash layers holding arrays of int hash_src = {"property" => "1"} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => "1"}, hash_dst) # 1 hash NOT overwriting 3 hash layers holding arrays of int hash_src = {"property" => "1"} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true}) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}, hash_dst) # 3 hash layers holding arrays of int (arrays are merged) but second hash's array is overwritten hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => "1"}}, hash_dst) # 3 hash layers holding arrays of int (arrays are merged) but second hash's array is NOT overwritten hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true}) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2"]}}, hash_dst) # 3 hash layers holding arrays of int, but one holds int. This one overwrites, but the rest merge hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [1]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [4,1]}, "bathroom_count" => ["2","1"]}}, hash_dst) # 3 hash layers holding arrays of int, but source is incomplete. hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}, hash_dst) # 3 hash layers holding arrays of int, but source is shorter and has new 2nd level ints. hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {2=>3, "king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}, hash_dst) # 3 hash layers holding arrays of int, but source is empty hash_src = {} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}, hash_dst) # 3 hash layers holding arrays of int, but dest is empty hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}, hash_dst) # 3 hash layers holding arrays of int, but source includes a nil in the array hash_src = {"property" => {"bedroom_count" => {"king_bed" => [nil], "queen_bed" => [1, nil]}, "bathroom_count" => [nil, "1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,nil], "queen_bed" => [4, 1, nil]}, "bathroom_count" => ["2", nil, "1"]}}, hash_dst) # 3 hash layers holding arrays of int, but destination includes a nil in the array hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [nil], "queen_bed" => [4, nil]}, "bathroom_count" => [nil,"2"]}} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"property" => {"bedroom_count" => {"king_bed" => [nil, 3], "queen_bed" => [4, nil, 1]}, "bathroom_count" => [nil, "2", "1"]}}, hash_dst) # if extend_existig_arrays == true && destination.kind_of?(Array) && source element is neither array nor hash, push source to destionation hash_src = { "property" => "4" } hash_dst = { "property" => ["1", "2", "3"] } DeepMerge::deep_merge!(hash_src, hash_dst, :extend_existing_arrays => true) assert_equal({"property" => ["1", "2", "3", "4"]}, hash_dst) # if extend_existig_arrays == true && destination.kind_of?(Array) && source.kind_of(Hash), push source to destionation hash_src = { "property" => {:number => "3"} } hash_dst = { "property" => [{:number => "1"}, {:number => "2"}] } DeepMerge::deep_merge!(hash_src, hash_dst, :extend_existing_arrays => true) assert_equal({"property"=>[{:number=>"1"}, {:number=>"2"}, {:number=>"3"}]}, hash_dst) # test parameter management for knockout_prefix and overwrite unmergable assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => ""})} assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => ""})} assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => "--"})} assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--"})} assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst)} assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})} # hash holding arrays of arrays hash_src = {["1", "2", "3"] => ["1", "2"]} hash_dst = {["4", "5"] => ["3"]} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({["1","2","3"] => ["1", "2"], ["4", "5"] => ["3"]}, hash_dst) # test merging of hash with blank hash, and make sure that source array split still functions hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'property' => {'bedroom_count' => ["1","2","3"]}}, hash_dst) # test merging of hash with blank hash, and make sure that source array split does not function when turned off hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({'property' => {'bedroom_count' => ["1","2,3"]}}, hash_dst) # test merging into a blank hash with overwrite_unmergeables turned on hash_src = {"action"=>"browse", "controller"=>"results"} hash_dst = {} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal hash_src, hash_dst # KNOCKOUT_PREFIX testing # the next few tests are looking for correct behavior from specific real-world params/session merges # using the custom modifiers built for param/session merges [nil, ","].each do |ko_split| # typical params/session style hash with knockout_merge elements hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}} hash_session = {"property"=>{"bedroom_count"=>["1", "2", "3"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split}) assert_equal({"property"=>{"bedroom_count"=>["2", "3"]}}, hash_session) # typical params/session style hash with knockout_merge elements hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}} hash_session = {"property"=>{"bedroom_count"=>["3"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split}) assert_equal({"property"=>{"bedroom_count"=>["3","2"]}}, hash_session) # typical params/session style hash with knockout_merge elements hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}} hash_session = {"property"=>{"bedroom_count"=>["4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split}) assert_equal({"property"=>{"bedroom_count"=>["4","2","3"]}}, hash_session) # typical params/session style hash with knockout_merge elements hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}} hash_session = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split}) assert_equal({"property"=>{"bedroom_count"=>["4","2","3"]}}, hash_session) # typical params/session style hash with knockout_merge elements hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1", FIELD_KNOCKOUT_PREFIX+"2", "3", "4"]}} hash_session = {"amenity"=>{"id"=>["1", "2"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split}) assert_equal({"amenity"=>{"id"=>["3","4"]}}, hash_session) end # special params/session style hash with knockout_merge elements in form src: ["1","2"] dest:["--1,--2", "3,4"] hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}} hash_session = {"amenity"=>{"id"=>["1", "2"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["3","4"]}}, hash_session) # same as previous but without ko_split value, this merge should fail hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}} hash_session = {"amenity"=>{"id"=>["1", "2"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>{"id"=>["1","2","3,4"]}}, hash_session) # special params/session style hash with knockout_merge elements in form src: ["1","2"] dest:["--1,--2", "3,4"] hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,2", "3,4", "--5", "6"]}} hash_session = {"amenity"=>{"id"=>["1", "2"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["2","3","4","6"]}}, hash_session) # special params/session style hash with knockout_merge elements in form src: ["--1,--2", "3,4", "--5", "6"] dest:["1,2", "3,4"] hash_params = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}1,#{FIELD_KNOCKOUT_PREFIX}2", "3,4", "#{FIELD_KNOCKOUT_PREFIX}5", "6"]}} hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["3","4","6"]}}, hash_session) hash_src = {"url_regions"=>[], "region"=>{"ids"=>["227,233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"} hash_dst = {"region"=>{"ids"=>["227"]}} DeepMerge::deep_merge!(hash_src.dup, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"url_regions"=>[], "region"=>{"ids"=>["227","233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"}, hash_dst) hash_src = {"region"=>{"ids"=>["--","227"], "id"=>"230"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}}, hash_dst) hash_src = {"region"=>{"ids"=>["--","227", "232", "233"], "id"=>"232"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst) hash_src = {"region"=>{"ids"=>["--,227,232,233"], "id"=>"232"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst) hash_src = {"region"=>{"ids"=>["--,227,232","233"], "id"=>"232"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst) hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}}, hash_dst) hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}} hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"}, hash_dst) hash_src = {"query_uuid"=>"6386333d-389b-ab5c-8943-6f3a2aa914d7", "region"=>{"ids"=>["--,227"], "id"=>"230"}} hash_dst = {"query_uuid"=>"6386333d-389b-ab5c-8943-6f3a2aa914d7", "url_regions"=>[], "region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"} DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"query_uuid" => "6386333d-389b-ab5c-8943-6f3a2aa914d7", "url_regions"=>[], "region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"}, hash_dst) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => "--"} hash_session = {"amenity" => "1"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => ""}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--"]} hash_session = {"amenity" => "1"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => []}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => "--"} hash_session = {"amenity" => ["1"]} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => ""}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--"]} hash_session = {"amenity" => ["1"]} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => []}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--"]} hash_session = {"amenity" => "1"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => []}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--", "2"]} hash_session = {'amenity' => ["1", "3", "7+"]} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => ["2"]}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--", "2"]} hash_session = {'amenity' => "5"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => ['2']}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => "--"} hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => ""}, hash_session) # knock out entire dest hash if "--" is passed for source hash_params = {'amenity' => ["--"]} hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","}) assert_equal({'amenity' => []}, hash_session) # knock out dest array if "--" is passed for source hash_params = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => {'ids' => ""}}, hash_session) # knock out dest array but leave other elements of hash intact hash_params = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => {'ids' => "", 'id'=>'11'}}, hash_session) # knock out entire tree of dest hash hash_params = {"region" => FIELD_KNOCKOUT_PREFIX} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => ""}, hash_session) # knock out entire tree of dest hash - retaining array format hash_params = {"region" => {'ids' => [FIELD_KNOCKOUT_PREFIX]}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => {'ids' => [], 'id'=>'11'}}, hash_session) # knock out entire tree of dest hash & replace with new content hash_params = {"region" => {'ids' => ["2", FIELD_KNOCKOUT_PREFIX, "6"]}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => {'ids' => ["2", "6"], 'id'=>'11'}}, hash_session) # knock out entire tree of dest hash & replace with new content hash_params = {"region" => {'ids' => ["7", FIELD_KNOCKOUT_PREFIX, "6"]}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({'region' => {'ids' => ["7", "6"], 'id'=>'11'}}, hash_session) # edge test: make sure that when we turn off knockout_prefix that all values are processed correctly hash_params = {"region" => {'ids' => ["7", "--", "2", "6,8"]}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session, {:unpack_arrays => ","}) assert_equal({'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6", "8"], 'id'=>'11'}}, hash_session) # edge test 2: make sure that when we turn off source array split that all values are processed correctly hash_params = {"region" => {'ids' => ["7", "3", "--", "6,8"]}} hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}} DeepMerge::deep_merge!(hash_params, hash_session) assert_equal({'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6,8"], 'id'=>'11'}}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""} hash_params = {"amenity"=>"--1"} hash_session = {"amenity"=>"1"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>""}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "2"} -> merges to {'key' => ""} hash_params = {"amenity"=>"--1"} hash_session = {"amenity"=>"2"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>""}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""} hash_params = {"amenity"=>["--1"]} hash_session = {"amenity"=>"1"} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>[]}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""} hash_params = {"amenity"=>["--1"]} hash_session = {"amenity"=>["1"]} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>[]}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""} hash_params = {"amenity"=>"--1"} hash_session = {} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>""}, hash_session) # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""} hash_params = {"amenity"=>"--1"} hash_session = {"amenity"=>["1"]} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>""}, hash_session) #are unmerged hashes passed unmodified w/out :unpack_arrays? hash_params = {"amenity"=>{"id"=>["26,27"]}} hash_session = {} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX}) assert_equal({"amenity"=>{"id"=>["26,27"]}}, hash_session) #hash should be merged hash_params = {"amenity"=>{"id"=>["26,27"]}} hash_session = {} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["26","27"]}}, hash_session) # second merge of same values should result in no change in output hash_params = {"amenity"=>{"id"=>["26,27"]}} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["26","27"]}}, hash_session) #hashes with knockout values are suppressed hash_params = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}26,#{FIELD_KNOCKOUT_PREFIX}27,28"]}} hash_session = {} DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","}) assert_equal({"amenity"=>{"id"=>["28"]}}, hash_session) hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'ids'=>['227','2','3','3']}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'ids'=>[]}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'ids'=>['227','2','3','3'], 'id' => '3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'ids'=>['--'], 'id' => '5'}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'ids'=>['--', '227'], 'id' => '5'}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>'--', 'id'=>'5'}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>'', 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--'], 'id'=>'5'}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--','227'], 'id'=>'5'}, 'query_uuid' => 'zzz'} hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst) hash_src = {"muni_city_id"=>"--", "id"=>""} hash_dst = {"muni_city_id"=>"", "id"=>""} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({"muni_city_id"=>"", "id"=>""}, hash_dst) hash_src = {"region"=>{"muni_city_id"=>"--", "id"=>""}} hash_dst = {"region"=>{"muni_city_id"=>"", "id"=>""}} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({"region"=>{"muni_city_id"=>"", "id"=>""}}, hash_dst) hash_src = {"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"--", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"} hash_dst = {"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"} DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","}) assert_equal({"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"}, hash_dst) # hash of array of hashes hash_src = {"item" => [{"1" => "3"}, {"2" => "4"}]} hash_dst = {"item" => [{"3" => "5"}]} DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"item" => [{"3" => "5"}, {"1" => "3"}, {"2" => "4"}]}, hash_dst) ###################################### # tests for "merge_hash_arrays" option hash_src = {"item" => [{"1" => "3"}]} hash_dst = {"item" => [{"3" => "5"}]} DeepMerge::deep_merge!(hash_src, hash_dst, {:merge_hash_arrays => true}) assert_equal({"item" => [{"3" => "5", "1" => "3"}]}, hash_dst) hash_src = {"item" => [{"1" => "3"}, {"2" => "4"}]} hash_dst = {"item" => [{"3" => "5"}]} DeepMerge::deep_merge!(hash_src, hash_dst, {:merge_hash_arrays => true}) assert_equal({"item" => [{"3" => "5", "1" => "3"}, {"2" => "4"}]}, hash_dst) hash_src = {"item" => [{"1" => "3"}]} hash_dst = {"item" => [{"3" => "5"}, {"2" => "4"}]} DeepMerge::deep_merge!(hash_src, hash_dst, {:merge_hash_arrays => true}) assert_equal({"item" => [{"3" => "5", "1" => "3"}, {"2" => "4"}]}, hash_dst) # if arrays contain non-hash objects, the :merge_hash_arrays option has # no effect. hash_src = {"item" => [{"1" => "3"}, "str"]} # contains "str", non-hash hash_dst = {"item" => [{"3" => "5"}]} DeepMerge::deep_merge!(hash_src, hash_dst, {:merge_hash_arrays => true}) assert_equal({"item" => [{"3" => "5"}, {"1" => "3"}, "str"]}, hash_dst) # Merging empty strings s1, s2 = "hello", "" [s1, s2].each { |s| s.extend StringBlank } hash_dst = {"item" => s1 } hash_src = {"item" => s2 } DeepMerge::deep_merge!(hash_src, hash_dst) assert_equal({"item" => ""}, hash_dst) end # test_deep_merge end deep_merge-1.1.1/LICENSE0000644000004100000410000000211312753253502014644 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2008-2016 Steve Midgley, Daniel DeLeo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. deep_merge-1.1.1/README.md0000644000004100000410000001254712753253502015132 0ustar www-datawww-dataDeepMerge Overview ================== Deep Merge is a simple set of utility functions for Hash. It permits you to merge elements inside a hash together recursively. The manner by which it does this is somewhat arbitrary (since there is no defining standard for this) but it should end up being pretty intuitive and do what you expect. You can learn a lot more about this by reading the test file. It's pretty well documented and has many examples of various merges from very simple to pretty complex. The primary need that caused me to write this library is the merging of elements coming from HTTP parameters and related stored parameters in session. This lets a user build up a set of parameters over time, modifying individual items. Deep Merge Core Documentation ============================= `deep_merge!` method permits merging of arbitrary child elements. The two top level elements must be hashes. These hashes can contain unlimited (to stack limit) levels of child elements. These child elements to not have to be of the same types. Where child elements are of the same type, `deep_merge` will attempt to merge them together. Where child elements are not of the same type, `deep_merge` will skip or optionally overwrite the destination element with the contents of the source element at that level. So if you have two hashes like this: source = {:x => [1,2,3], :y => 2} dest = {:x => [4,5,'6'], :y => [7,8,9]} dest.deep_merge!(source) Results: {:x => [1,2,3,4,5,'6'], :y => 2} By default, `deep_merge!` will overwrite any unmergeables and merge everything else. To avoid this, use `deep_merge` (no bang/exclamation mark) Options ------- Options are specified in the last parameter passed, which should be in hash format: hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'}) :preserve_unmergeables DEFAULT: false Set to true to skip any unmergeable elements from source :knockout_prefix DEFAULT: nil Set to string value to signify prefix which deletes elements from existing element :overwrite_arrays DEFAULT: false Set to true if you want to avoid merging arrays :sort_merged_arrays DEFAULT: false Set to true to sort all arrays that are merged together :unpack_arrays DEFAULT: nil Set to string value to run "Array::join" then "String::split" against all arrays :merge_hash_arrays DEFAULT: false Set to true to merge hashes within arrays :extend_existing_arrays DEFAULT: false Set to true to extend existing arrays, instead of overwriting them :merge_debug DEFAULT: false Set to true to get console output of merge process for debugging Selected Options Details ------------------------ **:knockout_prefix** The purpose of this is to provide a way to remove elements from existing Hash by specifying them in a special way in incoming hash source = {:x => ['--1', '2']} dest = {:x => ['1', '3']} dest.ko_deep_merge!(source) Results: {:x => ['2','3']} Additionally, if the knockout_prefix is passed alone as a string, it will cause the entire element to be removed: source = {:x => '--'} dest = {:x => [1,2,3]} dest.ko_deep_merge!(source) Results: {:x => ""} **:overwrite_arrays** The purpose of this is to provide a way to replace Arrays instead of having them merge together. source = {:x => ['1', '2']} dest = {:x => ['3', '4']} dest.deep_merge!(source, {:overwrite_arrays => true}) Results: {:x => ['1', '2']} **:unpack_arrays** The purpose of this is to permit compound elements to be passed in as strings and to be converted into discrete array elements irsource = {:x => ['1,2,3', '4']} dest = {:x => ['5','6','7,8']} dest.deep_merge!(source, {:unpack_arrays => ','}) Results: {:x => ['1','2','3','4','5','6','7','8'} Why: If receiving data from an HTML form, this makes it easy for a checkbox to pass multiple values from within a single HTML element **:merge_hash_arrays** merge hashes within arrays source = {:x => [{:y => 1}]} dest = {:x => [{:z => 2}]} dest.deep_merge!(source, {:merge_hash_arrays => true}) Results: {:x => [{:y => 1, :z => 2}]} **:extend_existing_arrays** Push src elements to existing arrays, instead of overwriting them. source = { "property" => "4" } dest = { "property" => ["1", "2", "3"] } dest.deep_merge!(source, {:extend_existing_arrays => true}) Results: {"property" => ["1", "2", "3", "4"]} There are many tests for this library - and you can learn more about the features and usages of deep_merge! by just browsing the test examples. Using deep_merge in Rails ========================= To avoid conflict with ActiveSupport, load deep_merge via: require 'deep_merge/rails_compat' In a Gemfile: gem "deep_merge", :require => 'deep_merge/rails_compat' The deep_merge methods will then be defined as Hash#deeper_merge Hash#deeper_merge! Hash#ko_deeper_merge! Simple Example Code =================== require 'deep_merge' x = {:x => [3,4,5]} y = {:x => [1,2,3]} y.deep_merge!(x) # results: y = {:x => [1,2,3,4,5]} Availablility ============= `deep_merge` was written by Steve Midgley, and is now maintained by Daniel DeLeo. The official home of `deep_merge` on the internet is now https://github.com/danielsdeleo/deep_merge Copyright (c) 2008-2016 Steve Midgley, released under the MIT license