orb-billing 0.1.0.pre.alpha.39 → 0.1.2
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 +4 -4
- data/README.md +10 -2
- data/lib/orb/internal/transport/base_client.rb +1 -1
- data/lib/orb/internal/transport/pooled_net_requester.rb +14 -5
- data/lib/orb/internal/type/array_of.rb +12 -2
- data/lib/orb/internal/type/base_model.rb +9 -5
- data/lib/orb/internal/type/boolean.rb +5 -1
- data/lib/orb/internal/type/converter.rb +42 -17
- data/lib/orb/internal/type/enum.rb +5 -1
- data/lib/orb/internal/type/hash_of.rb +6 -2
- data/lib/orb/internal/type/io_like.rb +75 -0
- data/lib/orb/internal/type/request_parameters.rb +11 -2
- data/lib/orb/internal/type/union.rb +7 -3
- data/lib/orb/internal/type/unknown.rb +5 -1
- data/lib/orb/internal/util.rb +80 -21
- data/lib/orb/version.rb +1 -1
- data/lib/orb.rb +1 -0
- data/rbi/lib/orb/internal/transport/pooled_net_requester.rbi +1 -1
- data/rbi/lib/orb/internal/type/array_of.rbi +7 -3
- data/rbi/lib/orb/internal/type/base_model.rbi +7 -3
- data/rbi/lib/orb/internal/type/boolean.rbi +9 -3
- data/rbi/lib/orb/internal/type/converter.rbi +23 -10
- data/rbi/lib/orb/internal/type/enum.rbi +12 -3
- data/rbi/lib/orb/internal/type/hash_of.rbi +6 -3
- data/rbi/lib/orb/internal/type/io_like.rbi +49 -0
- data/rbi/lib/orb/internal/type/union.rbi +11 -3
- data/rbi/lib/orb/internal/type/unknown.rbi +8 -3
- data/rbi/lib/orb/internal/util.rbi +35 -4
- data/sig/orb/internal/transport/pooled_net_requester.rbs +1 -1
- data/sig/orb/internal/type/array_of.rbs +5 -2
- data/sig/orb/internal/type/base_model.rbs +5 -2
- data/sig/orb/internal/type/boolean.rbs +5 -2
- data/sig/orb/internal/type/converter.rbs +11 -5
- data/sig/orb/internal/type/enum.rbs +5 -2
- data/sig/orb/internal/type/hash_of.rbs +5 -2
- data/sig/orb/internal/type/io_like.rbs +23 -0
- data/sig/orb/internal/type/union.rbs +5 -2
- data/sig/orb/internal/type/unknown.rbs +5 -2
- data/sig/orb/internal/util.rbs +17 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 990802d6d3401319cda433bc9532f639cdb8c8e95c5b7a9bd8f0669ee8b5b685
|
4
|
+
data.tar.gz: 20825707be0f39e420ffdc94a472b7177a39100dd9a9848c90e8d26513b83942
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 737f7cfff4e8a65355d611b420aa222b88750165695e2602ca837541b04b4cad746cf807974eab61f9845772ab24d1ebf13dbc9b4b2f6fbbd9ea9a2c8a9f0a58
|
7
|
+
data.tar.gz: 47a0fc3dcd570b218cb5f3c9ce5a0ce2598cf7f18626ac0270b58aab21d05aee9229151335b92e90ec1f9411c5d975a15bb996e4483b55e8d4f6625d6363e089
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
|
|
15
15
|
<!-- x-release-please-start-version -->
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
gem "orb-billing", "~> 0.1.
|
18
|
+
gem "orb-billing", "~> 0.1.2"
|
19
19
|
```
|
20
20
|
|
21
21
|
<!-- x-release-please-end -->
|
@@ -148,6 +148,13 @@ After Solargraph is installed, **you must populate its index** either via the pr
|
|
148
148
|
bundle exec solargraph gems
|
149
149
|
```
|
150
150
|
|
151
|
+
Note: if you had installed the gem either using a `git:` or `github:` URL, or had vendored the gem using bundler, you will need to set up your [`.solargraph.yml`](https://solargraph.org/guides/configuration) to include the path to the gem's `lib` directory.
|
152
|
+
|
153
|
+
```yaml
|
154
|
+
include:
|
155
|
+
- 'vendor/bundle/ruby/*/gems/orb-billing-*/lib/**/*.rb'
|
156
|
+
```
|
157
|
+
|
151
158
|
Otherwise Solargraph will not be able to provide type information or auto-completion for any non-indexed libraries.
|
152
159
|
|
153
160
|
### Sorbet
|
@@ -161,7 +168,8 @@ Due to limitations with the Sorbet type system, where a method otherwise can tak
|
|
161
168
|
Please follow Sorbet's [setup guides](https://sorbet.org/docs/adopting) for best experience.
|
162
169
|
|
163
170
|
```ruby
|
164
|
-
params =
|
171
|
+
params =
|
172
|
+
Orb::Models::CustomerCreateParams.new(email: "example-customer@withorb.com", name: "My Customer")
|
165
173
|
|
166
174
|
orb.customers.create(**params)
|
167
175
|
```
|
@@ -380,7 +380,7 @@ module Orb
|
|
380
380
|
in (400..) | Orb::Errors::APIConnectionError
|
381
381
|
self.class.reap_connection!(status, stream: stream)
|
382
382
|
|
383
|
-
delay = retry_delay(response, retry_count: retry_count)
|
383
|
+
delay = retry_delay(response || {}, retry_count: retry_count)
|
384
384
|
sleep(delay)
|
385
385
|
|
386
386
|
send_request(
|
@@ -54,7 +54,7 @@ module Orb
|
|
54
54
|
# @param blk [Proc]
|
55
55
|
#
|
56
56
|
# @yieldparam [String]
|
57
|
-
# @return [Net::HTTPGenericRequest]
|
57
|
+
# @return [Array(Net::HTTPGenericRequest, Proc)]
|
58
58
|
def build_request(request, &blk)
|
59
59
|
method, url, headers, body = request.fetch_values(:method, :url, :headers, :body)
|
60
60
|
req = Net::HTTPGenericRequest.new(
|
@@ -75,12 +75,12 @@ module Orb
|
|
75
75
|
in StringIO
|
76
76
|
req["content-length"] ||= body.size.to_s unless req["transfer-encoding"]
|
77
77
|
req.body_stream = Orb::Internal::Util::ReadIOAdapter.new(body, &blk)
|
78
|
-
in IO | Enumerator
|
78
|
+
in Pathname | IO | Enumerator
|
79
79
|
req["transfer-encoding"] ||= "chunked" unless req["content-length"]
|
80
80
|
req.body_stream = Orb::Internal::Util::ReadIOAdapter.new(body, &blk)
|
81
81
|
end
|
82
82
|
|
83
|
-
req
|
83
|
+
[req, req.body_stream&.method(:close)]
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -123,13 +123,17 @@ module Orb
|
|
123
123
|
def execute(request)
|
124
124
|
url, deadline = request.fetch_values(:url, :deadline)
|
125
125
|
|
126
|
+
req = nil
|
126
127
|
eof = false
|
127
128
|
finished = false
|
129
|
+
closing = nil
|
130
|
+
|
131
|
+
# rubocop:disable Metrics/BlockLength
|
128
132
|
enum = Enumerator.new do |y|
|
129
133
|
with_pool(url, deadline: deadline) do |conn|
|
130
134
|
next if finished
|
131
135
|
|
132
|
-
req = self.class.build_request(request) do
|
136
|
+
req, closing = self.class.build_request(request) do
|
133
137
|
self.class.calibrate_socket_timeout(conn, deadline)
|
134
138
|
end
|
135
139
|
|
@@ -154,8 +158,11 @@ module Orb
|
|
154
158
|
end
|
155
159
|
end
|
156
160
|
rescue Timeout::Error
|
157
|
-
raise Orb::Errors::APITimeoutError
|
161
|
+
raise Orb::Errors::APITimeoutError.new(url: url, request: req)
|
162
|
+
rescue StandardError
|
163
|
+
raise Orb::Errors::APIConnectionError.new(url: url, request: req)
|
158
164
|
end
|
165
|
+
# rubocop:enable Metrics/BlockLength
|
159
166
|
|
160
167
|
conn, _, response = enum.next
|
161
168
|
body = Orb::Internal::Util.fused_enum(enum, external: true) do
|
@@ -165,7 +172,9 @@ module Orb
|
|
165
172
|
rescue StopIteration
|
166
173
|
nil
|
167
174
|
end
|
175
|
+
ensure
|
168
176
|
conn.finish if !eof && conn&.started?
|
177
|
+
closing&.call
|
169
178
|
end
|
170
179
|
[Integer(response.code), response, (response.body = body)]
|
171
180
|
end
|
@@ -79,10 +79,20 @@ module Orb
|
|
79
79
|
#
|
80
80
|
# @param value [Array<Object>, Object]
|
81
81
|
#
|
82
|
+
# @param state [Hash{Symbol=>Object}] .
|
83
|
+
#
|
84
|
+
# @option state [Boolean] :can_retry
|
85
|
+
#
|
82
86
|
# @return [Array<Object>, Object]
|
83
|
-
def dump(value)
|
87
|
+
def dump(value, state:)
|
84
88
|
target = item_type
|
85
|
-
value.is_a?(Array)
|
89
|
+
if value.is_a?(Array)
|
90
|
+
value.map do
|
91
|
+
Orb::Internal::Type::Converter.dump(target, _1, state: state)
|
92
|
+
end
|
93
|
+
else
|
94
|
+
super
|
95
|
+
end
|
86
96
|
end
|
87
97
|
|
88
98
|
# @api private
|
@@ -252,8 +252,12 @@ module Orb
|
|
252
252
|
#
|
253
253
|
# @param value [Orb::Internal::Type::BaseModel, Object]
|
254
254
|
#
|
255
|
+
# @param state [Hash{Symbol=>Object}] .
|
256
|
+
#
|
257
|
+
# @option state [Boolean] :can_retry
|
258
|
+
#
|
255
259
|
# @return [Hash{Object=>Object}, Object]
|
256
|
-
def dump(value)
|
260
|
+
def dump(value, state:)
|
257
261
|
unless (coerced = Orb::Internal::Util.coerce_hash(value)).is_a?(Hash)
|
258
262
|
return super
|
259
263
|
end
|
@@ -264,7 +268,7 @@ module Orb
|
|
264
268
|
name = key.is_a?(String) ? key.to_sym : key
|
265
269
|
case (field = known_fields[name])
|
266
270
|
in nil
|
267
|
-
acc.store(name, super(val))
|
271
|
+
acc.store(name, super(val, state: state))
|
268
272
|
else
|
269
273
|
api_name, mode, type_fn = field.fetch_values(:api_name, :mode, :type_fn)
|
270
274
|
case mode
|
@@ -272,7 +276,7 @@ module Orb
|
|
272
276
|
next
|
273
277
|
else
|
274
278
|
target = type_fn.call
|
275
|
-
acc.store(api_name, Orb::Internal::Type::Converter.dump(target, val))
|
279
|
+
acc.store(api_name, Orb::Internal::Type::Converter.dump(target, val, state: state))
|
276
280
|
end
|
277
281
|
end
|
278
282
|
end
|
@@ -337,12 +341,12 @@ module Orb
|
|
337
341
|
# @param a [Object]
|
338
342
|
#
|
339
343
|
# @return [String]
|
340
|
-
def to_json(*a) =
|
344
|
+
def to_json(*a) = Orb::Internal::Type::Converter.dump(self.class, self).to_json(*a)
|
341
345
|
|
342
346
|
# @param a [Object]
|
343
347
|
#
|
344
348
|
# @return [String]
|
345
|
-
def to_yaml(*a) =
|
349
|
+
def to_yaml(*a) = Orb::Internal::Type::Converter.dump(self.class, self).to_yaml(*a)
|
346
350
|
|
347
351
|
# Create a new instance of a model.
|
348
352
|
#
|
@@ -45,8 +45,12 @@ module Orb
|
|
45
45
|
# #
|
46
46
|
# # @param value [Boolean, Object]
|
47
47
|
# #
|
48
|
+
# # @param state [Hash{Symbol=>Object}] .
|
49
|
+
# #
|
50
|
+
# # @option state [Boolean] :can_retry
|
51
|
+
# #
|
48
52
|
# # @return [Boolean, Object]
|
49
|
-
# def dump(value) = super
|
53
|
+
# def dump(value, state:) = super
|
50
54
|
end
|
51
55
|
end
|
52
56
|
end
|
@@ -26,15 +26,24 @@ module Orb
|
|
26
26
|
#
|
27
27
|
# @param value [Object]
|
28
28
|
#
|
29
|
+
# @param state [Hash{Symbol=>Object}] .
|
30
|
+
#
|
31
|
+
# @option state [Boolean] :can_retry
|
32
|
+
#
|
29
33
|
# @return [Object]
|
30
|
-
def dump(value)
|
34
|
+
def dump(value, state:)
|
31
35
|
case value
|
32
36
|
in Array
|
33
|
-
value.map { Orb::Internal::Type::Unknown.dump(_1) }
|
37
|
+
value.map { Orb::Internal::Type::Unknown.dump(_1, state: state) }
|
34
38
|
in Hash
|
35
|
-
value.transform_values { Orb::Internal::Type::Unknown.dump(_1) }
|
39
|
+
value.transform_values { Orb::Internal::Type::Unknown.dump(_1, state: state) }
|
36
40
|
in Orb::Internal::Type::BaseModel
|
37
|
-
value.class.dump(value)
|
41
|
+
value.class.dump(value, state: state)
|
42
|
+
in StringIO
|
43
|
+
value.string
|
44
|
+
in Pathname | IO
|
45
|
+
state[:can_retry] = false if value.is_a?(IO)
|
46
|
+
Orb::Internal::Util::SerializationAdapter.new(value)
|
38
47
|
else
|
39
48
|
value
|
40
49
|
end
|
@@ -140,9 +149,9 @@ module Orb
|
|
140
149
|
if value.is_a?(Integer)
|
141
150
|
exactness[:yes] += 1
|
142
151
|
return value
|
143
|
-
elsif strictness == :strong
|
152
|
+
elsif strictness == :strong && Integer(value, exception: false) != value
|
144
153
|
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
145
|
-
raise TypeError.new(message)
|
154
|
+
raise value.is_a?(Numeric) ? ArgumentError.new(message) : TypeError.new(message)
|
146
155
|
else
|
147
156
|
Kernel.then do
|
148
157
|
return Integer(value).tap { exactness[:maybe] += 1 }
|
@@ -182,18 +191,26 @@ module Orb
|
|
182
191
|
rescue ArgumentError, TypeError => e
|
183
192
|
raise e if strictness == :strong
|
184
193
|
end
|
185
|
-
in -> { _1 <=
|
194
|
+
in -> { _1 <= StringIO } if value.is_a?(String)
|
186
195
|
exactness[:yes] += 1
|
187
196
|
return StringIO.new(value.b)
|
188
197
|
else
|
189
198
|
end
|
190
199
|
in Symbol
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
200
|
+
case value
|
201
|
+
in Symbol | String
|
202
|
+
if value.to_sym == target
|
203
|
+
exactness[:yes] += 1
|
204
|
+
return target
|
205
|
+
else
|
206
|
+
exactness[:maybe] += 1
|
207
|
+
return value
|
208
|
+
end
|
209
|
+
else
|
210
|
+
if strictness == :strong
|
211
|
+
message = "cannot convert non-matching #{value.class} into #{target.inspect}"
|
212
|
+
raise ArgumentError.new(message)
|
213
|
+
end
|
197
214
|
end
|
198
215
|
else
|
199
216
|
end
|
@@ -207,13 +224,21 @@ module Orb
|
|
207
224
|
# @api private
|
208
225
|
#
|
209
226
|
# @param target [Orb::Internal::Type::Converter, Class]
|
227
|
+
#
|
210
228
|
# @param value [Object]
|
211
229
|
#
|
230
|
+
# @param state [Hash{Symbol=>Object}] .
|
231
|
+
#
|
232
|
+
# @option state [Boolean] :can_retry
|
233
|
+
#
|
212
234
|
# @return [Object]
|
213
|
-
def dump(target, value)
|
214
|
-
|
215
|
-
|
216
|
-
|
235
|
+
def dump(target, value, state: {can_retry: true})
|
236
|
+
case target
|
237
|
+
in Orb::Internal::Type::Converter
|
238
|
+
target.dump(value, state: state)
|
239
|
+
else
|
240
|
+
Orb::Internal::Type::Unknown.dump(value, state: state)
|
241
|
+
end
|
217
242
|
end
|
218
243
|
end
|
219
244
|
end
|
@@ -97,8 +97,12 @@ module Orb
|
|
97
97
|
# #
|
98
98
|
# # @param value [Symbol, Object]
|
99
99
|
# #
|
100
|
+
# # @param state [Hash{Symbol=>Object}] .
|
101
|
+
# #
|
102
|
+
# # @option state [Boolean] :can_retry
|
103
|
+
# #
|
100
104
|
# # @return [Symbol, Object]
|
101
|
-
# def dump(value) = super
|
105
|
+
# def dump(value, state:) = super
|
102
106
|
end
|
103
107
|
end
|
104
108
|
end
|
@@ -99,12 +99,16 @@ module Orb
|
|
99
99
|
#
|
100
100
|
# @param value [Hash{Object=>Object}, Object]
|
101
101
|
#
|
102
|
+
# @param state [Hash{Symbol=>Object}] .
|
103
|
+
#
|
104
|
+
# @option state [Boolean] :can_retry
|
105
|
+
#
|
102
106
|
# @return [Hash{Symbol=>Object}, Object]
|
103
|
-
def dump(value)
|
107
|
+
def dump(value, state:)
|
104
108
|
target = item_type
|
105
109
|
if value.is_a?(Hash)
|
106
110
|
value.transform_values do
|
107
|
-
Orb::Internal::Type::Converter.dump(target, _1)
|
111
|
+
Orb::Internal::Type::Converter.dump(target, _1, state: state)
|
108
112
|
end
|
109
113
|
else
|
110
114
|
super
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Orb
|
4
|
+
module Internal
|
5
|
+
module Type
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
#
|
10
|
+
# Either `Pathname` or `StringIO`.
|
11
|
+
class IOLike
|
12
|
+
extend Orb::Internal::Type::Converter
|
13
|
+
|
14
|
+
# @param other [Object]
|
15
|
+
#
|
16
|
+
# @return [Boolean]
|
17
|
+
def self.===(other)
|
18
|
+
case other
|
19
|
+
in StringIO | Pathname | IO
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param other [Object]
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
def self.==(other) = other.is_a?(Class) && other <= Orb::Internal::Type::IOLike
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
# @param value [StringIO, String, Object]
|
35
|
+
#
|
36
|
+
# @param state [Hash{Symbol=>Object}] .
|
37
|
+
#
|
38
|
+
# @option state [Boolean, :strong] :strictness
|
39
|
+
#
|
40
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
41
|
+
#
|
42
|
+
# @option state [Integer] :branched
|
43
|
+
#
|
44
|
+
# @return [StringIO, Object]
|
45
|
+
def coerce(value, state:)
|
46
|
+
exactness = state.fetch(:exactness)
|
47
|
+
case value
|
48
|
+
in String
|
49
|
+
exactness[:yes] += 1
|
50
|
+
StringIO.new(value)
|
51
|
+
in StringIO
|
52
|
+
exactness[:yes] += 1
|
53
|
+
value
|
54
|
+
else
|
55
|
+
exactness[:no] += 1
|
56
|
+
value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!parse
|
61
|
+
# # @api private
|
62
|
+
# #
|
63
|
+
# # @param value [Pathname, StringIO, IO, String, Object]
|
64
|
+
# #
|
65
|
+
# # @param state [Hash{Symbol=>Object}] .
|
66
|
+
# #
|
67
|
+
# # @option state [Boolean] :can_retry
|
68
|
+
# #
|
69
|
+
# # @return [Pathname, StringIO, IO, String, Object]
|
70
|
+
# def dump(value, state:) = super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -26,9 +26,18 @@ module Orb
|
|
26
26
|
#
|
27
27
|
# @return [Array(Object, Hash{Symbol=>Object})]
|
28
28
|
def dump_request(params)
|
29
|
-
|
29
|
+
state = {can_retry: true}
|
30
|
+
case (dumped = dump(params, state: state))
|
30
31
|
in Hash
|
31
|
-
|
32
|
+
options = Orb::Internal::Util.coerce_hash(dumped[:request_options])
|
33
|
+
request_options =
|
34
|
+
case [options, state.fetch(:can_retry)]
|
35
|
+
in [Hash | nil, false]
|
36
|
+
{**options.to_h, max_retries: 0}
|
37
|
+
else
|
38
|
+
options
|
39
|
+
end
|
40
|
+
[dumped.except(:request_options), request_options]
|
32
41
|
else
|
33
42
|
[dumped, nil]
|
34
43
|
end
|
@@ -205,15 +205,19 @@ module Orb
|
|
205
205
|
#
|
206
206
|
# @param value [Object]
|
207
207
|
#
|
208
|
+
# @param state [Hash{Symbol=>Object}] .
|
209
|
+
#
|
210
|
+
# @option state [Boolean] :can_retry
|
211
|
+
#
|
208
212
|
# @return [Object]
|
209
|
-
def dump(value)
|
213
|
+
def dump(value, state:)
|
210
214
|
if (target = resolve_variant(value))
|
211
|
-
return Orb::Internal::Type::Converter.dump(target, value)
|
215
|
+
return Orb::Internal::Type::Converter.dump(target, value, state: state)
|
212
216
|
end
|
213
217
|
|
214
218
|
known_variants.each do
|
215
219
|
target = _2.call
|
216
|
-
return Orb::Internal::Type::Converter.dump(target, value) if target === value
|
220
|
+
return Orb::Internal::Type::Converter.dump(target, value, state: state) if target === value
|
217
221
|
end
|
218
222
|
|
219
223
|
super
|
@@ -47,8 +47,12 @@ module Orb
|
|
47
47
|
# #
|
48
48
|
# # @param value [Object]
|
49
49
|
# #
|
50
|
+
# # @param state [Hash{Symbol=>Object}] .
|
51
|
+
# #
|
52
|
+
# # @option state [Boolean] :can_retry
|
53
|
+
# #
|
50
54
|
# # @return [Object]
|
51
|
-
# def dump(value) = super
|
55
|
+
# def dump(value, state:) = super
|
52
56
|
end
|
53
57
|
|
54
58
|
# rubocop:enable Lint/UnusedMethodArgument
|
data/lib/orb/internal/util.rb
CHANGED
@@ -122,7 +122,7 @@ module Orb
|
|
122
122
|
# @return [Hash{Object=>Object}, Object]
|
123
123
|
def coerce_hash(input)
|
124
124
|
case input
|
125
|
-
in NilClass | Array | Set | Enumerator
|
125
|
+
in NilClass | Array | Set | Enumerator | StringIO | IO
|
126
126
|
input
|
127
127
|
else
|
128
128
|
input.respond_to?(:to_h) ? input.to_h : input
|
@@ -348,10 +348,47 @@ module Orb
|
|
348
348
|
end
|
349
349
|
end
|
350
350
|
|
351
|
+
# @api private
|
352
|
+
class SerializationAdapter
|
353
|
+
# @return [Pathname, IO]
|
354
|
+
attr_reader :inner
|
355
|
+
|
356
|
+
# @param a [Object]
|
357
|
+
#
|
358
|
+
# @return [String]
|
359
|
+
def to_json(*a) = (inner.is_a?(IO) ? inner.read : inner.read(binmode: true)).to_json(*a)
|
360
|
+
|
361
|
+
# @param a [Object]
|
362
|
+
#
|
363
|
+
# @return [String]
|
364
|
+
def to_yaml(*a) = (inner.is_a?(IO) ? inner.read : inner.read(binmode: true)).to_yaml(*a)
|
365
|
+
|
366
|
+
# @api private
|
367
|
+
#
|
368
|
+
# @param inner [Pathname, IO]
|
369
|
+
def initialize(inner) = @inner = inner
|
370
|
+
end
|
371
|
+
|
351
372
|
# @api private
|
352
373
|
#
|
353
374
|
# An adapter that satisfies the IO interface required by `::IO.copy_stream`
|
354
375
|
class ReadIOAdapter
|
376
|
+
# @api private
|
377
|
+
#
|
378
|
+
# @return [Boolean, nil]
|
379
|
+
def close? = @closing
|
380
|
+
|
381
|
+
# @api private
|
382
|
+
def close
|
383
|
+
case @stream
|
384
|
+
in Enumerator
|
385
|
+
Orb::Internal::Util.close_fused!(@stream)
|
386
|
+
in IO if close?
|
387
|
+
@stream.close
|
388
|
+
else
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
355
392
|
# @api private
|
356
393
|
#
|
357
394
|
# @param max_len [Integer, nil]
|
@@ -396,12 +433,21 @@ module Orb
|
|
396
433
|
|
397
434
|
# @api private
|
398
435
|
#
|
399
|
-
# @param
|
436
|
+
# @param src [String, Pathname, StringIO, Enumerable<String>]
|
400
437
|
# @param blk [Proc]
|
401
438
|
#
|
402
439
|
# @yieldparam [String]
|
403
|
-
def initialize(
|
404
|
-
@stream =
|
440
|
+
def initialize(src, &blk)
|
441
|
+
@stream =
|
442
|
+
case src
|
443
|
+
in String
|
444
|
+
StringIO.new(src)
|
445
|
+
in Pathname
|
446
|
+
@closing = true
|
447
|
+
src.open(binmode: true)
|
448
|
+
else
|
449
|
+
src
|
450
|
+
end
|
405
451
|
@buf = String.new.b
|
406
452
|
@blk = blk
|
407
453
|
end
|
@@ -414,9 +460,10 @@ module Orb
|
|
414
460
|
# @return [Enumerable<String>]
|
415
461
|
def writable_enum(&blk)
|
416
462
|
Enumerator.new do |y|
|
463
|
+
buf = String.new.b
|
417
464
|
y.define_singleton_method(:write) do
|
418
|
-
self << _1
|
419
|
-
|
465
|
+
self << buf.replace(_1)
|
466
|
+
buf.bytesize
|
420
467
|
end
|
421
468
|
|
422
469
|
blk.call(y)
|
@@ -431,29 +478,39 @@ module Orb
|
|
431
478
|
# @param boundary [String]
|
432
479
|
# @param key [Symbol, String]
|
433
480
|
# @param val [Object]
|
434
|
-
|
481
|
+
# @param closing [Array<Proc>]
|
482
|
+
private def write_multipart_chunk(y, boundary:, key:, val:, closing:)
|
483
|
+
val = val.inner if val.is_a?(Orb::Internal::Util::SerializationAdapter)
|
484
|
+
|
435
485
|
y << "--#{boundary}\r\n"
|
436
486
|
y << "Content-Disposition: form-data"
|
437
487
|
unless key.nil?
|
438
488
|
name = ERB::Util.url_encode(key.to_s)
|
439
489
|
y << "; name=\"#{name}\""
|
440
490
|
end
|
441
|
-
|
491
|
+
case val
|
492
|
+
in Pathname | IO
|
442
493
|
filename = ERB::Util.url_encode(File.basename(val.to_path))
|
443
494
|
y << "; filename=\"#{filename}\""
|
495
|
+
else
|
444
496
|
end
|
445
497
|
y << "\r\n"
|
446
498
|
case val
|
499
|
+
in Pathname
|
500
|
+
y << "Content-Type: application/octet-stream\r\n\r\n"
|
501
|
+
io = val.open(binmode: true)
|
502
|
+
closing << io.method(:close)
|
503
|
+
IO.copy_stream(io, y)
|
447
504
|
in IO
|
448
505
|
y << "Content-Type: application/octet-stream\r\n\r\n"
|
449
|
-
IO.copy_stream(val
|
506
|
+
IO.copy_stream(val, y)
|
450
507
|
in StringIO
|
451
508
|
y << "Content-Type: application/octet-stream\r\n\r\n"
|
452
509
|
y << val.string
|
453
510
|
in String
|
454
511
|
y << "Content-Type: application/octet-stream\r\n\r\n"
|
455
512
|
y << val.to_s
|
456
|
-
in
|
513
|
+
in _ if primitive?(val)
|
457
514
|
y << "Content-Type: text/plain\r\n\r\n"
|
458
515
|
y << val.to_s
|
459
516
|
else
|
@@ -471,6 +528,7 @@ module Orb
|
|
471
528
|
private def encode_multipart_streaming(body)
|
472
529
|
boundary = SecureRandom.urlsafe_base64(60)
|
473
530
|
|
531
|
+
closing = []
|
474
532
|
strio = writable_enum do |y|
|
475
533
|
case body
|
476
534
|
in Hash
|
@@ -478,19 +536,20 @@ module Orb
|
|
478
536
|
case val
|
479
537
|
in Array if val.all? { primitive?(_1) }
|
480
538
|
val.each do |v|
|
481
|
-
write_multipart_chunk(y, boundary: boundary, key: key, val: v)
|
539
|
+
write_multipart_chunk(y, boundary: boundary, key: key, val: v, closing: closing)
|
482
540
|
end
|
483
541
|
else
|
484
|
-
write_multipart_chunk(y, boundary: boundary, key: key, val: val)
|
542
|
+
write_multipart_chunk(y, boundary: boundary, key: key, val: val, closing: closing)
|
485
543
|
end
|
486
544
|
end
|
487
545
|
else
|
488
|
-
write_multipart_chunk(y, boundary: boundary, key: nil, val: body)
|
546
|
+
write_multipart_chunk(y, boundary: boundary, key: nil, val: body, closing: closing)
|
489
547
|
end
|
490
548
|
y << "--#{boundary}--\r\n"
|
491
549
|
end
|
492
550
|
|
493
|
-
|
551
|
+
fused_io = fused_enum(strio) { closing.each(&:call) }
|
552
|
+
[boundary, fused_io]
|
494
553
|
end
|
495
554
|
|
496
555
|
# @api private
|
@@ -501,21 +560,21 @@ module Orb
|
|
501
560
|
# @return [Object]
|
502
561
|
def encode_content(headers, body)
|
503
562
|
content_type = headers["content-type"]
|
563
|
+
body = body.inner if body.is_a?(Orb::Internal::Util::SerializationAdapter)
|
564
|
+
|
504
565
|
case [content_type, body]
|
505
|
-
in [%r{^application/(?:vnd\.api\+)?json},
|
566
|
+
in [%r{^application/(?:vnd\.api\+)?json}, Hash | Array | -> { primitive?(_1) }]
|
506
567
|
[headers, JSON.fast_generate(body)]
|
507
|
-
in [%r{^application/(?:x-)?jsonl}, Enumerable]
|
568
|
+
in [%r{^application/(?:x-)?jsonl}, Enumerable] unless body.is_a?(StringIO) || body.is_a?(IO)
|
508
569
|
[headers, body.lazy.map { JSON.fast_generate(_1) }]
|
509
|
-
in [%r{^multipart/form-data}, Hash |
|
570
|
+
in [%r{^multipart/form-data}, Hash | Pathname | StringIO | IO]
|
510
571
|
boundary, strio = encode_multipart_streaming(body)
|
511
572
|
headers = {**headers, "content-type" => "#{content_type}; boundary=#{boundary}"}
|
512
573
|
[headers, strio]
|
513
|
-
in [_, IO]
|
514
|
-
[headers, body.tap(&:rewind)]
|
515
|
-
in [_, StringIO]
|
516
|
-
[headers, body.string]
|
517
574
|
in [_, Symbol | Numeric]
|
518
575
|
[headers, body.to_s]
|
576
|
+
in [_, StringIO]
|
577
|
+
[headers, body.string]
|
519
578
|
else
|
520
579
|
[headers, body]
|
521
580
|
end
|