wrest 4.0.0-universal-java-18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +169 -0
  3. data/LICENCE +7 -0
  4. data/README.md +436 -0
  5. data/bin/wrest +4 -0
  6. data/bin/wrest_shell.rb +23 -0
  7. data/lib/wrest/async_request/event_machine_backend.rb +32 -0
  8. data/lib/wrest/async_request/thread_backend.rb +34 -0
  9. data/lib/wrest/async_request/thread_pool.rb +29 -0
  10. data/lib/wrest/async_request.rb +51 -0
  11. data/lib/wrest/cache_proxy.rb +119 -0
  12. data/lib/wrest/caching/memcached.rb +37 -0
  13. data/lib/wrest/caching/redis.rb +38 -0
  14. data/lib/wrest/caching.rb +57 -0
  15. data/lib/wrest/callback.rb +70 -0
  16. data/lib/wrest/components/container/alias_accessors.rb +70 -0
  17. data/lib/wrest/components/container/typecaster.rb +178 -0
  18. data/lib/wrest/components/container.rb +204 -0
  19. data/lib/wrest/components/mutators/base.rb +65 -0
  20. data/lib/wrest/components/mutators/camel_to_snake_case.rb +26 -0
  21. data/lib/wrest/components/mutators/xml_type_caster.rb +56 -0
  22. data/lib/wrest/components/mutators.rb +42 -0
  23. data/lib/wrest/components/translators/content_types.rb +25 -0
  24. data/lib/wrest/components/translators/json.rb +36 -0
  25. data/lib/wrest/components/translators/txt.rb +35 -0
  26. data/lib/wrest/components/translators/xml/conversions.rb +56 -0
  27. data/lib/wrest/components/translators/xml.rb +77 -0
  28. data/lib/wrest/components/translators.rb +30 -0
  29. data/lib/wrest/components.rb +22 -0
  30. data/lib/wrest/core_ext/hash/conversions.rb +45 -0
  31. data/lib/wrest/core_ext/hash.rb +7 -0
  32. data/lib/wrest/core_ext/string/conversions.rb +38 -0
  33. data/lib/wrest/core_ext/string.rb +7 -0
  34. data/lib/wrest/exceptions.rb +38 -0
  35. data/lib/wrest/hash_with_case_insensitive_access.rb +52 -0
  36. data/lib/wrest/hash_with_indifferent_access.rb +442 -0
  37. data/lib/wrest/http_codes.rb +83 -0
  38. data/lib/wrest/http_shared/headers.rb +345 -0
  39. data/lib/wrest/http_shared/standard_headers.rb +22 -0
  40. data/lib/wrest/http_shared/standard_tokens.rb +21 -0
  41. data/lib/wrest/http_shared.rb +25 -0
  42. data/lib/wrest/multipart.rb +84 -0
  43. data/lib/wrest/native/connection_factory.rb +28 -0
  44. data/lib/wrest/native/delete.rb +27 -0
  45. data/lib/wrest/native/get.rb +83 -0
  46. data/lib/wrest/native/options.rb +27 -0
  47. data/lib/wrest/native/patch.rb +27 -0
  48. data/lib/wrest/native/post.rb +27 -0
  49. data/lib/wrest/native/post_multipart.rb +36 -0
  50. data/lib/wrest/native/put.rb +27 -0
  51. data/lib/wrest/native/put_multipart.rb +36 -0
  52. data/lib/wrest/native/redirection.rb +39 -0
  53. data/lib/wrest/native/request.rb +161 -0
  54. data/lib/wrest/native/response.rb +278 -0
  55. data/lib/wrest/native/session.rb +66 -0
  56. data/lib/wrest/native.rb +36 -0
  57. data/lib/wrest/test/request_patches.rb +12 -0
  58. data/lib/wrest/test.rb +3 -0
  59. data/lib/wrest/uri/builders.rb +48 -0
  60. data/lib/wrest/uri.rb +312 -0
  61. data/lib/wrest/uri_template.rb +63 -0
  62. data/lib/wrest/utils.rb +129 -0
  63. data/lib/wrest/version.rb +14 -0
  64. data/lib/wrest.rb +77 -0
  65. data/lib/wrest_no_ext.rb +7 -0
  66. metadata +286 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2009 Sidu Ponnappa
