drogus-deep_merge 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/README ADDED
@@ -0,0 +1,88 @@
1
+ DeepMerge Overview
2
+ ==================
3
+
4
+ Deep Merge is a simple set of utility functions for Hash. It permits
5
+ you to merge elements inside a hash together recursively. The manner
6
+ by which it does this is somewhat arbitrary (since there is no defining
7
+ standard for this) but it should end up being pretty intuitive and do what
8
+ you expect.
9
+
10
+ You can learn a lot more about this by reading the test file. It's pretty
11
+ well documented and has many examples of various merges from very simple
12
+ to pretty complex.
13
+
14
+ The primary need that caused me to write this library is the merging of elements
15
+ coming from HTTP parameters and related stored parameters in session. This lets
16
+ a user build up a set of parameters over time, modifying individual items.
17
+
18
+ Deep Merge Core Documentation
19
+ =============================
20
+ deep_merge! method permits merging of arbitrary child elements. The two top level
21
+ elements must be hashes. These hashes can contain unlimited (to stack limit) levels
22
+ of child elements. These child elements to not have to be of the same types.
23
+ Where child elements are of the same type, deep_merge will attempt to merge them together.
24
+ Where child elements are not of the same type, deep_merge will skip or optionally overwrite
25
+ the destination element with the contents of the source element at that level.
26
+ So if you have two hashes like this:
27
+ source = {:x => [1,2,3], :y => 2}
28
+ dest = {:x => [4,5,'6'], :y => [7,8,9]}
29
+ dest.deep_merge!(source)
30
+ Results: {:x => [1,2,3,4,5,'6'], :y => 2}
31
+ By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
32
+ To avoid this, use "deep_merge" (no bang/exclamation mark)
33
+
34
+ Options:
35
+ Options are specified in the last parameter passed, which should be in hash format:
36
+ hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
37
+ :preserve_unmergeables DEFAULT: false
38
+ Set to true to skip any unmergeable elements from source
39
+ :knockout_prefix DEFAULT: nil
40
+ Set to string value to signify prefix which deletes elements from existing element
41
+ :sort_merged_arrays DEFAULT: false
42
+ Set to true to sort all arrays that are merged together
43
+ :unpack_arrays DEFAULT: nil
44
+ Set to string value to run "Array::join" then "String::split" against all arrays
45
+ :merge_debug DEFAULT: false
46
+ Set to true to get console output of merge process for debugging
47
+
48
+ Selected Options Details:
49
+ :knockout_prefix => The purpose of this is to provide a way to remove elements
50
+ from existing Hash by specifying them in a special way in incoming hash
51
+ source = {:x => ['--1', '2']}
52
+ dest = {:x => ['1', '3']}
53
+ dest.ko_deep_merge!(source)
54
+ Results: {:x => ['2','3']}
55
+ Additionally, if the knockout_prefix is passed alone as a string, it will cause
56
+ the entire element to be removed:
57
+ source = {:x => '--'}
58
+ dest = {:x => [1,2,3]}
59
+ dest.ko_deep_merge!(source)
60
+ Results: {:x => ""}
61
+ :unpack_arrays => The purpose of this is to permit compound elements to be passed
62
+ in as strings and to be converted into discrete array elements
63
+ irsource = {:x => ['1,2,3', '4']}
64
+ dest = {:x => ['5','6','7,8']}
65
+ dest.deep_merge!(source, {:unpack_arrays => ','})
66
+ Results: {:x => ['1','2','3','4','5','6','7','8'}
67
+ Why: If receiving data from an HTML form, this makes it easy for a checkbox
68
+ to pass multiple values from within a single HTML element
69
+
70
+ There are many tests for this library - and you can learn more about the features
71
+ and usages of deep_merge! by just browsing the test examples
72
+
73
+
74
+ Simple Example Code
75
+ ===================
76
+
77
+ require 'deep_merge'
78
+ x = {:x => [3,4,5]}
79
+ y = {:x => [1,2,3]}
80
+ y.deep_merge!(x)
81
+ # results: y = {:x => [1,2,3,4,5]}
82
+
83
+ Availablility
84
+ =============
85
+ SVN Repo here: http://trac.misuse.org/science/wiki/DeepMerge
86
+ Contact author: http://www.misuse.org/science
87
+
88
+ Copyright (c) 2008 Steve Midgley, released under the MIT license
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "drogus-deep_merge"
8
+ gem.summary = %Q{deep_merge}
9
+ gem.description = %Q{deep_merge}
10
+ gem.email = "drogus@gmail.com"
11
+ gem.homepage = "http://github.com/drogus/deep_merge"
12
+ gem.authors = ["Piotr Sarnacki"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "deep_merge #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
@@ -0,0 +1,212 @@
1
+ module DeepMerge
2
+
3
+ class InvalidParameter < StandardError; end
4
+
5
+ DEFAULT_FIELD_KNOCKOUT_PREFIX = '--'
6
+
7
+ module DeepMergeHash
8
+ # ko_hash_merge! will merge and knockout elements prefixed with DEFAULT_FIELD_KNOCKOUT_PREFIX
9
+ def ko_deeper_merge!(source, options = {})
10
+ default_opts = {:knockout_prefix => "--", :preserve_unmergeables => false}
11
+ DeepMerge::deep_merge!(source, self, default_opts.merge(options))
12
+ end
13
+
14
+ # deep_merge! will merge and overwrite any unmergeables in destination hash
15
+ def deeper_merge!(source, options = {})
16
+ default_opts = {:preserve_unmergeables => false}
17
+ DeepMerge::deep_merge!(source, self, default_opts.merge(options))
18
+ end
19
+
20
+ # deep_merge will merge and skip any unmergeables in destination hash
21
+ def deeper_merge(source, options = {})
22
+ default_opts = {:preserve_unmergeables => true}
23
+ DeepMerge::deep_merge!(source, self, default_opts.merge(options))
24
+ end
25
+
26
+ end # DeepMergeHashExt
27
+
28
+ # Deep Merge core documentation.
29
+ # deep_merge! method permits merging of arbitrary child elements. The two top level
30
+ # elements must be hashes. These hashes can contain unlimited (to stack limit) levels
31
+ # of child elements. These child elements to not have to be of the same types.
32
+ # Where child elements are of the same type, deep_merge will attempt to merge them together.
33
+ # Where child elements are not of the same type, deep_merge will skip or optionally overwrite
34
+ # the destination element with the contents of the source element at that level.
35
+ # So if you have two hashes like this:
36
+ # source = {:x => [1,2,3], :y => 2}
37
+ # dest = {:x => [4,5,'6'], :y => [7,8,9]}
38
+ # dest.deep_merge!(source)
39
+ # Results: {:x => [1,2,3,4,5,'6'], :y => 2}
40
+ # By default, "deep_merge!" will overwrite any unmergeables and merge everything else.
41
+ # To avoid this, use "deep_merge" (no bang/exclamation mark)
42
+ #
43
+ # Options:
44
+ # Options are specified in the last parameter passed, which should be in hash format:
45
+ # hash.deep_merge!({:x => [1,2]}, {:knockout_prefix => '--'})
46
+ # :preserve_unmergeables DEFAULT: false
47
+ # Set to true to skip any unmergeable elements from source
48
+ # :knockout_prefix DEFAULT: nil
49
+ # Set to string value to signify prefix which deletes elements from existing element
50
+ # :sort_merged_arrays DEFAULT: false
51
+ # Set to true to sort all arrays that are merged together
52
+ # :unpack_arrays DEFAULT: nil
53
+ # Set to string value to run "Array::join" then "String::split" against all arrays
54
+ # :merge_debug DEFAULT: false
55
+ # Set to true to get console output of merge process for debugging
56
+ #
57
+ # Selected Options Details:
58
+ # :knockout_prefix => The purpose of this is to provide a way to remove elements
59
+ # from existing Hash by specifying them in a special way in incoming hash
60
+ # source = {:x => ['--1', '2']}
61
+ # dest = {:x => ['1', '3']}
62
+ # dest.ko_deep_merge!(source)
63
+ # Results: {:x => ['2','3']}
64
+ # Additionally, if the knockout_prefix is passed alone as a string, it will cause
65
+ # the entire element to be removed:
66
+ # source = {:x => '--'}
67
+ # dest = {:x => [1,2,3]}
68
+ # dest.ko_deep_merge!(source)
69
+ # Results: {:x => ""}
70
+ # :unpack_arrays => The purpose of this is to permit compound elements to be passed
71
+ # in as strings and to be converted into discrete array elements
72
+ # irsource = {:x => ['1,2,3', '4']}
73
+ # dest = {:x => ['5','6','7,8']}
74
+ # dest.deep_merge!(source, {:unpack_arrays => ','})
75
+ # Results: {:x => ['1','2','3','4','5','6','7','8'}
76
+ # Why: If receiving data from an HTML form, this makes it easy for a checkbox
77
+ # to pass multiple values from within a single HTML element
78
+ #
79
+ # There are many tests for this library - and you can learn more about the features
80
+ # and usages of deep_merge! by just browsing the test examples
81
+ def DeepMerge.deep_merge!(source, dest, options = {})
82
+ # turn on this line for stdout debugging text
83
+ merge_debug = options[:merge_debug] || false
84
+ overwrite_unmergeable = !options[:preserve_unmergeables]
85
+ knockout_prefix = options[:knockout_prefix] || nil
86
+ raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!" if knockout_prefix == ""
87
+ raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!" if knockout_prefix && !overwrite_unmergeable
88
+ # if present: we will split and join arrays on this char before merging
89
+ array_split_char = options[:unpack_arrays] || false
90
+ # request that we sort together any arrays when they are merged
91
+ sort_merged_arrays = options[:sort_merged_arrays] || false
92
+ di = options[:debug_indent] || ''
93
+ # do nothing if source is nil
94
+ return dest if !source || (source.respond_to?(:blank?) && source.blank?)
95
+ # if dest doesn't exist, then simply copy source to it
96
+ if !(dest) && overwrite_unmergeable
97
+ dest = source; return dest
98
+ end
99
+
100
+ puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
101
+ if source.kind_of?(Hash)
102
+ puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
103
+ source.each do |src_key, src_value|
104
+ if dest.kind_of?(Hash)
105
+ puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
106
+ if dest[src_key]
107
+ puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
108
+ dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
109
+ else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
110
+ puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
111
+ # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
112
+ begin
113
+ src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
114
+ rescue TypeError
115
+ src_dup = src_value
116
+ end
117
+ dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
118
+ end
119
+ else # dest isn't a hash, so we overwrite it completely (if permitted)
120
+ if overwrite_unmergeable
121
+ puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
122
+ dest = overwrite_unmergeables(source, dest, options)
123
+ end
124
+ end
125
+ end
126
+ elsif source.kind_of?(Array)
127
+ puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
128
+ # if we are instructed, join/split any source arrays before processing
129
+ if array_split_char
130
+ puts "#{di} split/join on source: #{source.inspect}" if merge_debug
131
+ source = source.join(array_split_char).split(array_split_char)
132
+ if dest.kind_of?(Array)
133
+ dest = dest.join(array_split_char).split(array_split_char)
134
+ end
135
+ end
136
+ # if there's a naked knockout_prefix in source, that means we are to truncate dest
137
+ if source.index(knockout_prefix)
138
+ dest = clear_or_nil(dest); source.delete(knockout_prefix)
139
+ end
140
+ if dest.kind_of?(Array)
141
+ if knockout_prefix
142
+ print "#{di} knocking out: " if merge_debug
143
+ # remove knockout prefix items from both source and dest
144
+ source.delete_if do |ko_item|
145
+ retval = false
146
+ item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
147
+ if item != ko_item
148
+ print "#{ko_item} - " if merge_debug
149
+ dest.delete(item)
150
+ dest.delete(ko_item)
151
+ retval = true
152
+ end
153
+ retval
154
+ end
155
+ puts if merge_debug
156
+ end
157
+ puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
158
+ dest = dest | source
159
+ dest.sort! if sort_merged_arrays
160
+ elsif overwrite_unmergeable
161
+ puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
162
+ dest = overwrite_unmergeables(source, dest, options)
163
+ end
164
+ else # src_hash is not an array or hash, so we'll have to overwrite dest
165
+ puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
166
+ dest = overwrite_unmergeables(source, dest, options)
167
+ end
168
+ puts "#{di}Returning #{dest.inspect}" if merge_debug
169
+ dest
170
+ end # deep_merge!
171
+
172
+ # allows deep_merge! to uniformly handle overwriting of unmergeable entities
173
+ def DeepMerge::overwrite_unmergeables(source, dest, options)
174
+ merge_debug = options[:merge_debug] || false
175
+ overwrite_unmergeable = !options[:preserve_unmergeables]
176
+ knockout_prefix = options[:knockout_prefix] || false
177
+ di = options[:debug_indent] || ''
178
+ if knockout_prefix && overwrite_unmergeable
179
+ if source.kind_of?(String) # remove knockout string from source before overwriting dest
180
+ src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
181
+ elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
182
+ src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
183
+ else
184
+ src_tmp = source
185
+ end
186
+ if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
187
+ puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
188
+ dest = src_tmp
189
+ else # if we do find a knockout_prefix, then we just delete dest
190
+ puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
191
+ dest = ""
192
+ end
193
+ elsif overwrite_unmergeable
194
+ dest = source
195
+ end
196
+ dest
197
+ end
198
+
199
+ def DeepMerge::clear_or_nil(obj)
200
+ if obj.respond_to?(:clear)
201
+ obj.clear
202
+ else
203
+ obj = nil
204
+ end
205
+ obj
206
+ end
207
+
208
+ end # module DeepMerge
209
+
210
+ class Hash
211
+ include DeepMerge::DeepMergeHash
212
+ end
@@ -0,0 +1,553 @@
1
+ require 'test/unit'
2
+ require '../lib/deep_merge.rb'
3
+
4
+ class TestDeepMerge < Test::Unit::TestCase
5
+
6
+ def setup
7
+ end
8
+
9
+ # show that Hash object has deep merge capabilities in form of three methods:
10
+ # ko_deep_merge! # uses '--' knockout and overwrites unmergeable
11
+ # deep_merge! # overwrites unmergeable
12
+ # deep_merge # skips unmergeable
13
+ def test_hash_deep_merge
14
+ x = {}
15
+ assert x.respond_to?('deep_merge!'.to_sym)
16
+ hash_src = {'id' => [3,4,5]}
17
+ hash_dest = {'id' => [1,2,3]}
18
+ assert hash_dest.ko_deep_merge!(hash_src)
19
+ assert_equal({'id' => [1,2,3,4,5]}, hash_dest)
20
+
21
+ hash_src = {'id' => [3,4,5]}
22
+ hash_dest = {'id' => [1,2,3]}
23
+ assert hash_dest.deep_merge!(hash_src)
24
+ assert_equal({'id' => [1,2,3,4,5]}, hash_dest)
25
+
26
+ hash_src = {'id' => 'xxx'}
27
+ hash_dest = {'id' => [1,2,3]}
28
+ assert hash_dest.deep_merge(hash_src)
29
+ assert_equal({'id' => [1,2,3]}, hash_dest)
30
+ end
31
+
32
+ FIELD_KNOCKOUT_PREFIX = DeepMerge::DEFAULT_FIELD_KNOCKOUT_PREFIX
33
+
34
+ # tests DeepMerge::deep_merge! function
35
+ def test_deep_merge
36
+ # merge tests (moving from basic to more complex)
37
+
38
+ # test merging an hash w/array into blank hash
39
+ hash_src = {'id' => '2'}
40
+ hash_dst = {}
41
+ DeepMerge::deep_merge!(hash_src.dup, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
42
+ assert_equal hash_src, hash_dst
43
+
44
+ # test merging an hash w/array into blank hash
45
+ hash_src = {'region' => {'id' => ['227', '2']}}
46
+ hash_dst = {}
47
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
48
+ assert_equal hash_src, hash_dst
49
+
50
+ # merge from empty hash
51
+ hash_src = {}
52
+ hash_dst = {"property" => ["2","4"]}
53
+ DeepMerge::deep_merge!(hash_src, hash_dst)
54
+ assert_equal({"property" => ["2","4"]}, hash_dst)
55
+
56
+ # merge to empty hash
57
+ hash_src = {"property" => ["2","4"]}
58
+ hash_dst = {}
59
+ DeepMerge::deep_merge!(hash_src, hash_dst)
60
+ assert_equal({"property" => ["2","4"]}, hash_dst)
61
+
62
+ # simple string overwrite
63
+ hash_src = {"name" => "value"}
64
+ hash_dst = {"name" => "value1"}
65
+ DeepMerge::deep_merge!(hash_src, hash_dst)
66
+ assert_equal({"name" => "value"}, hash_dst)
67
+
68
+ # simple string overwrite of empty hash
69
+ hash_src = {"name" => "value"}
70
+ hash_dst = {}
71
+ DeepMerge::deep_merge!(hash_src, hash_dst)
72
+ assert_equal(hash_src, hash_dst)
73
+
74
+ # hashes holding array
75
+ hash_src = {"property" => ["1","3"]}
76
+ hash_dst = {"property" => ["2","4"]}
77
+ DeepMerge::deep_merge!(hash_src, hash_dst)
78
+ assert_equal(["2","4","1","3"], hash_dst['property'])
79
+
80
+ # hashes holding array (sorted)
81
+ hash_src = {"property" => ["1","3"]}
82
+ hash_dst = {"property" => ["2","4"]}
83
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:sort_merged_arrays => true})
84
+ assert_equal(["1","2","3","4"].sort, hash_dst['property'])
85
+
86
+ # hashes holding hashes holding arrays (array with duplicate elements is merged with dest then src
87
+ hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
88
+ hash_dst = {"property" => {"bedroom_count" => ["3", "2"], "bathroom_count" => ["2"]}}
89
+ DeepMerge::deep_merge!(hash_src, hash_dst)
90
+ assert_equal({"property" => {"bedroom_count" => ["3","2","1"], "bathroom_count" => ["2", "1", "4+"]}}, hash_dst)
91
+
92
+ # hash holding hash holding array v string (string is overwritten by array)
93
+ hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
94
+ hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}}
95
+ DeepMerge::deep_merge!(hash_src, hash_dst)
96
+ assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst)
97
+
98
+ # hash holding hash holding array v string (string is NOT overwritten by array)
99
+ hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
100
+ hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}}
101
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
102
+ assert_equal({"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}, hash_dst)
103
+
104
+ # hash holding hash holding string v array (array is overwritten by string)
105
+ hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}}
106
+ hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
107
+ DeepMerge::deep_merge!(hash_src, hash_dst)
108
+ assert_equal({"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}, hash_dst)
109
+
110
+ # hash holding hash holding string v array (array does NOT overwrite string)
111
+ hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}}
112
+ hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
113
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
114
+ assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst)
115
+
116
+ # hash holding hash holding hash v array (array is overwritten by hash)
117
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
118
+ hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
119
+ DeepMerge::deep_merge!(hash_src, hash_dst)
120
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}, hash_dst)
121
+
122
+ # hash holding hash holding hash v array (array is NOT overwritten by hash)
123
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
124
+ hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
125
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
126
+ assert_equal({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}, hash_dst)
127
+
128
+ # 3 hash layers holding integers (integers are overwritten by source)
129
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
130
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => 2, "queen_bed" => 4}, "bathroom_count" => ["2"]}}
131
+ DeepMerge::deep_merge!(hash_src, hash_dst)
132
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}, hash_dst)
133
+
134
+ # 3 hash layers holding arrays of int (arrays are merged)
135
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => ["1", "4+"]}}
136
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
137
+ DeepMerge::deep_merge!(hash_src, hash_dst)
138
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2","1","4+"]}}, hash_dst)
139
+
140
+ # 1 hash overwriting 3 hash layers holding arrays of int
141
+ hash_src = {"property" => "1"}
142
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
143
+ DeepMerge::deep_merge!(hash_src, hash_dst)
144
+ assert_equal({"property" => "1"}, hash_dst)
145
+
146
+ # 1 hash NOT overwriting 3 hash layers holding arrays of int
147
+ hash_src = {"property" => "1"}
148
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
149
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
150
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}, hash_dst)
151
+
152
+ # 3 hash layers holding arrays of int (arrays are merged) but second hash's array is overwritten
153
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}}
154
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
155
+ DeepMerge::deep_merge!(hash_src, hash_dst)
156
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => "1"}}, hash_dst)
157
+
158
+ # 3 hash layers holding arrays of int (arrays are merged) but second hash's array is NOT overwritten
159
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}}
160
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
161
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
162
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2"]}}, hash_dst)
163
+
164
+ # 3 hash layers holding arrays of int, but one holds int. This one overwrites, but the rest merge
165
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [1]}, "bathroom_count" => ["1"]}}
166
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
167
+ DeepMerge::deep_merge!(hash_src, hash_dst)
168
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [4,1]}, "bathroom_count" => ["2","1"]}}, hash_dst)
169
+
170
+ # 3 hash layers holding arrays of int, but source is incomplete.
171
+ hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3]}, "bathroom_count" => ["1"]}}
172
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
173
+ DeepMerge::deep_merge!(hash_src, hash_dst)
174
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}, hash_dst)
175
+
176
+ # 3 hash layers holding arrays of int, but source is shorter and has new 2nd level ints.
177
+ hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}
178
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
179
+ DeepMerge::deep_merge!(hash_src, hash_dst)
180
+ assert_equal({"property" => {"bedroom_count" => {2=>3, "king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}, hash_dst)
181
+
182
+ # 3 hash layers holding arrays of int, but source is empty
183
+ hash_src = {}
184
+ hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
185
+ DeepMerge::deep_merge!(hash_src, hash_dst)
186
+ assert_equal({"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}, hash_dst)
187
+
188
+ # 3 hash layers holding arrays of int, but dest is empty
189
+ hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}
190
+ hash_dst = {}
191
+ DeepMerge::deep_merge!(hash_src, hash_dst)
192
+ assert_equal({"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}, hash_dst)
193
+
194
+ # test parameter management for knockout_prefix and overwrite unmergable
195
+ assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => ""})}
196
+ assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => ""})}
197
+ assert_raise(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => "--"})}
198
+ assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--"})}
199
+ assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst)}
200
+ assert_nothing_raised(DeepMerge::InvalidParameter) {DeepMerge::deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})}
201
+
202
+ # hash holding arrays of arrays
203
+ hash_src = {["1", "2", "3"] => ["1", "2"]}
204
+ hash_dst = {["4", "5"] => ["3"]}
205
+ DeepMerge::deep_merge!(hash_src, hash_dst)
206
+ assert_equal({["1","2","3"] => ["1", "2"], ["4", "5"] => ["3"]}, hash_dst)
207
+
208
+ # test merging of hash with blank hash, and make sure that source array split still functions
209
+ hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}}
210
+ hash_dst = {}
211
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
212
+ assert_equal({'property' => {'bedroom_count' => ["1","2","3"]}}, hash_dst)
213
+
214
+ # test merging of hash with blank hash, and make sure that source array split does not function when turned off
215
+ hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}}
216
+ hash_dst = {}
217
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
218
+ assert_equal({'property' => {'bedroom_count' => ["1","2,3"]}}, hash_dst)
219
+
220
+ # test merging into a blank hash with overwrite_unmergeables turned on
221
+ hash_src = {"action"=>"browse", "controller"=>"results"}
222
+ hash_dst = {}
223
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
224
+ assert_equal hash_src, hash_dst
225
+
226
+ # KNOCKOUT_PREFIX testing
227
+ # the next few tests are looking for correct behavior from specific real-world params/session merges
228
+ # using the custom modifiers built for param/session merges
229
+
230
+ [nil, ","].each do |ko_split|
231
+ # typical params/session style hash with knockout_merge elements
232
+ hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
233
+ hash_session = {"property"=>{"bedroom_count"=>["1", "2", "3"]}}
234
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
235
+ assert_equal({"property"=>{"bedroom_count"=>["2", "3"]}}, hash_session)
236
+
237
+ # typical params/session style hash with knockout_merge elements
238
+ hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
239
+ hash_session = {"property"=>{"bedroom_count"=>["3"]}}
240
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
241
+ assert_equal({"property"=>{"bedroom_count"=>["3","2"]}}, hash_session)
242
+
243
+ # typical params/session style hash with knockout_merge elements
244
+ hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
245
+ hash_session = {"property"=>{"bedroom_count"=>["4"]}}
246
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
247
+ assert_equal({"property"=>{"bedroom_count"=>["4","2","3"]}}, hash_session)
248
+
249
+ # typical params/session style hash with knockout_merge elements
250
+ hash_params = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
251
+ hash_session = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "4"]}}
252
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
253
+ assert_equal({"property"=>{"bedroom_count"=>["4","2","3"]}}, hash_session)
254
+
255
+ # typical params/session style hash with knockout_merge elements
256
+ hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1", FIELD_KNOCKOUT_PREFIX+"2", "3", "4"]}}
257
+ hash_session = {"amenity"=>{"id"=>["1", "2"]}}
258
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
259
+ assert_equal({"amenity"=>{"id"=>["3","4"]}}, hash_session)
260
+ end
261
+
262
+ # special params/session style hash with knockout_merge elements in form src: ["1","2"] dest:["--1,--2", "3,4"]
263
+ hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}}
264
+ hash_session = {"amenity"=>{"id"=>["1", "2"]}}
265
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
266
+ assert_equal({"amenity"=>{"id"=>["3","4"]}}, hash_session)
267
+
268
+ # same as previous but without ko_split value, this merge should fail
269
+ hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}}
270
+ hash_session = {"amenity"=>{"id"=>["1", "2"]}}
271
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
272
+ assert_equal({"amenity"=>{"id"=>["1","2","3,4"]}}, hash_session)
273
+
274
+ # special params/session style hash with knockout_merge elements in form src: ["1","2"] dest:["--1,--2", "3,4"]
275
+ hash_params = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,2", "3,4", "--5", "6"]}}
276
+ hash_session = {"amenity"=>{"id"=>["1", "2"]}}
277
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
278
+ assert_equal({"amenity"=>{"id"=>["2","3","4","6"]}}, hash_session)
279
+
280
+ # special params/session style hash with knockout_merge elements in form src: ["--1,--2", "3,4", "--5", "6"] dest:["1,2", "3,4"]
281
+ hash_params = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}1,#{FIELD_KNOCKOUT_PREFIX}2", "3,4", "#{FIELD_KNOCKOUT_PREFIX}5", "6"]}}
282
+ hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
283
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
284
+ assert_equal({"amenity"=>{"id"=>["3","4","6"]}}, hash_session)
285
+
286
+
287
+ hash_src = {"url_regions"=>[], "region"=>{"ids"=>["227,233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"}
288
+ hash_dst = {"region"=>{"ids"=>["227"]}}
289
+ DeepMerge::deep_merge!(hash_src.dup, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
290
+ assert_equal({"url_regions"=>[], "region"=>{"ids"=>["227","233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"}, hash_dst)
291
+
292
+ hash_src = {"region"=>{"ids"=>["--","227"], "id"=>"230"}}
293
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
294
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
295
+ assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}}, hash_dst)
296
+
297
+ hash_src = {"region"=>{"ids"=>["--","227", "232", "233"], "id"=>"232"}}
298
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
299
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
300
+ assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst)
301
+
302
+ hash_src = {"region"=>{"ids"=>["--,227,232,233"], "id"=>"232"}}
303
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
304
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
305
+ assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst)
306
+
307
+ hash_src = {"region"=>{"ids"=>["--,227,232","233"], "id"=>"232"}}
308
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
309
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
310
+ assert_equal({"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}, hash_dst)
311
+
312
+ hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}}
313
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
314
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
315
+ assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}}, hash_dst)
316
+
317
+ hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}}
318
+ hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"}
319
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
320
+ assert_equal({"region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse",
321
+ "controller"=>"results", "property_order_by"=>"property_type.descr"}, hash_dst)
322
+
323
+ hash_src = {"query_uuid"=>"6386333d-389b-ab5c-8943-6f3a2aa914d7", "region"=>{"ids"=>["--,227"], "id"=>"230"}}
324
+ 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"}
325
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
326
+ assert_equal({"query_uuid" => "6386333d-389b-ab5c-8943-6f3a2aa914d7", "url_regions"=>[],
327
+ "region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse",
328
+ "controller"=>"results", "property_order_by"=>"property_type.descr"}, hash_dst)
329
+
330
+ # knock out entire dest hash if "--" is passed for source
331
+ hash_params = {'amenity' => "--"}
332
+ hash_session = {"amenity" => "1"}
333
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
334
+ assert_equal({'amenity' => ""}, hash_session)
335
+
336
+ # knock out entire dest hash if "--" is passed for source
337
+ hash_params = {'amenity' => ["--"]}
338
+ hash_session = {"amenity" => "1"}
339
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
340
+ assert_equal({'amenity' => []}, hash_session)
341
+
342
+ # knock out entire dest hash if "--" is passed for source
343
+ hash_params = {'amenity' => "--"}
344
+ hash_session = {"amenity" => ["1"]}
345
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
346
+ assert_equal({'amenity' => ""}, hash_session)
347
+
348
+ # knock out entire dest hash if "--" is passed for source
349
+ hash_params = {'amenity' => ["--"]}
350
+ hash_session = {"amenity" => ["1"]}
351
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
352
+ assert_equal({'amenity' => []}, hash_session)
353
+
354
+ # knock out entire dest hash if "--" is passed for source
355
+ hash_params = {'amenity' => ["--"]}
356
+ hash_session = {"amenity" => "1"}
357
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
358
+ assert_equal({'amenity' => []}, hash_session)
359
+
360
+ # knock out entire dest hash if "--" is passed for source
361
+ hash_params = {'amenity' => ["--", "2"]}
362
+ hash_session = {'amenity' => ["1", "3", "7+"]}
363
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
364
+ assert_equal({'amenity' => ["2"]}, hash_session)
365
+
366
+ # knock out entire dest hash if "--" is passed for source
367
+ hash_params = {'amenity' => ["--", "2"]}
368
+ hash_session = {'amenity' => "5"}
369
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
370
+ assert_equal({'amenity' => ['2']}, hash_session)
371
+
372
+ # knock out entire dest hash if "--" is passed for source
373
+ hash_params = {'amenity' => "--"}
374
+ hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
375
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
376
+ assert_equal({'amenity' => ""}, hash_session)
377
+
378
+ # knock out entire dest hash if "--" is passed for source
379
+ hash_params = {'amenity' => ["--"]}
380
+ hash_session = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
381
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => "--", :unpack_arrays => ","})
382
+ assert_equal({'amenity' => []}, hash_session)
383
+
384
+ # knock out dest array if "--" is passed for source
385
+ hash_params = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}}
386
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"]}}
387
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
388
+ assert_equal({'region' => {'ids' => ""}}, hash_session)
389
+
390
+ # knock out dest array but leave other elements of hash intact
391
+ hash_params = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}}
392
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
393
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
394
+ assert_equal({'region' => {'ids' => "", 'id'=>'11'}}, hash_session)
395
+
396
+ # knock out entire tree of dest hash
397
+ hash_params = {"region" => FIELD_KNOCKOUT_PREFIX}
398
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
399
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
400
+ assert_equal({'region' => ""}, hash_session)
401
+
402
+ # knock out entire tree of dest hash - retaining array format
403
+ hash_params = {"region" => {'ids' => [FIELD_KNOCKOUT_PREFIX]}}
404
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
405
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
406
+ assert_equal({'region' => {'ids' => [], 'id'=>'11'}}, hash_session)
407
+
408
+ # knock out entire tree of dest hash & replace with new content
409
+ hash_params = {"region" => {'ids' => ["2", FIELD_KNOCKOUT_PREFIX, "6"]}}
410
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
411
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
412
+ assert_equal({'region' => {'ids' => ["2", "6"], 'id'=>'11'}}, hash_session)
413
+
414
+ # knock out entire tree of dest hash & replace with new content
415
+ hash_params = {"region" => {'ids' => ["7", FIELD_KNOCKOUT_PREFIX, "6"]}}
416
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
417
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
418
+ assert_equal({'region' => {'ids' => ["7", "6"], 'id'=>'11'}}, hash_session)
419
+
420
+ # edge test: make sure that when we turn off knockout_prefix that all values are processed correctly
421
+ hash_params = {"region" => {'ids' => ["7", "--", "2", "6,8"]}}
422
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
423
+ DeepMerge::deep_merge!(hash_params, hash_session, {:unpack_arrays => ","})
424
+ assert_equal({'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6", "8"], 'id'=>'11'}}, hash_session)
425
+
426
+ # edge test 2: make sure that when we turn off source array split that all values are processed correctly
427
+ hash_params = {"region" => {'ids' => ["7", "3", "--", "6,8"]}}
428
+ hash_session = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
429
+ DeepMerge::deep_merge!(hash_params, hash_session)
430
+ assert_equal({'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6,8"], 'id'=>'11'}}, hash_session)
431
+
432
+ # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""}
433
+ hash_params = {"amenity"=>"--1"}
434
+ hash_session = {"amenity"=>"1"}
435
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
436
+ assert_equal({"amenity"=>""}, hash_session)
437
+
438
+ # Example: src = {'key' => "--1"}, dst = {'key' => "2"} -> merges to {'key' => ""}
439
+ hash_params = {"amenity"=>"--1"}
440
+ hash_session = {"amenity"=>"2"}
441
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
442
+ assert_equal({"amenity"=>""}, hash_session)
443
+
444
+ # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""}
445
+ hash_params = {"amenity"=>["--1"]}
446
+ hash_session = {"amenity"=>"1"}
447
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
448
+ assert_equal({"amenity"=>[]}, hash_session)
449
+
450
+ # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""}
451
+ hash_params = {"amenity"=>["--1"]}
452
+ hash_session = {"amenity"=>["1"]}
453
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
454
+ assert_equal({"amenity"=>[]}, hash_session)
455
+
456
+ # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""}
457
+ hash_params = {"amenity"=>"--1"}
458
+ hash_session = {}
459
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
460
+ assert_equal({"amenity"=>""}, hash_session)
461
+
462
+
463
+ # Example: src = {'key' => "--1"}, dst = {'key' => "1"} -> merges to {'key' => ""}
464
+ hash_params = {"amenity"=>"--1"}
465
+ hash_session = {"amenity"=>["1"]}
466
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
467
+ assert_equal({"amenity"=>""}, hash_session)
468
+
469
+ #are unmerged hashes passed unmodified w/out :unpack_arrays?
470
+ hash_params = {"amenity"=>{"id"=>["26,27"]}}
471
+ hash_session = {}
472
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
473
+ assert_equal({"amenity"=>{"id"=>["26,27"]}}, hash_session)
474
+
475
+ #hash should be merged
476
+ hash_params = {"amenity"=>{"id"=>["26,27"]}}
477
+ hash_session = {}
478
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
479
+ assert_equal({"amenity"=>{"id"=>["26","27"]}}, hash_session)
480
+
481
+ # second merge of same values should result in no change in output
482
+ hash_params = {"amenity"=>{"id"=>["26,27"]}}
483
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
484
+ assert_equal({"amenity"=>{"id"=>["26","27"]}}, hash_session)
485
+
486
+ #hashes with knockout values are suppressed
487
+ hash_params = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}26,#{FIELD_KNOCKOUT_PREFIX}27,28"]}}
488
+ hash_session = {}
489
+ DeepMerge::deep_merge!(hash_params, hash_session, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
490
+ assert_equal({"amenity"=>{"id"=>["28"]}}, hash_session)
491
+
492
+ hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
493
+ hash_dst= {'region' =>{'ids'=>['227','2','3','3']}, 'query_uuid' => 'zzz'}
494
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
495
+ assert_equal({'region' =>{'ids'=>[]}, 'query_uuid' => 'zzz'}, hash_dst)
496
+
497
+ hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
498
+ hash_dst= {'region' =>{'ids'=>['227','2','3','3'], 'id' => '3'}, 'query_uuid' => 'zzz'}
499
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
500
+ assert_equal({'region' =>{'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}, hash_dst)
501
+
502
+ hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
503
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
504
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
505
+ assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}, hash_dst)
506
+
507
+ hash_src= {'region' =>{'ids'=>['--'], 'id' => '5'}, 'query_uuid' => 'zzz'}
508
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
509
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
510
+ assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst)
511
+
512
+ hash_src= {'region' =>{'ids'=>['--', '227'], 'id' => '5'}, 'query_uuid' => 'zzz'}
513
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
514
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
515
+ assert_equal({'region' =>{'muni_city_id' => '2244', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst)
516
+
517
+ hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>'--', 'id'=>'5'}, 'query_uuid' => 'zzz'}
518
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
519
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
520
+ assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>'', 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst)
521
+
522
+ hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
523
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
524
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
525
+ assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst)
526
+
527
+ hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--','227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
528
+ hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
529
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
530
+ assert_equal({'region' =>{'muni_city_id' => '', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}, hash_dst)
531
+
532
+ hash_src = {"muni_city_id"=>"--", "id"=>""}
533
+ hash_dst = {"muni_city_id"=>"", "id"=>""}
534
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
535
+ assert_equal({"muni_city_id"=>"", "id"=>""}, hash_dst)
536
+
537
+ hash_src = {"region"=>{"muni_city_id"=>"--", "id"=>""}}
538
+ hash_dst = {"region"=>{"muni_city_id"=>"", "id"=>""}}
539
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
540
+ assert_equal({"region"=>{"muni_city_id"=>"", "id"=>""}}, hash_dst)
541
+
542
+ 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"}
543
+ 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"}
544
+ DeepMerge::deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
545
+ 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)
546
+
547
+ # hash of array of hashes
548
+ hash_src = {"item" => [{"1" => "3"}, {"2" => "4"}]}
549
+ hash_dst = {"item" => [{"3" => "5"}]}
550
+ DeepMerge::deep_merge!(hash_src, hash_dst)
551
+ assert_equal({"item" => [{"3" => "5"}, {"1" => "3"}, {"2" => "4"}]}, hash_dst)
552
+ end # test_deep_merge
553
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drogus-deep_merge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Sarnacki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-27 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: deep_merge
17
+ email: drogus@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - .document
26
+ - .gitignore
27
+ - README
28
+ - Rakefile
29
+ - lib/deep_merge.rb
30
+ - test/test_deep_merge.rb
31
+ has_rdoc: true
32
+ homepage: http://github.com/drogus/deep_merge
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --charset=UTF-8
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.3.5
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: deep_merge
59
+ test_files:
60
+ - test/test_deep_merge.rb