wrest 4.0.0-universal-java-18
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.
- checksums.yaml +7 -0
- data/CHANGELOG +169 -0
- data/LICENCE +7 -0
- data/README.md +436 -0
- data/bin/wrest +4 -0
- data/bin/wrest_shell.rb +23 -0
- data/lib/wrest/async_request/event_machine_backend.rb +32 -0
- data/lib/wrest/async_request/thread_backend.rb +34 -0
- data/lib/wrest/async_request/thread_pool.rb +29 -0
- data/lib/wrest/async_request.rb +51 -0
- data/lib/wrest/cache_proxy.rb +119 -0
- data/lib/wrest/caching/memcached.rb +37 -0
- data/lib/wrest/caching/redis.rb +38 -0
- data/lib/wrest/caching.rb +57 -0
- data/lib/wrest/callback.rb +70 -0
- data/lib/wrest/components/container/alias_accessors.rb +70 -0
- data/lib/wrest/components/container/typecaster.rb +178 -0
- data/lib/wrest/components/container.rb +204 -0
- data/lib/wrest/components/mutators/base.rb +65 -0
- data/lib/wrest/components/mutators/camel_to_snake_case.rb +26 -0
- data/lib/wrest/components/mutators/xml_type_caster.rb +56 -0
- data/lib/wrest/components/mutators.rb +42 -0
- data/lib/wrest/components/translators/content_types.rb +25 -0
- data/lib/wrest/components/translators/json.rb +36 -0
- data/lib/wrest/components/translators/txt.rb +35 -0
- data/lib/wrest/components/translators/xml/conversions.rb +56 -0
- data/lib/wrest/components/translators/xml.rb +77 -0
- data/lib/wrest/components/translators.rb +30 -0
- data/lib/wrest/components.rb +22 -0
- data/lib/wrest/core_ext/hash/conversions.rb +45 -0
- data/lib/wrest/core_ext/hash.rb +7 -0
- data/lib/wrest/core_ext/string/conversions.rb +38 -0
- data/lib/wrest/core_ext/string.rb +7 -0
- data/lib/wrest/exceptions.rb +38 -0
- data/lib/wrest/hash_with_case_insensitive_access.rb +52 -0
- data/lib/wrest/hash_with_indifferent_access.rb +442 -0
- data/lib/wrest/http_codes.rb +83 -0
- data/lib/wrest/http_shared/headers.rb +345 -0
- data/lib/wrest/http_shared/standard_headers.rb +22 -0
- data/lib/wrest/http_shared/standard_tokens.rb +21 -0
- data/lib/wrest/http_shared.rb +25 -0
- data/lib/wrest/multipart.rb +84 -0
- data/lib/wrest/native/connection_factory.rb +28 -0
- data/lib/wrest/native/delete.rb +27 -0
- data/lib/wrest/native/get.rb +83 -0
- data/lib/wrest/native/options.rb +27 -0
- data/lib/wrest/native/patch.rb +27 -0
- data/lib/wrest/native/post.rb +27 -0
- data/lib/wrest/native/post_multipart.rb +36 -0
- data/lib/wrest/native/put.rb +27 -0
- data/lib/wrest/native/put_multipart.rb +36 -0
- data/lib/wrest/native/redirection.rb +39 -0
- data/lib/wrest/native/request.rb +161 -0
- data/lib/wrest/native/response.rb +278 -0
- data/lib/wrest/native/session.rb +66 -0
- data/lib/wrest/native.rb +36 -0
- data/lib/wrest/test/request_patches.rb +12 -0
- data/lib/wrest/test.rb +3 -0
- data/lib/wrest/uri/builders.rb +48 -0
- data/lib/wrest/uri.rb +312 -0
- data/lib/wrest/uri_template.rb +63 -0
- data/lib/wrest/utils.rb +129 -0
- data/lib/wrest/version.rb +14 -0
- data/lib/wrest.rb +77 -0
- data/lib/wrest_no_ext.rb +7 -0
- 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,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,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
|