4
+
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
9
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and limitations under the License.
11
+
12
+ module Wrest
13
+ module CoreExt # :nodoc:
14
+ module Hash # :nodoc:
15
+ # Makes it easier to build other objects from a Hash
16
+ module Conversions
17
+ # This method accepts a hash mutator (found in Wrest::Compononents)
18
+ # to build a new hash map by making changes to an existing one.
19
+ #
20
+ # No, this method does not mutate the state of the hash it is invoked on,
21
+ # but rather builds a new one.
22
+ #
23
+ # Yes, the name is misleading in that respect. However, one
24
+ # hopes the absence of an exclamation mark will increase clarity.
25
+ #
26
+ # Uses include mutating the hash produced by deserialising xml
27
+ # by using the meta data in the hash to type cast values.
28
+ #
29
+ # Example:
30
+ # "http://search.yahooapis.com/NewsSearchService/V1/newsSearch".to_uri.get(
31
+ # :appid => 'YahooDemo',
32
+ # :output => 'xml',
33
+ # :query => 'India',
34
+ # :results=> '3',
35
+ # :start => '1'
36
+ # ).deserialise.mutate_using(XmlTypeCaster.new)
37
+ def mutate_using(mutator)
38
+ mutated_hash = {}
39
+ each { |tuple| mutated_hash.store(*mutator.mutate(tuple)) }
40
+ mutated_hash
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrest/core_ext/hash/conversions'
4
+
5
+ class Hash # :nodoc:
6
+ include Wrest::CoreExt::Hash::Conversions
7
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2009 Sidu Ponnappa
4
+
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the License
9
+ # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and limitations under the License.
11
+
12
+ module Wrest
13
+ module CoreExt # :nodoc:
14
+ module String # :nodoc:
15
+ # Makes it easier to build other objects from a String
16
+ # This module is opt-out - if you don't want the to_uri
17
+ # and to_uri_template convenience method on String, set
18
+ # the NoStringExtensions constant on the Wrest module
19
+ # before requiring wrest.
20
+ #
21
+ # module Wrest
22
+ # NoStringExtensions = true
23
+ # end
24
+ # require 'wrest'
25
+ module Conversions
26
+ # A convenience method equivalent to Wrest::Uri.new(string)
27
+ def to_uri(options = {})
28
+ Wrest::Uri.new(strip, options)
29
+ end
30
+
31
+ # A convenience method equivalent to Wrest:UriTemplate.new(uri_pattern)
32
+ def to_uri_template(options = {})
33
+ Wrest::UriTemplate.new(strip, options)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrest/core_ext/string/conversions'
4
+
5
+ class String # :nodoc:
6
+ include Wrest::CoreExt::String::Conversions unless Wrest.const_defined?('NoStringExtensions')
7
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrest
4
+ module Exceptions # :nodoc:
5
+ # Raised when a base method that should be overriden
6
+ # is invoked on a subclass that has not implemented it.
7
+ class MethodNotOverridden < StandardError
8
+ end
9
+
10
+ # Raised when a translator for an unregisterd response content type
11
+ # is requested. See Translators.
12
+ class UnsupportedContentType < StandardError
13
+ end
14
+
15
+ # Raised when a request auto redirects more times than are allowed
16
+ # by its follow_redirects_limit. See Wrest::Native::Redirection.
17
+ class AutoRedirectLimitExceeded < StandardError
18
+ end
19
+
20
+ # Raised when a request is made when either RAILS_ENV or
21
+ # ENV['RAILS_ENV'] is set to 'test', which is the case when
22
+ # running tests/specs in a Rails application.
23
+ #
24
+ # See wrest/test/request_patches.
25
+ class RealRequestMadeInTestEnvironmet < StandardError
26
+ end
27
+
28
+ # Raised when a request times out
29
+ class Timeout < StandardError
30
+ end
31
+
32
+ class UnsupportedHttpVerb < StandardError
33
+ end
34
+
35
+ class UnsupportedFeature < StandardError
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrest
4
+ # A hash with case-insensitive key access.
5
+ #
6
+ # hash = Wrest::HashWithCaseInsensitiveAccess.new 'Abcd' => 1, 'xyz' => 2
7
+ #
8
+ # hash['abcd'] #=> 1
9
+ # hash['aBCd'] #=> 1
10
+ #
11
+ class HashWithCaseInsensitiveAccess < ::Hash # :nodoc:
12
+ def initialize(hash = {})
13
+ super()
14
+ hash.each do |key, value|
15
+ self[convert_key(key)] = value
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ super(convert_key(key))
21
+ end
22
+
23
+ def []=(key, value)
24
+ super(convert_key(key), value)
25
+ end
26
+
27
+ def delete(key)
28
+ super(convert_key(key))
29
+ end
30
+
31
+ def values_at(*indices)
32
+ indices.collect { |key| self[convert_key(key)] }
33
+ end
34
+
35
+ def merge(other)
36
+ dup.merge!(other)
37
+ end
38
+
39
+ def merge!(other)
40
+ other.each do |key, value|
41
+ self[convert_key(key)] = value
42
+ end
43
+ self
44
+ end
45
+
46
+ protected
47
+
48
+ def convert_key(key)
49
+ key.is_a?(String) ? key.downcase : key
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,442 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wrest
4
+ # https://github.com/rails/rails/commit/1dcad65f8075d5b766082d3fbabdcb20fecb4ccd
5
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
6
+ # to be the same.
7
+ #
8
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
9
+ #
10
+ # rgb[:black] = '#000000'
11
+ # rgb[:black] # => '#000000'
12
+ # rgb['black'] # => '#000000'
13
+ #
14
+ # rgb['white'] = '#FFFFFF'
15
+ # rgb[:white] # => '#FFFFFF'
16
+ # rgb['white'] # => '#FFFFFF'
17
+ #
18
+ # Internally symbols are mapped to strings when used as keys in the entire
19
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
20
+ # mapping belongs to the public interface. For example, given:
21
+ #
22
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
23
+ #
24
+ # You are guaranteed that the key is returned as a string:
25
+ #
26
+ # hash.keys # => ["a"]
27
+ #
28
+ # Technically other types of keys are accepted:
29
+ #
30
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
31
+ # hash[0] = 0
32
+ # hash # => {"a"=>1, 0=>0}
33
+ #
34
+ # but this class is intended for use cases where strings or symbols are the
35
+ # expected keys and it is convenient to understand both as the same. For
36
+ # example the +params+ hash in Ruby on Rails.
37
+ #
38
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
39
+ #
40
+ # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
41
+ #
42
+ # which may be handy.
43
+ #
44
+ class HashWithIndifferentAccess < Hash
45
+ # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
46
+ # this class.
47
+ def extractable_options?
48
+ true
49
+ end
50
+
51
+ def with_indifferent_access
52
+ dup
53
+ end
54
+
55
+ def nested_under_indifferent_access
56
+ self
57
+ end
58
+
59
+ def initialize(constructor = nil)
60
+ if constructor.respond_to?(:to_hash)
61
+ super()
62
+ update(constructor)
63
+
64
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
65
+ self.default = hash.default if hash.default
66
+ self.default_proc = hash.default_proc if hash.default_proc
67
+ elsif constructor.nil?
68
+ super()
69
+ else
70
+ super(constructor)
71
+ end
72
+ end
73
+
74
+ def self.[](*args)
75
+ new.merge!(Hash[*args])
76
+ end
77
+
78
+ alias regular_writer []= unless method_defined?(:regular_writer)
79
+ alias regular_update update unless method_defined?(:regular_update)
80
+
81
+ # Assigns a new value to the hash:
82
+ #
83
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
84
+ # hash[:key] = 'value'
85
+ #
86
+ # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
87
+ def []=(key, value)
88
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
89
+ end
90
+
91
+ alias store []=
92
+
93
+ # Updates the receiver in-place, merging in the hashes passed as arguments:
94
+ #
95
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
96
+ # hash_1[:key] = 'value'
97
+ #
98
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
99
+ # hash_2[:key] = 'New Value!'
100
+ #
101
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
102
+ #
103
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
104
+ # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
105
+ #
106
+ # The arguments can be either an
107
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
108
+ # In either case the merge respects the semantics of indifferent access.
109
+ #
110
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
111
+ # of the values end up in the receiver, but which one is unspecified.
112
+ #
113
+ # When given a block, the value for duplicated keys will be determined
114
+ # by the result of invoking the block with the duplicated key, the value
115
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
116
+ # keys follow the semantics of indifferent access:
117
+ #
118
+ # hash_1[:key] = 10
119
+ # hash_2['key'] = 12
120
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
121
+ def update(*other_hashes, &block)
122
+ if other_hashes.size == 1
123
+ update_with_single_argument(other_hashes.first, block)
124
+ else
125
+ other_hashes.each do |other_hash|
126
+ update_with_single_argument(other_hash, block)
127
+ end
128
+ end
129
+ self
130
+ end
131
+
132
+ alias merge! update
133
+
134
+ # Checks the hash for a key matching the argument passed in:
135
+ #
136
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
137
+ # hash['key'] = 'value'
138
+ # hash.key?(:key) # => true
139
+ # hash.key?('key') # => true
140
+ def key?(key)
141
+ super(convert_key(key))
142
+ end
143
+
144
+ alias include? key?
145
+ alias has_key? key?
146
+ alias member? key?
147
+
148
+ # Same as <tt>Hash#[]</tt> where the key passed as argument can be
149
+ # either a string or a symbol:
150
+ #
151
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
152
+ # counters[:foo] = 1
153
+ #
154
+ # counters['foo'] # => 1
155
+ # counters[:foo] # => 1
156
+ # counters[:zoo] # => nil
157
+ def [](key)
158
+ super(convert_key(key))
159
+ end
160
+
161
+ # Same as <tt>Hash#assoc</tt> where the key passed as argument can be
162
+ # either a string or a symbol:
163
+ #
164
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
165
+ # counters[:foo] = 1
166
+ #
167
+ # counters.assoc('foo') # => ["foo", 1]
168
+ # counters.assoc(:foo) # => ["foo", 1]
169
+ # counters.assoc(:zoo) # => nil
170
+ def assoc(key)
171
+ super(convert_key(key))
172
+ end
173
+
174
+ # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
175
+ # either a string or a symbol:
176
+ #
177
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
178
+ # counters[:foo] = 1
179
+ #
180
+ # counters.fetch('foo') # => 1
181
+ # counters.fetch(:bar, 0) # => 0
182
+ # counters.fetch(:bar) { |key| 0 } # => 0
183
+ # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
184
+ def fetch(key, *extras)
185
+ super(convert_key(key), *extras)
186
+ end
187
+
188
+ # Same as <tt>Hash#dig</tt> where the key passed as argument can be
189
+ # either a string or a symbol:
190
+ #
191
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
192
+ # counters[:foo] = { bar: 1 }
193
+ #
194
+ # counters.dig('foo', 'bar') # => 1
195
+ # counters.dig(:foo, :bar) # => 1
196
+ # counters.dig(:zoo) # => nil
197
+ def dig(*args)
198
+ args[0] = convert_key(args[0]) if args.size.positive?
199
+ super(*args)
200
+ end
201
+
202
+ # Same as <tt>Hash#default</tt> where the key passed as argument can be
203
+ # either a string or a symbol:
204
+ #
205
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(1)
206
+ # hash.default # => 1
207
+ #
208
+ # hash = ActiveSupport::HashWithIndifferentAccess.new { |hash, key| key }
209
+ # hash.default # => nil
210
+ # hash.default('foo') # => 'foo'
211
+ # hash.default(:foo) # => 'foo'
212
+ def default(*args)
213
+ super(*args.map { |arg| convert_key(arg) })
214
+ end
215
+
216
+ # Returns an array of the values at the specified indices:
217
+ #
218
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
219
+ # hash[:a] = 'x'
220
+ # hash[:b] = 'y'
221
+ # hash.values_at('a', 'b') # => ["x", "y"]
222
+ def values_at(*keys)
223
+ super(*keys.map { |key| convert_key(key) })
224
+ end
225
+
226
+ # Returns an array of the values at the specified indices, but also
227
+ # raises an exception when one of the keys can't be found.
228
+ #
229
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
230
+ # hash[:a] = 'x'
231
+ # hash[:b] = 'y'
232
+ # hash.fetch_values('a', 'b') # => ["x", "y"]
233
+ # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
234
+ # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
235
+ def fetch_values(*indices, &block)
236
+ super(*indices.map { |key| convert_key(key) }, &block)
237
+ end
238
+
239
+ # Returns a shallow copy of the hash.
240
+ #
241
+ # hash = ActiveSupport::HashWithIndifferentAccess.new({ a: { b: 'b' } })
242
+ # dup = hash.dup
243
+ # dup[:a][:c] = 'c'
244
+ #
245
+ # hash[:a][:c] # => "c"
246
+ # dup[:a][:c] # => "c"
247
+ def dup
248
+ self.class.new(self).tap do |new_hash|
249
+ set_defaults(new_hash)
250
+ end
251
+ end
252
+
253
+ # This method has the same semantics of +update+, except it does not
254
+ # modify the receiver but rather returns a new hash with indifferent
255
+ # access with the result of the merge.
256
+ def merge(*hashes, &block)
257
+ dup.update(*hashes, &block)
258
+ end
259
+
260
+ # Like +merge+ but the other way around: Merges the receiver into the
261
+ # argument and returns a new hash with indifferent access as result:
262
+ #
263
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
264
+ # hash['a'] = nil
265
+ # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
266
+ def reverse_merge(other_hash)
267
+ super(self.class.new(other_hash))
268
+ end
269
+
270
+ alias with_defaults reverse_merge
271
+
272
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
273
+ def reverse_merge!(other_hash)
274
+ super(self.class.new(other_hash))
275
+ end
276
+
277
+ alias with_defaults! reverse_merge!
278
+
279
+ # Replaces the contents of this hash with other_hash.
280
+ #
281
+ # h = { "a" => 100, "b" => 200 }
282
+ # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
283
+ def replace(other_hash)
284
+ super(self.class.new(other_hash))
285
+ end
286
+
287
+ # Removes the specified key from the hash.
288
+ def delete(key)
289
+ super(convert_key(key))
290
+ end
291
+
292
+ # Returns a hash with indifferent access that includes everything except given keys.
293
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
294
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
295
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
296
+ def except(*keys)
297
+ slice(*self.keys - keys.map { |key| convert_key(key) })
298
+ end
299
+
300
+ alias without except
301
+
302
+ def stringify_keys!
303
+ transform_keys!(&:to_s)
304
+ end
305
+
306
+ def stringify_keys
307
+ transform_keys(&:to_s)
308
+ end
309
+
310
+ def symbolize_keys
311
+ symbolize_keys!
312
+ end
313
+
314
+ def symbolize_keys!
315
+ transform_keys! do |key|
316
+ key.to_sym
317
+ rescue StandardError
318
+ key
319
+ end
320
+ end
321
+
322
+ alias to_options symbolize_keys
323
+
324
+ def to_options!
325
+ self
326
+ end
327
+
328
+ def select(*args, &block)
329
+ return to_enum(:select) unless block_given?
330
+
331
+ dup.tap { |hash| hash.select!(*args, &block) }
332
+ end
333
+
334
+ def reject(*args, &block)
335
+ return to_enum(:reject) unless block_given?
336
+
337
+ dup.tap { |hash| hash.reject!(*args, &block) }
338
+ end
339
+
340
+ def transform_values(*args, &block)
341
+ return to_enum(:transform_values) unless block_given?
342
+
343
+ dup.tap { |hash| hash.transform_values!(*args, &block) }
344
+ end
345
+
346
+ def transform_keys(*args, &block)
347
+ return to_enum(:transform_keys) unless block_given?
348
+
349
+ dup.tap { |hash| hash.transform_keys!(*args, &block) }
350
+ end
351
+
352
+ def transform_keys!
353
+ return enum_for(:transform_keys!) { size } unless block_given?
354
+
355
+ keys.each do |key|
356
+ self[yield(key)] = delete(key)
357
+ end
358
+ self
359
+ end
360
+
361
+ def slice(*keys)
362
+ keys.map! { |key| convert_key(key) }
363
+ self.class.new(super)
364
+ end
365
+
366
+ def slice!(*keys)
367
+ keys.map! { |key| convert_key(key) }
368
+ super
369
+ end
370
+
371
+ def compact
372
+ dup.tap(&:compact!)
373
+ end
374
+
375
+ # Convert to a regular hash with string keys.
376
+ def to_hash
377
+ a_new_hash = {}
378
+ set_defaults(a_new_hash)
379
+
380
+ each do |key, value|
381
+ a_new_hash[key] = convert_value(value, conversion: :to_hash)
382
+ end
383
+ a_new_hash
384
+ end
385
+
386
+ private
387
+
388
+ if Symbol.method_defined?(:name)
389
+ def convert_key(key)
390
+ key.is_a?(Symbol) ? key.name : key
391
+ end
392
+ else
393
+ def convert_key(key)
394
+ key.is_a?(Symbol) ? key.to_s : key
395
+ end
396
+ end
397
+
398
+ def convert_value(value, conversion: nil)
399
+ case value
400
+ when Hash
401
+ convert_hash_value(conversion, value)
402
+ when Array
403
+ convert_array_value(conversion, value)
404
+ else
405
+ value
406
+ end
407
+ end
408
+
409
+ def convert_array_value(conversion, value)
410
+ value = value.dup if conversion != :assignment || value.frozen?
411
+ value.map! { |e| convert_value(e, conversion: conversion) }
412
+ value
413
+ end
414
+
415
+ def convert_hash_value(conversion, value)
416
+ if conversion == :to_hash
417
+ value.to_hash
418
+ else
419
+ HashWithIndifferentAccess.new(value)
420
+ end
421
+ end
422
+
423
+ def set_defaults(target) # rubocop:disable Naming/AccessorMethodName
424
+ if default_proc
425
+ target.default_proc = default_proc.dup
426
+ else
427
+ target.default = default
428
+ end
429
+ end
430
+
431
+ def update_with_single_argument(other_hash, block)
432
+ if other_hash.is_a? HashWithIndifferentAccess
433
+ regular_update(other_hash, &block)
434
+ else
435
+ other_hash.to_hash.each_pair do |key, value|
436
+ value = block.call(convert_key(key), self[key], value) if block && key?(key)
437
+ regular_writer(convert_key(key), convert_value(value))
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end