finch-api 0.1.0.pre.alpha.3 → 0.1.0.pre.alpha.4
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/lib/finch-api/base_model.rb +421 -473
- data/lib/finch-api/individuals_page.rb +1 -1
- data/lib/finch-api/models/introspection.rb +12 -1
- data/lib/finch-api/page.rb +1 -1
- data/lib/finch-api/single_page.rb +1 -1
- data/lib/finch-api/util.rb +3 -5
- data/lib/finch-api/version.rb +1 -1
- data/lib/finch-api.rb +0 -1
- data/rbi/lib/finch-api/base_client.rbi +1 -1
- data/rbi/lib/finch-api/base_model.rbi +84 -118
- data/rbi/lib/finch-api/models/introspection.rbi +20 -3
- data/rbi/lib/finch-api/version.rbi +1 -1
- data/sig/finch-api/base_client.rbs +1 -1
- data/sig/finch-api/base_model.rbs +41 -53
- data/sig/finch-api/models/introspection.rbs +10 -1
- data/sig/finch-api/version.rbs +1 -1
- metadata +2 -5
- data/lib/finch-api/extern.rb +0 -7
- data/rbi/lib/finch-api/extern.rbi +0 -7
- data/sig/finch-api/extern.rbs +0 -4
data/lib/finch-api/base_model.rb
CHANGED
@@ -9,22 +9,34 @@ module FinchAPI
|
|
9
9
|
#
|
10
10
|
# @param value [Object]
|
11
11
|
#
|
12
|
-
# @
|
13
|
-
def coerce(value) = value
|
14
|
-
|
15
|
-
# @api private
|
12
|
+
# @param state [Hash{Symbol=>Object}] .
|
16
13
|
#
|
17
|
-
#
|
14
|
+
# @option state [Boolean, :strong] :strictness
|
15
|
+
#
|
16
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
17
|
+
#
|
18
|
+
# @option state [Integer] :branched
|
18
19
|
#
|
19
20
|
# @return [Object]
|
20
|
-
def
|
21
|
+
def coerce(value, state:) = (raise NotImplementedError)
|
21
22
|
|
22
23
|
# @api private
|
23
24
|
#
|
24
25
|
# @param value [Object]
|
25
26
|
#
|
26
|
-
# @return [
|
27
|
-
def
|
27
|
+
# @return [Object]
|
28
|
+
def dump(value)
|
29
|
+
case value
|
30
|
+
in Array
|
31
|
+
value.map { FinchAPI::Unknown.dump(_1) }
|
32
|
+
in Hash
|
33
|
+
value.transform_values { FinchAPI::Unknown.dump(_1) }
|
34
|
+
in FinchAPI::BaseModel
|
35
|
+
value.class.dump(value)
|
36
|
+
else
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end
|
28
40
|
|
29
41
|
# rubocop:enable Lint/UnusedMethodArgument
|
30
42
|
|
@@ -44,14 +56,14 @@ module FinchAPI
|
|
44
56
|
# @return [Proc]
|
45
57
|
def type_info(spec)
|
46
58
|
case spec
|
47
|
-
in Hash
|
48
|
-
type_info(spec.slice(:const, :enum, :union).first&.last)
|
49
59
|
in Proc
|
50
60
|
spec
|
51
|
-
in
|
52
|
-
|
61
|
+
in Hash
|
62
|
+
type_info(spec.slice(:const, :enum, :union).first&.last)
|
53
63
|
in true | false
|
54
64
|
-> { FinchAPI::BooleanModel }
|
65
|
+
in FinchAPI::Converter | Class | Symbol
|
66
|
+
-> { spec }
|
55
67
|
in NilClass | Integer | Float
|
56
68
|
-> { spec.class }
|
57
69
|
end
|
@@ -66,108 +78,127 @@ module FinchAPI
|
|
66
78
|
# converted value
|
67
79
|
# 3. otherwise, the given `value` unaltered
|
68
80
|
#
|
81
|
+
# The coercion process is subject to improvement between minor release versions.
|
82
|
+
# See https://docs.pydantic.dev/latest/concepts/unions/#smart-mode
|
83
|
+
#
|
69
84
|
# @param target [FinchAPI::Converter, Class]
|
85
|
+
#
|
70
86
|
# @param value [Object]
|
71
87
|
#
|
88
|
+
# @param state [Hash{Symbol=>Object}] The `strictness` is one of `true`, `false`, or `:strong`. This informs the
|
89
|
+
# coercion strategy when we have to decide between multiple possible conversion
|
90
|
+
# targets:
|
91
|
+
#
|
92
|
+
# - `true`: the conversion must be exact, with minimum coercion.
|
93
|
+
# - `false`: the conversion can be approximate, with some coercion.
|
94
|
+
# - `:strong`: the conversion must be exact, with no coercion, and raise an error
|
95
|
+
# if not possible.
|
96
|
+
#
|
97
|
+
# The `exactness` is `Hash` with keys being one of `yes`, `no`, or `maybe`. For
|
98
|
+
# any given conversion attempt, the exactness will be updated based on how closely
|
99
|
+
# the value recursively matches the target type:
|
100
|
+
#
|
101
|
+
# - `yes`: the value can be converted to the target type with minimum coercion.
|
102
|
+
# - `maybe`: the value can be converted to the target type with some reasonable
|
103
|
+
# coercion.
|
104
|
+
# - `no`: the value cannot be converted to the target type.
|
105
|
+
#
|
106
|
+
# See implementation below for more details.
|
107
|
+
#
|
108
|
+
# @option state [Boolean, :strong] :strictness
|
109
|
+
#
|
110
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
111
|
+
#
|
112
|
+
# @option state [Integer] :branched
|
113
|
+
#
|
72
114
|
# @return [Object]
|
73
|
-
def coerce(target, value)
|
115
|
+
def coerce(target, value, state: {strictness: true, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0})
|
116
|
+
strictness, exactness = state.fetch_values(:strictness, :exactness)
|
117
|
+
|
74
118
|
case target
|
75
119
|
in FinchAPI::Converter
|
76
|
-
target.coerce(value)
|
77
|
-
in
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
value
|
120
|
+
return target.coerce(value, state: state)
|
121
|
+
in Class
|
122
|
+
if value.is_a?(target)
|
123
|
+
exactness[:yes] += 1
|
124
|
+
return value
|
83
125
|
end
|
84
|
-
|
126
|
+
|
85
127
|
case target
|
86
128
|
in -> { _1 <= NilClass }
|
87
|
-
nil
|
129
|
+
exactness[value.nil? ? :yes : :maybe] += 1
|
130
|
+
return nil
|
88
131
|
in -> { _1 <= Integer }
|
89
|
-
value.is_a?(
|
132
|
+
if value.is_a?(Integer)
|
133
|
+
exactness[:yes] += 1
|
134
|
+
return value
|
135
|
+
elsif strictness == :strong
|
136
|
+
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
137
|
+
raise TypeError.new(message)
|
138
|
+
else
|
139
|
+
Kernel.then do
|
140
|
+
return Integer(value).tap { exactness[:maybe] += 1 }
|
141
|
+
rescue ArgumentError, TypeError
|
142
|
+
end
|
143
|
+
end
|
90
144
|
in -> { _1 <= Float }
|
91
|
-
value.is_a?(Numeric)
|
92
|
-
|
93
|
-
|
145
|
+
if value.is_a?(Numeric)
|
146
|
+
exactness[:yes] += 1
|
147
|
+
return Float(value)
|
148
|
+
elsif strictness == :strong
|
149
|
+
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
150
|
+
raise TypeError.new(message)
|
151
|
+
else
|
152
|
+
Kernel.then do
|
153
|
+
return Float(value).tap { exactness[:maybe] += 1 }
|
154
|
+
rescue ArgumentError, TypeError
|
155
|
+
end
|
156
|
+
end
|
94
157
|
in -> { _1 <= String }
|
95
|
-
|
158
|
+
case value
|
159
|
+
in String | Symbol | Numeric
|
160
|
+
exactness[value.is_a?(Numeric) ? :maybe : :yes] += 1
|
161
|
+
return value.to_s
|
162
|
+
else
|
163
|
+
if strictness == :strong
|
164
|
+
message = "no implicit conversion of #{value.class} into #{target.inspect}"
|
165
|
+
raise TypeError.new(message)
|
166
|
+
end
|
167
|
+
end
|
96
168
|
in -> { _1 <= Date || _1 <= Time }
|
97
|
-
|
98
|
-
|
99
|
-
|
169
|
+
Kernel.then do
|
170
|
+
return target.parse(value).tap { exactness[:yes] += 1 }
|
171
|
+
rescue ArgumentError, TypeError => e
|
172
|
+
raise e if strictness == :strong
|
173
|
+
end
|
174
|
+
in -> { _1 <= IO } if value.is_a?(String)
|
175
|
+
exactness[:yes] += 1
|
176
|
+
return StringIO.new(value.b)
|
100
177
|
else
|
101
|
-
value
|
102
178
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# @return [Object]
|
112
|
-
def dump(target, value)
|
113
|
-
case target
|
114
|
-
in FinchAPI::Converter
|
115
|
-
target.dump(value)
|
179
|
+
in Symbol
|
180
|
+
if (value.is_a?(Symbol) || value.is_a?(String)) && value.to_sym == target
|
181
|
+
exactness[:yes] += 1
|
182
|
+
return target
|
183
|
+
elsif strictness == :strong
|
184
|
+
message = "cannot convert non-matching #{value.class} into #{target.inspect}"
|
185
|
+
raise ArgumentError.new(message)
|
186
|
+
end
|
116
187
|
else
|
117
|
-
value
|
118
188
|
end
|
189
|
+
|
190
|
+
exactness[:no] += 1
|
191
|
+
value
|
119
192
|
end
|
120
193
|
|
121
194
|
# @api private
|
122
195
|
#
|
123
|
-
# The underlying algorithm for computing maximal compatibility is subject to
|
124
|
-
# future improvements.
|
125
|
-
#
|
126
|
-
# Similar to `#.coerce`, used to determine the best union variant to decode into.
|
127
|
-
#
|
128
|
-
# 1. determine if strict-ish coercion is possible
|
129
|
-
# 2. return either result of successful coercion or if loose coercion is possible
|
130
|
-
# 3. return a score for recursively tallied count for fields that can be coerced
|
131
|
-
#
|
132
196
|
# @param target [FinchAPI::Converter, Class]
|
133
197
|
# @param value [Object]
|
134
198
|
#
|
135
199
|
# @return [Object]
|
136
|
-
def
|
137
|
-
|
138
|
-
in FinchAPI::Converter
|
139
|
-
target.try_strict_coerce(value)
|
140
|
-
in Symbol
|
141
|
-
case value
|
142
|
-
in Symbol | String if (val = value.to_sym) == target
|
143
|
-
[true, val, 1]
|
144
|
-
else
|
145
|
-
[false, false, 0]
|
146
|
-
end
|
147
|
-
in Module
|
148
|
-
case [target, value]
|
149
|
-
in [-> { _1 <= NilClass }, _]
|
150
|
-
[true, nil, value.nil? ? 1 : 0]
|
151
|
-
in [-> { _1 <= Integer }, Numeric]
|
152
|
-
[true, Integer(value), 1]
|
153
|
-
in [-> { _1 <= Float }, Numeric]
|
154
|
-
[true, Float(value), 1]
|
155
|
-
in [-> { _1 <= Symbol }, String]
|
156
|
-
[true, value.to_sym, 1]
|
157
|
-
in [-> { _1 <= String }, Symbol]
|
158
|
-
[true, value.to_s, 1]
|
159
|
-
in [-> { _1 <= Date || _1 <= Time }, String]
|
160
|
-
Kernel.then do
|
161
|
-
[true, target.parse(value), 1]
|
162
|
-
rescue ArgumentError
|
163
|
-
[false, false, 0]
|
164
|
-
end
|
165
|
-
in [_, ^target]
|
166
|
-
[true, value, 1]
|
167
|
-
else
|
168
|
-
[false, false, 0]
|
169
|
-
end
|
170
|
-
end
|
200
|
+
def dump(target, value)
|
201
|
+
target.is_a?(FinchAPI::Converter) ? target.dump(value) : FinchAPI::Unknown.dump(value)
|
171
202
|
end
|
172
203
|
end
|
173
204
|
end
|
@@ -193,13 +224,23 @@ module FinchAPI
|
|
193
224
|
def self.==(other) = other.is_a?(Class) && other <= FinchAPI::Unknown
|
194
225
|
|
195
226
|
class << self
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
227
|
+
# @api private
|
228
|
+
#
|
229
|
+
# @param value [Object]
|
230
|
+
#
|
231
|
+
# @param state [Hash{Symbol=>Object}] .
|
232
|
+
#
|
233
|
+
# @option state [Boolean, :strong] :strictness
|
234
|
+
#
|
235
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
236
|
+
#
|
237
|
+
# @option state [Integer] :branched
|
238
|
+
#
|
239
|
+
# @return [Object]
|
240
|
+
def coerce(value, state:)
|
241
|
+
state.fetch(:exactness)[:yes] += 1
|
242
|
+
value
|
243
|
+
end
|
203
244
|
|
204
245
|
# @!parse
|
205
246
|
# # @api private
|
@@ -208,16 +249,6 @@ module FinchAPI
|
|
208
249
|
# #
|
209
250
|
# # @return [Object]
|
210
251
|
# def dump(value) = super
|
211
|
-
|
212
|
-
# @api private
|
213
|
-
#
|
214
|
-
# @param value [Object]
|
215
|
-
#
|
216
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
217
|
-
def try_strict_coerce(value)
|
218
|
-
# prevent unknown variant from being chosen during the first coercion pass
|
219
|
-
[false, true, 0]
|
220
|
-
end
|
221
252
|
end
|
222
253
|
|
223
254
|
# rubocop:enable Lint/UnusedMethodArgument
|
@@ -242,13 +273,23 @@ module FinchAPI
|
|
242
273
|
def self.==(other) = other.is_a?(Class) && other <= FinchAPI::BooleanModel
|
243
274
|
|
244
275
|
class << self
|
245
|
-
#
|
246
|
-
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
276
|
+
# @api private
|
277
|
+
#
|
278
|
+
# @param value [Boolean, Object]
|
279
|
+
#
|
280
|
+
# @param state [Hash{Symbol=>Object}] .
|
281
|
+
#
|
282
|
+
# @option state [Boolean, :strong] :strictness
|
283
|
+
#
|
284
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
285
|
+
#
|
286
|
+
# @option state [Integer] :branched
|
287
|
+
#
|
288
|
+
# @return [Boolean, Object]
|
289
|
+
def coerce(value, state:)
|
290
|
+
state.fetch(:exactness)[value == true || value == false ? :yes : :no] += 1
|
291
|
+
value
|
292
|
+
end
|
252
293
|
|
253
294
|
# @!parse
|
254
295
|
# # @api private
|
@@ -257,20 +298,6 @@ module FinchAPI
|
|
257
298
|
# #
|
258
299
|
# # @return [Boolean, Object]
|
259
300
|
# def dump(value) = super
|
260
|
-
|
261
|
-
# @api private
|
262
|
-
#
|
263
|
-
# @param value [Object]
|
264
|
-
#
|
265
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
266
|
-
def try_strict_coerce(value)
|
267
|
-
case value
|
268
|
-
in true | false
|
269
|
-
[true, value, 1]
|
270
|
-
else
|
271
|
-
[false, false, 0]
|
272
|
-
end
|
273
|
-
end
|
274
301
|
end
|
275
302
|
end
|
276
303
|
|
@@ -333,19 +360,34 @@ module FinchAPI
|
|
333
360
|
#
|
334
361
|
# @return [Boolean]
|
335
362
|
def ==(other)
|
336
|
-
other.is_a?(Module) && other.singleton_class
|
363
|
+
other.is_a?(Module) && other.singleton_class <= FinchAPI::Enum && other.values.to_set == values.to_set
|
337
364
|
end
|
338
365
|
|
339
366
|
# @api private
|
340
367
|
#
|
368
|
+
# Unlike with primitives, `Enum` additionally validates that the value is a member
|
369
|
+
# of the enum.
|
370
|
+
#
|
341
371
|
# @param value [String, Symbol, Object]
|
342
372
|
#
|
373
|
+
# @param state [Hash{Symbol=>Object}] .
|
374
|
+
#
|
375
|
+
# @option state [Boolean, :strong] :strictness
|
376
|
+
#
|
377
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
378
|
+
#
|
379
|
+
# @option state [Integer] :branched
|
380
|
+
#
|
343
381
|
# @return [Symbol, Object]
|
344
|
-
def coerce(value)
|
345
|
-
|
346
|
-
|
382
|
+
def coerce(value, state:)
|
383
|
+
exactness = state.fetch(:exactness)
|
384
|
+
val = value.is_a?(String) ? value.to_sym : value
|
385
|
+
|
386
|
+
if values.include?(val)
|
387
|
+
exactness[:yes] += 1
|
347
388
|
val
|
348
389
|
else
|
390
|
+
exactness[values.first&.class == val.class ? :maybe : :no] += 1
|
349
391
|
value
|
350
392
|
end
|
351
393
|
end
|
@@ -357,27 +399,6 @@ module FinchAPI
|
|
357
399
|
# #
|
358
400
|
# # @return [Symbol, Object]
|
359
401
|
# def dump(value) = super
|
360
|
-
|
361
|
-
# @api private
|
362
|
-
#
|
363
|
-
# @param value [Object]
|
364
|
-
#
|
365
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
366
|
-
def try_strict_coerce(value)
|
367
|
-
return [true, value, 1] if values.include?(value)
|
368
|
-
|
369
|
-
case value
|
370
|
-
in Symbol | String if values.include?(val = value.to_sym)
|
371
|
-
[true, val, 1]
|
372
|
-
else
|
373
|
-
case [value, values.first]
|
374
|
-
in [true | false, true | false] | [Integer, Integer] | [Symbol | String, Symbol]
|
375
|
-
[false, true, 0]
|
376
|
-
else
|
377
|
-
[false, false, 0]
|
378
|
-
end
|
379
|
-
end
|
380
|
-
end
|
381
402
|
end
|
382
403
|
|
383
404
|
# @api private
|
@@ -422,9 +443,7 @@ module FinchAPI
|
|
422
443
|
# All of the specified variants for this union.
|
423
444
|
#
|
424
445
|
# @return [Array<Object>]
|
425
|
-
def variants
|
426
|
-
derefed_variants.map(&:last)
|
427
|
-
end
|
446
|
+
def variants = derefed_variants.map(&:last)
|
428
447
|
|
429
448
|
# @api private
|
430
449
|
#
|
@@ -454,7 +473,7 @@ module FinchAPI
|
|
454
473
|
case key
|
455
474
|
in Symbol
|
456
475
|
[key, FinchAPI::Converter.type_info(spec)]
|
457
|
-
in Proc | FinchAPI::Converter |
|
476
|
+
in Proc | FinchAPI::Converter | Class | Hash
|
458
477
|
[nil, FinchAPI::Converter.type_info(key)]
|
459
478
|
end
|
460
479
|
|
@@ -471,16 +490,14 @@ module FinchAPI
|
|
471
490
|
in [_, FinchAPI::BaseModel]
|
472
491
|
value.class
|
473
492
|
in [Symbol, Hash]
|
474
|
-
key =
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
end
|
493
|
+
key = value.fetch(@discriminator) do
|
494
|
+
value.fetch(@discriminator.to_s, FinchAPI::Util::OMIT)
|
495
|
+
end
|
496
|
+
|
497
|
+
return nil if key == FinchAPI::Util::OMIT
|
480
498
|
|
481
499
|
key = key.to_sym if key.is_a?(String)
|
482
|
-
|
483
|
-
resolved.nil? ? FinchAPI::Unknown : resolved.call
|
500
|
+
known_variants.find { |k,| k == key }&.last&.call
|
484
501
|
else
|
485
502
|
nil
|
486
503
|
end
|
@@ -502,36 +519,63 @@ module FinchAPI
|
|
502
519
|
#
|
503
520
|
# @return [Boolean]
|
504
521
|
def ==(other)
|
505
|
-
other.is_a?(Module) && other.singleton_class
|
522
|
+
other.is_a?(Module) && other.singleton_class <= FinchAPI::Union && other.derefed_variants == derefed_variants
|
506
523
|
end
|
507
524
|
|
508
525
|
# @api private
|
509
526
|
#
|
510
527
|
# @param value [Object]
|
511
528
|
#
|
529
|
+
# @param state [Hash{Symbol=>Object}] .
|
530
|
+
#
|
531
|
+
# @option state [Boolean, :strong] :strictness
|
532
|
+
#
|
533
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
534
|
+
#
|
535
|
+
# @option state [Integer] :branched
|
536
|
+
#
|
512
537
|
# @return [Object]
|
513
|
-
def coerce(value)
|
514
|
-
if (
|
515
|
-
return FinchAPI::Converter.coerce(
|
538
|
+
def coerce(value, state:)
|
539
|
+
if (target = resolve_variant(value))
|
540
|
+
return FinchAPI::Converter.coerce(target, value, state: state)
|
516
541
|
end
|
517
542
|
|
518
|
-
|
543
|
+
strictness = state.fetch(:strictness)
|
544
|
+
exactness = state.fetch(:exactness)
|
545
|
+
state[:strictness] = strictness == :strong ? true : strictness
|
519
546
|
|
547
|
+
alternatives = []
|
520
548
|
known_variants.each do |_, variant_fn|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
549
|
+
target = variant_fn.call
|
550
|
+
exact = state[:exactness] = {yes: 0, no: 0, maybe: 0}
|
551
|
+
state[:branched] += 1
|
552
|
+
|
553
|
+
coerced = FinchAPI::Converter.coerce(target, value, state: state)
|
554
|
+
yes, no, maybe = exact.values
|
555
|
+
if (no + maybe).zero? || (!strictness && yes.positive?)
|
556
|
+
exact.each { exactness[_1] += _2 }
|
557
|
+
state[:exactness] = exactness
|
525
558
|
return coerced
|
526
|
-
|
527
|
-
|
528
|
-
in [false, false, _]
|
529
|
-
nil
|
559
|
+
elsif maybe.positive?
|
560
|
+
alternatives << [[-yes, -maybe, no], exact, coerced]
|
530
561
|
end
|
531
562
|
end
|
532
563
|
|
533
|
-
|
534
|
-
|
564
|
+
case alternatives.sort_by(&:first)
|
565
|
+
in []
|
566
|
+
exactness[:no] += 1
|
567
|
+
if strictness == :strong
|
568
|
+
message = "no possible conversion of #{value.class} into a variant of #{target.inspect}"
|
569
|
+
raise ArgumentError.new(message)
|
570
|
+
end
|
571
|
+
value
|
572
|
+
in [[_, exact, coerced], *]
|
573
|
+
exact.each { exactness[_1] += _2 }
|
574
|
+
coerced
|
575
|
+
end
|
576
|
+
.tap { state[:exactness] = exactness }
|
577
|
+
ensure
|
578
|
+
state[:strictness] = strictness
|
535
579
|
end
|
536
580
|
|
537
581
|
# @api private
|
@@ -540,49 +584,16 @@ module FinchAPI
|
|
540
584
|
#
|
541
585
|
# @return [Object]
|
542
586
|
def dump(value)
|
543
|
-
if (
|
544
|
-
return FinchAPI::Converter.dump(
|
545
|
-
end
|
546
|
-
|
547
|
-
known_variants.each do |_, variant_fn|
|
548
|
-
variant = variant_fn.call
|
549
|
-
if variant === value
|
550
|
-
return FinchAPI::Converter.dump(variant, value)
|
551
|
-
end
|
587
|
+
if (target = resolve_variant(value))
|
588
|
+
return FinchAPI::Converter.dump(target, value)
|
552
589
|
end
|
553
|
-
value
|
554
|
-
end
|
555
590
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
#
|
560
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
561
|
-
def try_strict_coerce(value)
|
562
|
-
# TODO(ruby) this will result in super linear decoding behaviour for nested unions
|
563
|
-
# follow up with a decoding context that captures current strictness levels
|
564
|
-
if (variant = resolve_variant(value))
|
565
|
-
return Converter.try_strict_coerce(variant, value)
|
591
|
+
known_variants.each do
|
592
|
+
target = _2.call
|
593
|
+
return FinchAPI::Converter.dump(target, value) if target === value
|
566
594
|
end
|
567
595
|
|
568
|
-
|
569
|
-
max_score = 0
|
570
|
-
|
571
|
-
known_variants.each do |_, variant_fn|
|
572
|
-
variant = variant_fn.call
|
573
|
-
|
574
|
-
case FinchAPI::Converter.try_strict_coerce(variant, value)
|
575
|
-
in [true, coerced, score]
|
576
|
-
return [true, coerced, score]
|
577
|
-
in [false, true, score]
|
578
|
-
coercible = true
|
579
|
-
max_score = [max_score, score].max
|
580
|
-
in [false, false, _]
|
581
|
-
nil
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
[false, coercible, max_score]
|
596
|
+
super
|
586
597
|
end
|
587
598
|
|
588
599
|
# rubocop:enable Style/CaseEquality
|
@@ -613,36 +624,46 @@ module FinchAPI
|
|
613
624
|
# @param other [Object]
|
614
625
|
#
|
615
626
|
# @return [Boolean]
|
616
|
-
def ===(other)
|
617
|
-
type = item_type
|
618
|
-
case other
|
619
|
-
in Array
|
620
|
-
# rubocop:disable Style/CaseEquality
|
621
|
-
other.all? { type === _1 }
|
622
|
-
# rubocop:enable Style/CaseEquality
|
623
|
-
else
|
624
|
-
false
|
625
|
-
end
|
626
|
-
end
|
627
|
+
def ===(other) = other.is_a?(Array) && other.all?(item_type)
|
627
628
|
|
628
629
|
# @param other [Object]
|
629
630
|
#
|
630
631
|
# @return [Boolean]
|
631
|
-
def ==(other) = other.is_a?(FinchAPI::ArrayOf) && other.item_type == item_type
|
632
|
+
def ==(other) = other.is_a?(FinchAPI::ArrayOf) && other.nilable? == nilable? && other.item_type == item_type
|
632
633
|
|
633
634
|
# @api private
|
634
635
|
#
|
635
636
|
# @param value [Enumerable, Object]
|
636
637
|
#
|
638
|
+
# @param state [Hash{Symbol=>Object}] .
|
639
|
+
#
|
640
|
+
# @option state [Boolean, :strong] :strictness
|
641
|
+
#
|
642
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
643
|
+
#
|
644
|
+
# @option state [Integer] :branched
|
645
|
+
#
|
637
646
|
# @return [Array<Object>, Object]
|
638
|
-
def coerce(value)
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
value
|
647
|
+
def coerce(value, state:)
|
648
|
+
exactness = state.fetch(:exactness)
|
649
|
+
|
650
|
+
unless value.is_a?(Array)
|
651
|
+
exactness[:no] += 1
|
652
|
+
return value
|
645
653
|
end
|
654
|
+
|
655
|
+
target = item_type
|
656
|
+
exactness[:yes] += 1
|
657
|
+
value
|
658
|
+
.map do |item|
|
659
|
+
case [nilable?, item]
|
660
|
+
in [true, nil]
|
661
|
+
exactness[:yes] += 1
|
662
|
+
nil
|
663
|
+
else
|
664
|
+
FinchAPI::Converter.coerce(target, item, state: state)
|
665
|
+
end
|
666
|
+
end
|
646
667
|
end
|
647
668
|
|
648
669
|
# @api private
|
@@ -651,57 +672,19 @@ module FinchAPI
|
|
651
672
|
#
|
652
673
|
# @return [Array<Object>, Object]
|
653
674
|
def dump(value)
|
654
|
-
|
655
|
-
|
656
|
-
in Enumerable unless value.is_a?(Hash)
|
657
|
-
value.map { FinchAPI::Converter.dump(type, _1) }.to_a
|
658
|
-
else
|
659
|
-
value
|
660
|
-
end
|
675
|
+
target = item_type
|
676
|
+
value.is_a?(Array) ? value.map { FinchAPI::Converter.dump(target, _1) } : super
|
661
677
|
end
|
662
678
|
|
663
679
|
# @api private
|
664
680
|
#
|
665
|
-
# @
|
666
|
-
|
667
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
668
|
-
def try_strict_coerce(value)
|
669
|
-
case value
|
670
|
-
in Array
|
671
|
-
type = item_type
|
672
|
-
great_success = true
|
673
|
-
tally = 0
|
674
|
-
|
675
|
-
mapped =
|
676
|
-
value.map do |item|
|
677
|
-
case FinchAPI::Converter.try_strict_coerce(type, item)
|
678
|
-
in [true, coerced, score]
|
679
|
-
tally += score
|
680
|
-
coerced
|
681
|
-
in [false, true, score]
|
682
|
-
great_success = false
|
683
|
-
tally += score
|
684
|
-
item
|
685
|
-
in [false, false, _]
|
686
|
-
great_success &&= item.nil?
|
687
|
-
item
|
688
|
-
end
|
689
|
-
end
|
690
|
-
|
691
|
-
if great_success
|
692
|
-
[true, mapped, tally]
|
693
|
-
else
|
694
|
-
[false, true, tally]
|
695
|
-
end
|
696
|
-
else
|
697
|
-
[false, false, 0]
|
698
|
-
end
|
699
|
-
end
|
681
|
+
# @return [FinchAPI::Converter, Class]
|
682
|
+
protected def item_type = @item_type_fn.call
|
700
683
|
|
701
684
|
# @api private
|
702
685
|
#
|
703
|
-
# @return [
|
704
|
-
protected def
|
686
|
+
# @return [Boolean]
|
687
|
+
protected def nilable? = @nilable
|
705
688
|
|
706
689
|
# @api private
|
707
690
|
#
|
@@ -718,6 +701,7 @@ module FinchAPI
|
|
718
701
|
# @option spec [Boolean] :"nil?"
|
719
702
|
def initialize(type_info, spec = {})
|
720
703
|
@item_type_fn = FinchAPI::Converter.type_info(type_info || spec)
|
704
|
+
@nilable = spec[:nil?]
|
721
705
|
end
|
722
706
|
end
|
723
707
|
|
@@ -765,24 +749,46 @@ module FinchAPI
|
|
765
749
|
# @param other [Object]
|
766
750
|
#
|
767
751
|
# @return [Boolean]
|
768
|
-
def ==(other) = other.is_a?(FinchAPI::HashOf) && other.item_type == item_type
|
752
|
+
def ==(other) = other.is_a?(FinchAPI::HashOf) && other.nilable? == nilable? && other.item_type == item_type
|
769
753
|
|
770
754
|
# @api private
|
771
755
|
#
|
772
756
|
# @param value [Hash{Object=>Object}, Object]
|
773
757
|
#
|
758
|
+
# @param state [Hash{Symbol=>Object}] .
|
759
|
+
#
|
760
|
+
# @option state [Boolean, :strong] :strictness
|
761
|
+
#
|
762
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
763
|
+
#
|
764
|
+
# @option state [Integer] :branched
|
765
|
+
#
|
774
766
|
# @return [Hash{Symbol=>Object}, Object]
|
775
|
-
def coerce(value)
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
[key.is_a?(String) ? key.to_sym : key, coerced]
|
782
|
-
end
|
783
|
-
else
|
784
|
-
value
|
767
|
+
def coerce(value, state:)
|
768
|
+
exactness = state.fetch(:exactness)
|
769
|
+
|
770
|
+
unless value.is_a?(Hash)
|
771
|
+
exactness[:no] += 1
|
772
|
+
return value
|
785
773
|
end
|
774
|
+
|
775
|
+
target = item_type
|
776
|
+
exactness[:yes] += 1
|
777
|
+
value
|
778
|
+
.to_h do |key, val|
|
779
|
+
k = key.is_a?(String) ? key.to_sym : key
|
780
|
+
v =
|
781
|
+
case [nilable?, val]
|
782
|
+
in [true, nil]
|
783
|
+
exactness[:yes] += 1
|
784
|
+
nil
|
785
|
+
else
|
786
|
+
FinchAPI::Converter.coerce(target, val, state: state)
|
787
|
+
end
|
788
|
+
|
789
|
+
exactness[:no] += 1 unless k.is_a?(Symbol)
|
790
|
+
[k, v]
|
791
|
+
end
|
786
792
|
end
|
787
793
|
|
788
794
|
# @api private
|
@@ -791,59 +797,19 @@ module FinchAPI
|
|
791
797
|
#
|
792
798
|
# @return [Hash{Symbol=>Object}, Object]
|
793
799
|
def dump(value)
|
794
|
-
|
795
|
-
|
796
|
-
in Hash
|
797
|
-
value.transform_values do |val|
|
798
|
-
FinchAPI::Converter.dump(type, val)
|
799
|
-
end
|
800
|
-
else
|
801
|
-
value
|
802
|
-
end
|
800
|
+
target = item_type
|
801
|
+
value.is_a?(Hash) ? value.transform_values { FinchAPI::Converter.dump(target, _1) } : super
|
803
802
|
end
|
804
803
|
|
805
804
|
# @api private
|
806
805
|
#
|
807
|
-
# @
|
808
|
-
|
809
|
-
# @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
|
810
|
-
def try_strict_coerce(value)
|
811
|
-
case value
|
812
|
-
in Hash
|
813
|
-
type = item_type
|
814
|
-
great_success = true
|
815
|
-
tally = 0
|
816
|
-
|
817
|
-
mapped =
|
818
|
-
value.transform_values do |val|
|
819
|
-
case FinchAPI::Converter.try_strict_coerce(type, val)
|
820
|
-
in [true, coerced, score]
|
821
|
-
tally += score
|
822
|
-
coerced
|
823
|
-
in [false, true, score]
|
824
|
-
great_success = false
|
825
|
-
tally += score
|
826
|
-
val
|
827
|
-
in [false, false, _]
|
828
|
-
great_success &&= val.nil?
|
829
|
-
val
|
830
|
-
end
|
831
|
-
end
|
832
|
-
|
833
|
-
if great_success
|
834
|
-
[true, mapped, tally]
|
835
|
-
else
|
836
|
-
[false, true, tally]
|
837
|
-
end
|
838
|
-
else
|
839
|
-
[false, false, 0]
|
840
|
-
end
|
841
|
-
end
|
806
|
+
# @return [FinchAPI::Converter, Class]
|
807
|
+
protected def item_type = @item_type_fn.call
|
842
808
|
|
843
809
|
# @api private
|
844
810
|
#
|
845
|
-
# @return [
|
846
|
-
protected def
|
811
|
+
# @return [Boolean]
|
812
|
+
protected def nilable? = @nilable
|
847
813
|
|
848
814
|
# @api private
|
849
815
|
#
|
@@ -860,6 +826,7 @@ module FinchAPI
|
|
860
826
|
# @option spec [Boolean] :"nil?"
|
861
827
|
def initialize(type_info, spec = {})
|
862
828
|
@item_type_fn = FinchAPI::Converter.type_info(type_info || spec)
|
829
|
+
@nilable = spec[:nil?]
|
863
830
|
end
|
864
831
|
end
|
865
832
|
|
@@ -886,13 +853,6 @@ module FinchAPI
|
|
886
853
|
@known_fields ||= (self < FinchAPI::BaseModel ? superclass.known_fields.dup : {})
|
887
854
|
end
|
888
855
|
|
889
|
-
# @api private
|
890
|
-
#
|
891
|
-
# @return [Hash{Symbol=>Symbol}]
|
892
|
-
def reverse_map
|
893
|
-
@reverse_map ||= (self < FinchAPI::BaseModel ? superclass.reverse_map.dup : {})
|
894
|
-
end
|
895
|
-
|
896
856
|
# @api private
|
897
857
|
#
|
898
858
|
# @return [Hash{Symbol=>Hash{Symbol=>Object}}]
|
@@ -902,11 +862,6 @@ module FinchAPI
|
|
902
862
|
end
|
903
863
|
end
|
904
864
|
|
905
|
-
# @api private
|
906
|
-
#
|
907
|
-
# @return [Hash{Symbol=>Proc}]
|
908
|
-
def defaults = (@defaults ||= {})
|
909
|
-
|
910
865
|
# @api private
|
911
866
|
#
|
912
867
|
# @param name_sym [Symbol]
|
@@ -927,38 +882,40 @@ module FinchAPI
|
|
927
882
|
private def add_field(name_sym, required:, type_info:, spec:)
|
928
883
|
type_fn, info =
|
929
884
|
case type_info
|
930
|
-
in Proc |
|
885
|
+
in Proc | FinchAPI::Converter | Class
|
931
886
|
[FinchAPI::Converter.type_info({**spec, union: type_info}), spec]
|
932
887
|
in Hash
|
933
888
|
[FinchAPI::Converter.type_info(type_info), type_info]
|
934
889
|
end
|
935
890
|
|
936
|
-
fallback = info[:const]
|
937
|
-
defaults[name_sym] = fallback if required && !info[:nil?] && info.key?(:const)
|
938
|
-
|
939
|
-
key = info[:api_name]&.tap { reverse_map[_1] = name_sym } || name_sym
|
940
891
|
setter = "#{name_sym}="
|
892
|
+
api_name = info.fetch(:api_name, name_sym)
|
893
|
+
nilable = info[:nil?]
|
894
|
+
const = required && !nilable ? info.fetch(:const, FinchAPI::Util::OMIT) : FinchAPI::Util::OMIT
|
941
895
|
|
942
|
-
if known_fields.key?(name_sym)
|
943
|
-
[name_sym, setter].each { undef_method(_1) }
|
944
|
-
end
|
896
|
+
[name_sym, setter].each { undef_method(_1) } if known_fields.key?(name_sym)
|
945
897
|
|
946
|
-
known_fields[name_sym] =
|
898
|
+
known_fields[name_sym] =
|
899
|
+
{
|
900
|
+
mode: @mode,
|
901
|
+
api_name: api_name,
|
902
|
+
required: required,
|
903
|
+
nilable: nilable,
|
904
|
+
const: const,
|
905
|
+
type_fn: type_fn
|
906
|
+
}
|
947
907
|
|
948
|
-
define_method(setter)
|
949
|
-
@data[key] = val
|
950
|
-
end
|
908
|
+
define_method(setter) { @data.store(name_sym, _1) }
|
951
909
|
|
952
910
|
define_method(name_sym) do
|
953
|
-
|
954
|
-
value = @data.fetch(
|
955
|
-
|
911
|
+
target = type_fn.call
|
912
|
+
value = @data.fetch(name_sym) { const == FinchAPI::Util::OMIT ? nil : const }
|
913
|
+
state = {strictness: :strong, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
|
914
|
+
(nilable || !required) && value.nil? ? nil : FinchAPI::Converter.coerce(target, value, state: state)
|
956
915
|
rescue StandardError
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
"To get the unparsed API response, use #{name}[:#{key}]."
|
961
|
-
)
|
916
|
+
cls = self.class.name.split("::").last
|
917
|
+
message = "Failed to parse #{cls}.#{__method__} from #{value.class} to #{target.inspect}. To get the unparsed API response, use #{cls}[:#{__method__}]."
|
918
|
+
raise FinchAPI::ConversionError.new(message)
|
962
919
|
end
|
963
920
|
end
|
964
921
|
|
@@ -1024,120 +981,124 @@ module FinchAPI
|
|
1024
981
|
ensure
|
1025
982
|
@mode = nil
|
1026
983
|
end
|
984
|
+
|
985
|
+
# @param other [Object]
|
986
|
+
#
|
987
|
+
# @return [Boolean]
|
988
|
+
def ==(other) = other.is_a?(Class) && other <= FinchAPI::BaseModel && other.fields == fields
|
1027
989
|
end
|
1028
990
|
|
1029
991
|
# @param other [Object]
|
1030
992
|
#
|
1031
993
|
# @return [Boolean]
|
1032
|
-
def ==(other)
|
1033
|
-
case other
|
1034
|
-
in FinchAPI::BaseModel
|
1035
|
-
self.class.fields == other.class.fields && @data == other.to_h
|
1036
|
-
else
|
1037
|
-
false
|
1038
|
-
end
|
1039
|
-
end
|
994
|
+
def ==(other) = self.class == other.class && @data == other.to_h
|
1040
995
|
|
1041
996
|
class << self
|
1042
997
|
# @api private
|
1043
998
|
#
|
1044
999
|
# @param value [FinchAPI::BaseModel, Hash{Object=>Object}, Object]
|
1045
1000
|
#
|
1001
|
+
# @param state [Hash{Symbol=>Object}] .
|
1002
|
+
#
|
1003
|
+
# @option state [Boolean, :strong] :strictness
|
1004
|
+
#
|
1005
|
+
# @option state [Hash{Symbol=>Object}] :exactness
|
1006
|
+
#
|
1007
|
+
# @option state [Integer] :branched
|
1008
|
+
#
|
1046
1009
|
# @return [FinchAPI::BaseModel, Object]
|
1047
|
-
def coerce(value)
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
value
|
1010
|
+
def coerce(value, state:)
|
1011
|
+
exactness = state.fetch(:exactness)
|
1012
|
+
|
1013
|
+
if value.is_a?(self.class)
|
1014
|
+
exactness[:yes] += 1
|
1015
|
+
return value
|
1053
1016
|
end
|
1054
|
-
end
|
1055
1017
|
|
1056
|
-
|
1057
|
-
|
1058
|
-
# @param value [FinchAPI::BaseModel, Object]
|
1059
|
-
#
|
1060
|
-
# @return [Hash{Object=>Object}, Object]
|
1061
|
-
def dump(value)
|
1062
|
-
unless (coerced = FinchAPI::Util.coerce_hash(value)).is_a?(Hash)
|
1018
|
+
unless (val = FinchAPI::Util.coerce_hash(value)).is_a?(Hash)
|
1019
|
+
exactness[:no] += 1
|
1063
1020
|
return value
|
1064
1021
|
end
|
1022
|
+
exactness[:yes] += 1
|
1065
1023
|
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1024
|
+
keys = val.keys.to_set
|
1025
|
+
instance = new
|
1026
|
+
data = instance.to_h
|
1027
|
+
|
1028
|
+
fields.each do |name, field|
|
1029
|
+
mode, required, target = field.fetch_values(:mode, :required, :type)
|
1030
|
+
api_name, nilable, const = field.fetch_values(:api_name, :nilable, :const)
|
1031
|
+
|
1032
|
+
unless val.key?(api_name)
|
1033
|
+
if const != FinchAPI::Util::OMIT
|
1034
|
+
exactness[:yes] += 1
|
1035
|
+
elsif required && mode != :dump
|
1036
|
+
exactness[nilable ? :maybe : :no] += 1
|
1076
1037
|
else
|
1077
|
-
|
1078
|
-
[api_name, FinchAPI::Converter.dump(target, val)]
|
1038
|
+
exactness[:yes] += 1
|
1079
1039
|
end
|
1040
|
+
next
|
1080
1041
|
end
|
1081
|
-
end.to_h
|
1082
1042
|
|
1083
|
-
|
1084
|
-
|
1043
|
+
item = val.fetch(api_name)
|
1044
|
+
keys.delete(api_name)
|
1085
1045
|
|
1086
|
-
|
1046
|
+
converted =
|
1047
|
+
if item.nil? && (nilable || !required)
|
1048
|
+
exactness[nilable ? :yes : :maybe] += 1
|
1049
|
+
nil
|
1050
|
+
else
|
1051
|
+
coerced = FinchAPI::Converter.coerce(target, item, state: state)
|
1052
|
+
case target
|
1053
|
+
in FinchAPI::Converter | Symbol
|
1054
|
+
coerced
|
1055
|
+
else
|
1056
|
+
item
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
data.store(name, converted)
|
1087
1060
|
end
|
1088
1061
|
|
1089
|
-
|
1062
|
+
keys.each { data.store(_1, val.fetch(_1)) }
|
1063
|
+
instance
|
1090
1064
|
end
|
1091
1065
|
|
1092
1066
|
# @api private
|
1093
1067
|
#
|
1094
|
-
# @param value [Object]
|
1068
|
+
# @param value [FinchAPI::BaseModel, Object]
|
1095
1069
|
#
|
1096
|
-
# @return [
|
1097
|
-
def
|
1098
|
-
|
1099
|
-
|
1100
|
-
value = value.to_h
|
1101
|
-
else
|
1102
|
-
return [false, false, 0]
|
1070
|
+
# @return [Hash{Object=>Object}, Object]
|
1071
|
+
def dump(value)
|
1072
|
+
unless (coerced = FinchAPI::Util.coerce_hash(value)).is_a?(Hash)
|
1073
|
+
return super
|
1103
1074
|
end
|
1104
1075
|
|
1105
|
-
keys = value.keys.to_set
|
1106
|
-
great_success = true
|
1107
|
-
tally = 0
|
1108
1076
|
acc = {}
|
1109
1077
|
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
great_success = false
|
1124
|
-
tally += score
|
1125
|
-
acc[api_name] = item
|
1126
|
-
in [false, false, _]
|
1127
|
-
great_success &&= item.nil?
|
1078
|
+
coerced.each do |key, val|
|
1079
|
+
name = key.is_a?(String) ? key.to_sym : key
|
1080
|
+
case (field = known_fields[name])
|
1081
|
+
in nil
|
1082
|
+
acc.store(name, super(val))
|
1083
|
+
else
|
1084
|
+
mode, api_name, type_fn = field.fetch_values(:mode, :api_name, :type_fn)
|
1085
|
+
case mode
|
1086
|
+
in :coerce
|
1087
|
+
next
|
1088
|
+
else
|
1089
|
+
target = type_fn.call
|
1090
|
+
acc.store(api_name, FinchAPI::Converter.dump(target, val))
|
1128
1091
|
end
|
1129
|
-
in [true, false]
|
1130
|
-
great_success = false
|
1131
|
-
in [false, false]
|
1132
|
-
nil
|
1133
1092
|
end
|
1134
1093
|
end
|
1135
1094
|
|
1136
|
-
|
1137
|
-
|
1095
|
+
known_fields.each_value do |field|
|
1096
|
+
mode, api_name, const = field.fetch_values(:mode, :api_name, :const)
|
1097
|
+
next if mode == :coerce || acc.key?(api_name) || const == FinchAPI::Util::OMIT
|
1098
|
+
acc.store(api_name, const)
|
1138
1099
|
end
|
1139
1100
|
|
1140
|
-
|
1101
|
+
acc
|
1141
1102
|
end
|
1142
1103
|
end
|
1143
1104
|
|
@@ -1177,14 +1138,15 @@ module FinchAPI
|
|
1177
1138
|
#
|
1178
1139
|
# @return [Hash{Symbol=>Object}]
|
1179
1140
|
def deconstruct_keys(keys)
|
1180
|
-
(keys || self.class.known_fields.keys)
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1141
|
+
(keys || self.class.known_fields.keys)
|
1142
|
+
.filter_map do |k|
|
1143
|
+
unless self.class.known_fields.key?(k)
|
1144
|
+
next
|
1145
|
+
end
|
1184
1146
|
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1147
|
+
[k, public_send(k)]
|
1148
|
+
end
|
1149
|
+
.to_h
|
1188
1150
|
end
|
1189
1151
|
|
1190
1152
|
# Create a new instance of a model.
|
@@ -1193,34 +1155,20 @@ module FinchAPI
|
|
1193
1155
|
def initialize(data = {})
|
1194
1156
|
case FinchAPI::Util.coerce_hash(data)
|
1195
1157
|
in Hash => coerced
|
1196
|
-
@data = coerced
|
1197
|
-
name = key.to_sym
|
1198
|
-
mapped = self.class.reverse_map.fetch(name, name)
|
1199
|
-
type = self.class.fields[mapped]&.fetch(:type)
|
1200
|
-
stored =
|
1201
|
-
case [type, value]
|
1202
|
-
in [Module, Hash] if type <= FinchAPI::BaseModel
|
1203
|
-
type.new(value)
|
1204
|
-
in [FinchAPI::ArrayOf, Array] | [FinchAPI::HashOf, Hash]
|
1205
|
-
type.coerce(value)
|
1206
|
-
else
|
1207
|
-
value
|
1208
|
-
end
|
1209
|
-
[name, stored]
|
1210
|
-
end
|
1158
|
+
@data = coerced
|
1211
1159
|
else
|
1212
1160
|
raise ArgumentError.new("Expected a #{Hash} or #{FinchAPI::BaseModel}, got #{data.inspect}")
|
1213
1161
|
end
|
1214
1162
|
end
|
1215
1163
|
|
1216
|
-
# @return [String]
|
1217
|
-
def to_s = @data.to_s
|
1218
|
-
|
1219
1164
|
# @return [String]
|
1220
1165
|
def inspect
|
1221
|
-
|
1222
|
-
"#{
|
1223
|
-
|
1166
|
+
rows = self.class.known_fields.keys.map do
|
1167
|
+
"#{_1}=#{@data.key?(_1) ? public_send(_1) : ''}"
|
1168
|
+
rescue FinchAPI::ConversionError
|
1169
|
+
"#{_1}=#{@data.fetch(_1)}"
|
1170
|
+
end
|
1171
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} #{rows.join(' ')}>"
|
1224
1172
|
end
|
1225
1173
|
end
|
1226
1174
|
end
|