deep_merge 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'