deep_hash_transform 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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 538a3d6cd8c8b0d3c8b7625395514c097f9a047b
4
+ data.tar.gz: 456d6517d42225594a5a928db69c37d643f548f2
5
+ SHA512:
6
+ metadata.gz: 2286c0194b9bb0a9699e5ea6d542bd64992c4aac32f404457cef1c03b40b22bd259f85320d24d3fab662ac83cf32d9be068a6f816dd4590b5784419a059145e2
7
+ data.tar.gz: ca69c276a74b53d8ce7caa67b652afb8cfc9af9a847a9630ba8b58a3fd92ab22f3734d17815f27ec01330824bd930fff2091ed217bf4b49412d2d8cfcf09d91b
@@ -0,0 +1,135 @@
1
+ # These features are available in Rails 4.0+
2
+ #
3
+ # Prefer Active Support's implementation when available.
4
+ #
5
+ # This allows libraries which support multiple Rails versions to depend on
6
+ # `deep_hash_transform` without worrying about implementation collision.
7
+ # This lib will step aside if it sees its work is done.
8
+ begin
9
+ require 'active_support/core_ext/hash/keys'
10
+ rescue LoadError
11
+ # No worries if Active Support isn't present. Can be used in isolation.
12
+ end
13
+
14
+ class Hash
15
+ # Returns a new hash with all keys converted using the block operation.
16
+ #
17
+ # hash = { name: 'Rob', age: '28' }
18
+ #
19
+ # hash.transform_keys{ |key| key.to_s.upcase }
20
+ # # => {"NAME"=>"Rob", "AGE"=>"28"}
21
+ def transform_keys
22
+ result = {}
23
+ each_key do |key|
24
+ result[yield(key)] = self[key]
25
+ end
26
+ result
27
+ end unless method_defined?(:transform_keys)
28
+
29
+ # Destructively convert all keys using the block operations.
30
+ # Same as transform_keys but modifies +self+.
31
+ def transform_keys!
32
+ keys.each do |key|
33
+ self[yield(key)] = delete(key)
34
+ end
35
+ self
36
+ end unless method_defined?(:transform_keys!)
37
+
38
+ # Returns a new hash with all keys converted to strings.
39
+ #
40
+ # hash = { name: 'Rob', age: '28' }
41
+ #
42
+ # hash.stringify_keys
43
+ # # => { "name" => "Rob", "age" => "28" }
44
+ def stringify_keys
45
+ transform_keys { |key| key.to_s }
46
+ end unless method_defined?(:stringify_keys)
47
+
48
+ # Destructively convert all keys to strings. Same as
49
+ # +stringify_keys+, but modifies +self+.
50
+ def stringify_keys!
51
+ transform_keys! { |key| key.to_s }
52
+ end unless method_defined?(:stringify_keys!)
53
+
54
+ # Returns a new hash with all keys converted to symbols, as long as
55
+ # they respond to +to_sym+.
56
+ #
57
+ # hash = { 'name' => 'Rob', 'age' => '28' }
58
+ #
59
+ # hash.symbolize_keys
60
+ # # => { name: "Rob", age: "28" }
61
+ def symbolize_keys
62
+ transform_keys { |key| key.to_sym rescue key }
63
+ end unless method_defined?(:symbolize_keys)
64
+
65
+ # Destructively convert all keys to symbols, as long as they respond
66
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
67
+ def symbolize_keys!
68
+ transform_keys! { |key| key.to_sym rescue key }
69
+ end unless method_defined?(:symbolize_keys!)
70
+
71
+ # Returns a new hash with all keys converted by the block operation.
72
+ # This includes the keys from the root hash and from all
73
+ # nested hashes.
74
+ #
75
+ # hash = { person: { name: 'Rob', age: '28' } }
76
+ #
77
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
78
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
79
+ def deep_transform_keys(&block)
80
+ result = {}
81
+ each do |key, value|
82
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
83
+ end
84
+ result
85
+ end unless method_defined?(:deep_transform_keys)
86
+
87
+ # Destructively convert all keys by using the block operation.
88
+ # This includes the keys from the root hash and from all
89
+ # nested hashes.
90
+ def deep_transform_keys!(&block)
91
+ keys.each do |key|
92
+ value = delete(key)
93
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
94
+ end
95
+ self
96
+ end unless method_defined?(:deep_transform_keys!)
97
+
98
+ # Returns a new hash with all keys converted to strings.
99
+ # This includes the keys from the root hash and from all
100
+ # nested hashes.
101
+ #
102
+ # hash = { person: { name: 'Rob', age: '28' } }
103
+ #
104
+ # hash.deep_stringify_keys
105
+ # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
106
+ def deep_stringify_keys
107
+ deep_transform_keys { |key| key.to_s }
108
+ end unless method_defined?(:deep_stringify_keys)
109
+
110
+ # Destructively convert all keys to strings.
111
+ # This includes the keys from the root hash and from all
112
+ # nested hashes.
113
+ def deep_stringify_keys!
114
+ deep_transform_keys! { |key| key.to_s }
115
+ end unless method_defined?(:deep_stringify_keys!)
116
+
117
+ # Returns a new hash with all keys converted to symbols, as long as
118
+ # they respond to +to_sym+. This includes the keys from the root hash
119
+ # and from all nested hashes.
120
+ #
121
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
122
+ #
123
+ # hash.deep_symbolize_keys
124
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
125
+ def deep_symbolize_keys
126
+ deep_transform_keys { |key| key.to_sym rescue key }
127
+ end unless method_defined?(:deep_symbolize_keys)
128
+
129
+ # Destructively convert all keys to symbols, as long as they respond
130
+ # to +to_sym+. This includes the keys from the root hash and from all
131
+ # nested hashes.
132
+ def deep_symbolize_keys!
133
+ deep_transform_keys! { |key| key.to_sym rescue key }
134
+ end unless method_defined?(:deep_symbolize_keys!)
135
+ end
@@ -0,0 +1,14 @@
1
+ STUBBED_ACTIVE_SUPPORT_HASH_KEYS_EXTENSIONS = true
2
+
3
+ class Hash
4
+ STUBS = [
5
+ :transform_keys, :transform_keys!,
6
+ :stringify_keys, :stringify_keys!,
7
+ :symbolize_keys, :symbolize_keys!,
8
+ :deep_transform_keys, :deep_transform_keys!,
9
+ :deep_stringify_keys, :deep_stringify_keys!,
10
+ :deep_symbolize_keys, :deep_symbolize_keys! ]
11
+
12
+ def rofl; :trololo end
13
+ STUBS.each { |stub| alias_method stub, :rofl }
14
+ end
@@ -0,0 +1,233 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'minitest/autorun'
4
+ $VERBOSE = true
5
+
6
+ require 'deep_hash_transform'
7
+
8
+ class TransformTest < Minitest::Test
9
+ def setup
10
+ @strings = { 'a' => 1, 'b' => 2 }
11
+ @nested_strings = { 'a' => { 'b' => { 'c' => 3 } } }
12
+ @symbols = { :a => 1, :b => 2 }
13
+ @nested_symbols = { :a => { :b => { :c => 3 } } }
14
+ @mixed = { :a => 1, 'b' => 2 }
15
+ @nested_mixed = { 'a' => { :b => { 'c' => 3 } } }
16
+ @fixnums = { 0 => 1, 1 => 2 }
17
+ @nested_fixnums = { 0 => { 1 => { 2 => 3} } }
18
+ @illegal_symbols = { [] => 3 }
19
+ @nested_illegal_symbols = { [] => { [] => 3} }
20
+ @upcase_strings = { 'A' => 1, 'B' => 2 }
21
+ @nested_upcase_strings = { 'A' => { 'B' => { 'C' => 3 } } }
22
+ end
23
+
24
+ def test_transform_keys
25
+ assert_equal @upcase_strings, @strings.transform_keys { |key| key.to_s.upcase }
26
+ assert_equal @upcase_strings, @symbols.transform_keys { |key| key.to_s.upcase }
27
+ assert_equal @upcase_strings, @mixed.transform_keys { |key| key.to_s.upcase }
28
+ end
29
+
30
+ def test_transform_keys_not_mutates
31
+ transformed_hash = @mixed.dup
32
+ transformed_hash.transform_keys { |key| key.to_s.upcase }
33
+ assert_equal @mixed, transformed_hash
34
+ end
35
+
36
+ def test_deep_transform_keys
37
+ assert_equal @nested_upcase_strings, @nested_symbols.deep_transform_keys { |key| key.to_s.upcase }
38
+ assert_equal @nested_upcase_strings, @nested_strings.deep_transform_keys { |key| key.to_s.upcase }
39
+ assert_equal @nested_upcase_strings, @nested_mixed.deep_transform_keys { |key| key.to_s.upcase }
40
+ end
41
+
42
+ def test_deep_transform_keys_not_mutates
43
+ transformed_hash = deep_dup(@nested_mixed)
44
+ transformed_hash.deep_transform_keys { |key| key.to_s.upcase }
45
+ assert_equal @nested_mixed, transformed_hash
46
+ end
47
+
48
+ def test_transform_keys!
49
+ assert_equal @upcase_strings, @symbols.dup.transform_keys! { |key| key.to_s.upcase }
50
+ assert_equal @upcase_strings, @strings.dup.transform_keys! { |key| key.to_s.upcase }
51
+ assert_equal @upcase_strings, @mixed.dup.transform_keys! { |key| key.to_s.upcase }
52
+ end
53
+
54
+ def test_transform_keys_with_bang_mutates
55
+ transformed_hash = @mixed.dup
56
+ transformed_hash.transform_keys! { |key| key.to_s.upcase }
57
+ assert_equal @upcase_strings, transformed_hash
58
+ assert_equal @mixed, { :a => 1, "b" => 2 }
59
+ end
60
+
61
+ def test_deep_transform_keys!
62
+ assert_equal @nested_upcase_strings, deep_dup(@nested_symbols).deep_transform_keys! { |key| key.to_s.upcase }
63
+ assert_equal @nested_upcase_strings, deep_dup(@nested_strings).deep_transform_keys! { |key| key.to_s.upcase }
64
+ assert_equal @nested_upcase_strings, deep_dup(@nested_mixed).deep_transform_keys! { |key| key.to_s.upcase }
65
+ end
66
+
67
+ def test_deep_transform_keys_with_bang_mutates
68
+ transformed_hash = deep_dup(@nested_mixed)
69
+ transformed_hash.deep_transform_keys! { |key| key.to_s.upcase }
70
+ assert_equal @nested_upcase_strings, transformed_hash
71
+ assert_equal @nested_mixed, { 'a' => { :b => { 'c' => 3 } } }
72
+ end
73
+
74
+ def test_symbolize_keys
75
+ assert_equal @symbols, @symbols.symbolize_keys
76
+ assert_equal @symbols, @strings.symbolize_keys
77
+ assert_equal @symbols, @mixed.symbolize_keys
78
+ end
79
+
80
+ def test_symbolize_keys_not_mutates
81
+ transformed_hash = @mixed.dup
82
+ transformed_hash.symbolize_keys
83
+ assert_equal @mixed, transformed_hash
84
+ end
85
+
86
+ def test_deep_symbolize_keys
87
+ assert_equal @nested_symbols, @nested_symbols.deep_symbolize_keys
88
+ assert_equal @nested_symbols, @nested_strings.deep_symbolize_keys
89
+ assert_equal @nested_symbols, @nested_mixed.deep_symbolize_keys
90
+ end
91
+
92
+ def test_deep_symbolize_keys_not_mutates
93
+ transformed_hash = deep_dup(@nested_mixed)
94
+ transformed_hash.deep_symbolize_keys
95
+ assert_equal @nested_mixed, transformed_hash
96
+ end
97
+
98
+ def test_symbolize_keys!
99
+ assert_equal @symbols, @symbols.dup.symbolize_keys!
100
+ assert_equal @symbols, @strings.dup.symbolize_keys!
101
+ assert_equal @symbols, @mixed.dup.symbolize_keys!
102
+ end
103
+
104
+ def test_symbolize_keys_with_bang_mutates
105
+ transformed_hash = @mixed.dup
106
+ transformed_hash.deep_symbolize_keys!
107
+ assert_equal @symbols, transformed_hash
108
+ assert_equal @mixed, { :a => 1, "b" => 2 }
109
+ end
110
+
111
+ def test_deep_symbolize_keys!
112
+ assert_equal @nested_symbols, deep_dup(@nested_symbols).deep_symbolize_keys!
113
+ assert_equal @nested_symbols, deep_dup(@nested_strings).deep_symbolize_keys!
114
+ assert_equal @nested_symbols, deep_dup(@nested_mixed).deep_symbolize_keys!
115
+ end
116
+
117
+ def test_deep_symbolize_keys_with_bang_mutates
118
+ transformed_hash = deep_dup(@nested_mixed)
119
+ transformed_hash.deep_symbolize_keys!
120
+ assert_equal @nested_symbols, transformed_hash
121
+ assert_equal @nested_mixed, { 'a' => { :b => { 'c' => 3 } } }
122
+ end
123
+
124
+ def test_symbolize_keys_preserves_keys_that_cant_be_symbolized
125
+ assert_equal @illegal_symbols, @illegal_symbols.symbolize_keys
126
+ assert_equal @illegal_symbols, @illegal_symbols.dup.symbolize_keys!
127
+ end
128
+
129
+ def test_deep_symbolize_keys_preserves_keys_that_cant_be_symbolized
130
+ assert_equal @nested_illegal_symbols, @nested_illegal_symbols.deep_symbolize_keys
131
+ assert_equal @nested_illegal_symbols, deep_dup(@nested_illegal_symbols).deep_symbolize_keys!
132
+ end
133
+
134
+ def test_symbolize_keys_preserves_fixnum_keys
135
+ assert_equal @fixnums, @fixnums.symbolize_keys
136
+ assert_equal @fixnums, @fixnums.dup.symbolize_keys!
137
+ end
138
+
139
+ def test_deep_symbolize_keys_preserves_fixnum_keys
140
+ assert_equal @nested_fixnums, @nested_fixnums.deep_symbolize_keys
141
+ assert_equal @nested_fixnums, deep_dup(@nested_fixnums).deep_symbolize_keys!
142
+ end
143
+
144
+ def test_stringify_keys
145
+ assert_equal @strings, @symbols.stringify_keys
146
+ assert_equal @strings, @strings.stringify_keys
147
+ assert_equal @strings, @mixed.stringify_keys
148
+ end
149
+
150
+ def test_stringify_keys_not_mutates
151
+ transformed_hash = @mixed.dup
152
+ transformed_hash.stringify_keys
153
+ assert_equal @mixed, transformed_hash
154
+ end
155
+
156
+ def test_deep_stringify_keys
157
+ assert_equal @nested_strings, @nested_symbols.deep_stringify_keys
158
+ assert_equal @nested_strings, @nested_strings.deep_stringify_keys
159
+ assert_equal @nested_strings, @nested_mixed.deep_stringify_keys
160
+ end
161
+
162
+ def test_deep_stringify_keys_not_mutates
163
+ transformed_hash = deep_dup(@nested_mixed)
164
+ transformed_hash.deep_stringify_keys
165
+ assert_equal @nested_mixed, transformed_hash
166
+ end
167
+
168
+ def test_stringify_keys!
169
+ assert_equal @strings, @symbols.dup.stringify_keys!
170
+ assert_equal @strings, @strings.dup.stringify_keys!
171
+ assert_equal @strings, @mixed.dup.stringify_keys!
172
+ end
173
+
174
+ def test_stringify_keys_with_bang_mutates
175
+ transformed_hash = @mixed.dup
176
+ transformed_hash.stringify_keys!
177
+ assert_equal @strings, transformed_hash
178
+ assert_equal @mixed, { :a => 1, "b" => 2 }
179
+ end
180
+
181
+ def test_deep_stringify_keys!
182
+ assert_equal @nested_strings, deep_dup(@nested_symbols).deep_stringify_keys!
183
+ assert_equal @nested_strings, deep_dup(@nested_strings).deep_stringify_keys!
184
+ assert_equal @nested_strings, deep_dup(@nested_mixed).deep_stringify_keys!
185
+ end
186
+
187
+ def test_deep_stringify_keys_with_bang_mutates
188
+ transformed_hash = deep_dup(@nested_mixed)
189
+ transformed_hash.deep_stringify_keys!
190
+ assert_equal @nested_strings, transformed_hash
191
+ assert_equal @nested_mixed, { 'a' => { :b => { 'c' => 3 } } }
192
+ end
193
+
194
+ private
195
+ def deep_dup(hash)
196
+ Marshal.load(Marshal.dump(hash))
197
+ end
198
+ end
199
+
200
+ # Put a fake Active Support implementation in the load path to verify that
201
+ # we defer to its Hash extensions when they're already defined.
202
+ class ForwardCompatibilityTest < Minitest::Test
203
+ def setup; expunge_loaded_features end
204
+ def teardown; expunge_loaded_features end
205
+
206
+ def test_check_for_active_support_implementation_before_providing_our_own
207
+ assert_equal [], $LOADED_FEATURES.grep(/hash\/keys/)
208
+
209
+ with_stubbed_active_support_in_load_path do
210
+ require 'deep_hash_transform'
211
+ end
212
+
213
+ assert defined?(::STUBBED_ACTIVE_SUPPORT_HASH_KEYS_EXTENSIONS)
214
+
215
+ Hash::STUBS.each do |stub|
216
+ assert_equal :trololo, Hash.new.send(stub)
217
+ end
218
+ end
219
+
220
+ private
221
+ def expunge_loaded_features
222
+ $LOADED_FEATURES.delete_if { |feature| feature =~ /deep_hash_transform/ }
223
+ end
224
+
225
+ def with_stubbed_active_support_in_load_path
226
+ $LOAD_PATH.unshift File.expand_path('../active_support_stub', __FILE__)
227
+ old_verbose, $VERBOSE = $VERBOSE, nil
228
+ yield
229
+ ensure
230
+ $VERBOSE = old_verbose
231
+ $LOAD_PATH.shift
232
+ end
233
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deep_hash_transform
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rails Backport
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.1'
41
+ description:
42
+ email: support@basecamp.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - "./lib/deep_hash_transform.rb"
48
+ - "./test/active_support_stub/active_support/core_ext/hash/keys.rb"
49
+ - "./test/deep_hash_transform_test.rb"
50
+ homepage: https://github.com/basecamp/deep_hash_transform
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.8.7
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.2.0
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Re-key a nested Hash to all-Symbol or -String keys
74
+ test_files:
75
+ - "./test/active_support_stub/active_support/core_ext/hash/keys.rb"
76
+ - "./test/deep_hash_transform_test.rb"