deep_merge 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ 2011-05-23 Joe Van Dyk <joe@fixieconsulting.com>
2
+
3
+ * Added Changelog
4
+
5
+ 2011-05-18 Joe Van Dyk <joe@fixieconsulting.com>
6
+
7
+ * Merging empty strings should work if String#blank? is defined.
8
+
9
+ * Use unix line endings
10
+
11
+ * Removing extra whitespace
12
+
13
+ 2010-01-11 Dan DeLeo <danielsdeleo@mac.com>
14
+
15
+ * fix boolean merging according to mdkent's patch explicitly test
16
+ for nils w/ #nil? instead of negating. Thanks mdkent!
17
+
18
+ 2009-12-25 Dan DeLeo <danielsdeleo@mac.com>
19
+
20
+ * miscellaneous cleanup
21
+
22
+ * make rails/active_support compat optional
23
+
24
+ * add jeweler rake task for gemability
25
+
26
+ 2009-12-24 Dan DeLeo <danielsdeleo@mac.com>
27
+
28
+ * VERSION: Version bump to 0.0.1
29
+
30
+ * VERSION: Version bump to 0.0.0
31
+
32
+ 2009-11-06 Jonathan Weiss <jw@innerewut.de>
33
+
34
+ * import
data/README CHANGED
@@ -1,88 +1,95 @@
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
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_hash_arrays DEFAULT: false
46
+ Set to true to merge hashes within arrays
47
+ :merge_debug DEFAULT: false
48
+ Set to true to get console output of merge process for debugging
49
+
50
+ Selected Options Details:
51
+ :knockout_prefix => The purpose of this is to provide a way to remove elements
52
+ from existing Hash by specifying them in a special way in incoming hash
53
+ source = {:x => ['--1', '2']}
54
+ dest = {:x => ['1', '3']}
55
+ dest.ko_deep_merge!(source)
56
+ Results: {:x => ['2','3']}
57
+ Additionally, if the knockout_prefix is passed alone as a string, it will cause
58
+ the entire element to be removed:
59
+ source = {:x => '--'}
60
+ dest = {:x => [1,2,3]}
61
+ dest.ko_deep_merge!(source)
62
+ Results: {:x => ""}
63
+ :unpack_arrays => The purpose of this is to permit compound elements to be passed
64
+ in as strings and to be converted into discrete array elements
65
+ irsource = {:x => ['1,2,3', '4']}
66
+ dest = {:x => ['5','6','7,8']}
67
+ dest.deep_merge!(source, {:unpack_arrays => ','})
68
+ Results: {:x => ['1','2','3','4','5','6','7','8'}
69
+ Why: If receiving data from an HTML form, this makes it easy for a checkbox
70
+ to pass multiple values from within a single HTML element
71
+ :merge_hash_arrays => merge hashes within arrays
72
+ source = {:x => [{:y => 1}]}
73
+ dest = {:x => [{:z => 2}]}
74
+ dest.deep_merge!(source, {:merge_hash_arrays => true})
75
+ Results: {:x => [{:y => 1, :z => 2}]}
76
+
77
+ There are many tests for this library - and you can learn more about the features
78
+ and usages of deep_merge! by just browsing the test examples
79
+
80
+
81
+ Simple Example Code
82
+ ===================
83
+
84
+ require 'deep_merge'
85
+ x = {:x => [3,4,5]}
86
+ y = {:x => [1,2,3]}
87
+ y.deep_merge!(x)
88
+ # results: y = {:x => [1,2,3,4,5]}
89
+
90
+ Availablility
91
+ =============
92
+ SVN Repo here: http://trac.misuse.org/science/wiki/DeepMerge
93
+ Contact author: http://www.misuse.org/science
94
+
95
+ Copyright (c) 2008 Steve Midgley, released under the MIT license
@@ -0,0 +1,19 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << FileList['lib/**.rb']
5
+ t.test_files = FileList['test/test*.rb']
6
+ end
7
+
8
+ task :default => :test
9
+
10
+ begin
11
+ require 'rubygems'
12
+ require 'rubygems/package_task'
13
+
14
+ gemspec = eval(IO.read('deep_merge.gemspec'))
15
+ Gem::PackageTask.new(gemspec).define
16
+ rescue LoadError
17
+ #okay, then
18
+ end
19
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -1,206 +1,2 @@
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_deep_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 deep_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 deep_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
- if knockout_prefix == "" : raise InvalidParameter, "knockout_prefix cannot be an empty string in deep_merge!"; end
87
- if knockout_prefix && !overwrite_unmergeable : raise InvalidParameter, "overwrite_unmergeable must be true if knockout_prefix is specified in deep_merge!"; end
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
- if !source || (source.respond_to?(:blank?) && source.blank?) : return dest; end
95
- # if dest doesn't exist, then simply copy source to it
96
- if !(dest) && overwrite_unmergeable : dest = source; return dest; end
97
-
98
- puts "#{di}Source class: #{source.class.inspect} :: Dest class: #{dest.class.inspect}" if merge_debug
99
- if source.kind_of?(Hash)
100
- puts "#{di}Hashes: #{source.inspect} :: #{dest.inspect}" if merge_debug
101
- source.each do |src_key, src_value|
102
- if dest.kind_of?(Hash)
103
- puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
104
- if dest[src_key]
105
- puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
106
- dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(:debug_indent => di + ' '))
107
- else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
108
- puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
109
- # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
110
- begin
111
- src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
112
- rescue TypeError
113
- src_dup = src_value
114
- end
115
- dest[src_key] = deep_merge!(src_value, src_dup, options.merge(:debug_indent => di + ' '))
116
- end
117
- else # dest isn't a hash, so we overwrite it completely (if permitted)
118
- if overwrite_unmergeable
119
- puts "#{di} overwriting dest: #{src_key.inspect} => #{src_value.inspect} -over-> #{dest.inspect}" if merge_debug
120
- dest = overwrite_unmergeables(source, dest, options)
121
- end
122
- end
123
- end
124
- elsif source.kind_of?(Array)
125
- puts "#{di}Arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
126
- # if we are instructed, join/split any source arrays before processing
127
- if array_split_char
128
- puts "#{di} split/join on source: #{source.inspect}" if merge_debug
129
- source = source.join(array_split_char).split(array_split_char)
130
- if dest.kind_of?(Array) : dest = dest.join(array_split_char).split(array_split_char); end
131
- end
132
- # if there's a naked knockout_prefix in source, that means we are to truncate dest
133
- if source.index(knockout_prefix) : dest = clear_or_nil(dest); source.delete(knockout_prefix); end
134
- if dest.kind_of?(Array)
135
- if knockout_prefix
136
- print "#{di} knocking out: " if merge_debug
137
- # remove knockout prefix items from both source and dest
138
- source.delete_if do |ko_item|
139
- retval = false
140
- item = ko_item.respond_to?(:gsub) ? ko_item.gsub(%r{^#{knockout_prefix}}, "") : ko_item
141
- if item != ko_item
142
- print "#{ko_item} - " if merge_debug
143
- dest.delete(item)
144
- dest.delete(ko_item)
145
- retval = true
146
- end
147
- retval
148
- end
149
- puts if merge_debug
150
- end
151
- puts "#{di} merging arrays: #{source.inspect} :: #{dest.inspect}" if merge_debug
152
- dest = dest | source
153
- if sort_merged_arrays : dest.sort!; end
154
- elsif overwrite_unmergeable
155
- puts "#{di} overwriting dest: #{source.inspect} -over-> #{dest.inspect}" if merge_debug
156
- dest = overwrite_unmergeables(source, dest, options)
157
- end
158
- else # src_hash is not an array or hash, so we'll have to overwrite dest
159
- puts "#{di}Others: #{source.inspect} :: #{dest.inspect}" if merge_debug
160
- dest = overwrite_unmergeables(source, dest, options)
161
- end
162
- puts "#{di}Returning #{dest.inspect}" if merge_debug
163
- dest
164
- end # deep_merge!
165
-
166
- # allows deep_merge! to uniformly handle overwriting of unmergeable entities
167
- def DeepMerge::overwrite_unmergeables(source, dest, options)
168
- merge_debug = options[:merge_debug] || false
169
- overwrite_unmergeable = !options[:preserve_unmergeables]
170
- knockout_prefix = options[:knockout_prefix] || false
171
- di = options[:debug_indent] || ''
172
- if knockout_prefix && overwrite_unmergeable
173
- if source.kind_of?(String) # remove knockout string from source before overwriting dest
174
- src_tmp = source.gsub(%r{^#{knockout_prefix}},"")
175
- elsif source.kind_of?(Array) # remove all knockout elements before overwriting dest
176
- src_tmp = source.delete_if {|ko_item| ko_item.kind_of?(String) && ko_item.match(%r{^#{knockout_prefix}}) }
177
- else
178
- src_tmp = source
179
- end
180
- if src_tmp == source # if we didn't find a knockout_prefix then we just overwrite dest
181
- puts "#{di}#{src_tmp.inspect} -over-> #{dest.inspect}" if merge_debug
182
- dest = src_tmp
183
- else # if we do find a knockout_prefix, then we just delete dest
184
- puts "#{di}\"\" -over-> #{dest.inspect}" if merge_debug
185
- dest = ""
186
- end
187
- elsif overwrite_unmergeable
188
- dest = source
189
- end
190
- dest
191
- end
192
-
193
- def DeepMerge::clear_or_nil(obj)
194
- if obj.respond_to?(:clear)
195
- obj.clear
196
- else
197
- obj = nil
198
- end
199
- obj
200
- end
201
-
202
- end # module DeepMerge
203
-
204
- class Hash
205
- include DeepMerge::DeepMergeHash
206
- end
1
+ require 'deep_merge/core'
2
+ require 'deep_merge/deep_merge_hash'