terminal-shop 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -8
  3. data/lib/terminal-shop/client.rb +1 -1
  4. data/lib/terminal-shop/models/address_create_params.rb +1 -1
  5. data/lib/terminal-shop/models/address_delete_params.rb +1 -1
  6. data/lib/terminal-shop/models/address_get_params.rb +1 -1
  7. data/lib/terminal-shop/models/address_list_params.rb +1 -1
  8. data/lib/terminal-shop/models/app_create_params.rb +1 -1
  9. data/lib/terminal-shop/models/app_delete_params.rb +1 -1
  10. data/lib/terminal-shop/models/app_get_params.rb +1 -1
  11. data/lib/terminal-shop/models/app_list_params.rb +1 -1
  12. data/lib/terminal-shop/models/card_collect_params.rb +1 -1
  13. data/lib/terminal-shop/models/card_create_params.rb +1 -1
  14. data/lib/terminal-shop/models/card_delete_params.rb +1 -1
  15. data/lib/terminal-shop/models/card_get_params.rb +1 -1
  16. data/lib/terminal-shop/models/card_list_params.rb +1 -1
  17. data/lib/terminal-shop/models/cart_clear_params.rb +1 -1
  18. data/lib/terminal-shop/models/cart_convert_params.rb +1 -1
  19. data/lib/terminal-shop/models/cart_get_params.rb +1 -1
  20. data/lib/terminal-shop/models/cart_set_address_params.rb +1 -1
  21. data/lib/terminal-shop/models/cart_set_card_params.rb +1 -1
  22. data/lib/terminal-shop/models/cart_set_item_params.rb +1 -1
  23. data/lib/terminal-shop/models/email_create_params.rb +1 -1
  24. data/lib/terminal-shop/models/order_create_params.rb +1 -1
  25. data/lib/terminal-shop/models/order_get_params.rb +1 -1
  26. data/lib/terminal-shop/models/order_list_params.rb +1 -1
  27. data/lib/terminal-shop/models/product_get_params.rb +1 -1
  28. data/lib/terminal-shop/models/product_list_params.rb +1 -1
  29. data/lib/terminal-shop/models/profile_me_params.rb +1 -1
  30. data/lib/terminal-shop/models/profile_update_params.rb +1 -1
  31. data/lib/terminal-shop/models/subscription_create_params.rb +1 -1
  32. data/lib/terminal-shop/models/subscription_delete_params.rb +1 -1
  33. data/lib/terminal-shop/models/subscription_get_params.rb +1 -1
  34. data/lib/terminal-shop/models/subscription_list_params.rb +1 -1
  35. data/lib/terminal-shop/models/token_create_params.rb +1 -1
  36. data/lib/terminal-shop/models/token_delete_params.rb +1 -1
  37. data/lib/terminal-shop/models/token_get_params.rb +1 -1
  38. data/lib/terminal-shop/models/token_list_params.rb +1 -1
  39. data/lib/terminal-shop/models/view_init_params.rb +1 -1
  40. data/lib/terminal-shop/request_options.rb +0 -33
  41. data/lib/terminal-shop/transport/base_client.rb +459 -0
  42. data/lib/terminal-shop/transport/pooled_net_requester.rb +182 -0
  43. data/lib/terminal-shop/type/array_of.rb +110 -0
  44. data/lib/terminal-shop/type/base_model.rb +362 -0
  45. data/lib/terminal-shop/type/base_page.rb +61 -0
  46. data/lib/terminal-shop/type/boolean_model.rb +52 -0
  47. data/lib/terminal-shop/type/converter.rb +217 -0
  48. data/lib/terminal-shop/type/enum.rb +101 -0
  49. data/lib/terminal-shop/type/hash_of.rb +136 -0
  50. data/lib/terminal-shop/type/request_parameters.rb +38 -0
  51. data/lib/terminal-shop/type/union.rb +183 -0
  52. data/lib/terminal-shop/type/unknown.rb +56 -0
  53. data/lib/terminal-shop/type.rb +23 -0
  54. data/lib/terminal-shop/util.rb +3 -5
  55. data/lib/terminal-shop/version.rb +1 -1
  56. data/lib/terminal-shop.rb +14 -6
  57. data/rbi/lib/terminal-shop/client.rbi +1 -1
  58. data/rbi/lib/terminal-shop/models/address_create_params.rbi +12 -2
  59. data/rbi/lib/terminal-shop/models/address_delete_params.rbi +1 -1
  60. data/rbi/lib/terminal-shop/models/address_get_params.rbi +1 -1
  61. data/rbi/lib/terminal-shop/models/address_list_params.rbi +1 -1
  62. data/rbi/lib/terminal-shop/models/app_create_params.rbi +1 -1
  63. data/rbi/lib/terminal-shop/models/app_delete_params.rbi +1 -1
  64. data/rbi/lib/terminal-shop/models/app_get_params.rbi +1 -1
  65. data/rbi/lib/terminal-shop/models/app_list_params.rbi +1 -1
  66. data/rbi/lib/terminal-shop/models/card_collect_params.rbi +1 -1
  67. data/rbi/lib/terminal-shop/models/card_create_params.rbi +1 -1
  68. data/rbi/lib/terminal-shop/models/card_delete_params.rbi +1 -1
  69. data/rbi/lib/terminal-shop/models/card_get_params.rbi +1 -1
  70. data/rbi/lib/terminal-shop/models/card_list_params.rbi +1 -1
  71. data/rbi/lib/terminal-shop/models/cart_clear_params.rbi +1 -1
  72. data/rbi/lib/terminal-shop/models/cart_convert_params.rbi +1 -1
  73. data/rbi/lib/terminal-shop/models/cart_get_params.rbi +1 -1
  74. data/rbi/lib/terminal-shop/models/cart_set_address_params.rbi +1 -1
  75. data/rbi/lib/terminal-shop/models/cart_set_card_params.rbi +1 -1
  76. data/rbi/lib/terminal-shop/models/cart_set_item_params.rbi +1 -1
  77. data/rbi/lib/terminal-shop/models/email_create_params.rbi +1 -1
  78. data/rbi/lib/terminal-shop/models/order_create_params.rbi +1 -1
  79. data/rbi/lib/terminal-shop/models/order_get_params.rbi +1 -1
  80. data/rbi/lib/terminal-shop/models/order_list_params.rbi +1 -1
  81. data/rbi/lib/terminal-shop/models/product_get_params.rbi +1 -1
  82. data/rbi/lib/terminal-shop/models/product_list_params.rbi +1 -1
  83. data/rbi/lib/terminal-shop/models/profile_me_params.rbi +1 -1
  84. data/rbi/lib/terminal-shop/models/profile_update_params.rbi +1 -1
  85. data/rbi/lib/terminal-shop/models/subscription_create_params.rbi +1 -1
  86. data/rbi/lib/terminal-shop/models/subscription_delete_params.rbi +1 -1
  87. data/rbi/lib/terminal-shop/models/subscription_get_params.rbi +1 -1
  88. data/rbi/lib/terminal-shop/models/subscription_list_params.rbi +1 -1
  89. data/rbi/lib/terminal-shop/models/token_create_params.rbi +1 -1
  90. data/rbi/lib/terminal-shop/models/token_delete_params.rbi +1 -1
  91. data/rbi/lib/terminal-shop/models/token_get_params.rbi +1 -1
  92. data/rbi/lib/terminal-shop/models/token_list_params.rbi +1 -1
  93. data/rbi/lib/terminal-shop/models/view_init_params.rbi +1 -1
  94. data/rbi/lib/terminal-shop/models/view_init_response.rbi +12 -1
  95. data/rbi/lib/terminal-shop/request_options.rbi +0 -15
  96. data/rbi/lib/terminal-shop/transport/base_client.rbi +208 -0
  97. data/rbi/lib/terminal-shop/transport/pooled_net_requester.rbi +64 -0
  98. data/rbi/lib/terminal-shop/type/array_of.rbi +82 -0
  99. data/rbi/lib/terminal-shop/type/base_model.rbi +194 -0
  100. data/rbi/lib/terminal-shop/type/base_page.rbi +38 -0
  101. data/rbi/lib/terminal-shop/type/boolean_model.rbi +41 -0
  102. data/rbi/lib/terminal-shop/type/converter.rbi +101 -0
  103. data/rbi/lib/terminal-shop/type/enum.rbi +58 -0
  104. data/rbi/lib/terminal-shop/type/hash_of.rbi +85 -0
  105. data/rbi/lib/terminal-shop/type/request_parameters.rbi +20 -0
  106. data/rbi/lib/terminal-shop/type/union.rbi +68 -0
  107. data/rbi/lib/terminal-shop/type/unknown.rbi +37 -0
  108. data/rbi/lib/terminal-shop/type.rbi +23 -0
  109. data/rbi/lib/terminal-shop/version.rbi +1 -1
  110. data/sig/terminal-shop/client.rbs +1 -1
  111. data/sig/terminal-shop/models/address_create_params.rbs +1 -1
  112. data/sig/terminal-shop/models/address_delete_params.rbs +1 -1
  113. data/sig/terminal-shop/models/address_get_params.rbs +1 -1
  114. data/sig/terminal-shop/models/address_list_params.rbs +1 -1
  115. data/sig/terminal-shop/models/app_create_params.rbs +1 -1
  116. data/sig/terminal-shop/models/app_delete_params.rbs +1 -1
  117. data/sig/terminal-shop/models/app_get_params.rbs +1 -1
  118. data/sig/terminal-shop/models/app_list_params.rbs +1 -1
  119. data/sig/terminal-shop/models/card_collect_params.rbs +1 -1
  120. data/sig/terminal-shop/models/card_create_params.rbs +1 -1
  121. data/sig/terminal-shop/models/card_delete_params.rbs +1 -1
  122. data/sig/terminal-shop/models/card_get_params.rbs +1 -1
  123. data/sig/terminal-shop/models/card_list_params.rbs +1 -1
  124. data/sig/terminal-shop/models/cart_clear_params.rbs +1 -1
  125. data/sig/terminal-shop/models/cart_convert_params.rbs +1 -1
  126. data/sig/terminal-shop/models/cart_get_params.rbs +1 -1
  127. data/sig/terminal-shop/models/cart_set_address_params.rbs +1 -1
  128. data/sig/terminal-shop/models/cart_set_card_params.rbs +1 -1
  129. data/sig/terminal-shop/models/cart_set_item_params.rbs +1 -1
  130. data/sig/terminal-shop/models/email_create_params.rbs +1 -1
  131. data/sig/terminal-shop/models/order_create_params.rbs +1 -1
  132. data/sig/terminal-shop/models/order_get_params.rbs +1 -1
  133. data/sig/terminal-shop/models/order_list_params.rbs +1 -1
  134. data/sig/terminal-shop/models/product_get_params.rbs +1 -1
  135. data/sig/terminal-shop/models/product_list_params.rbs +1 -1
  136. data/sig/terminal-shop/models/profile_me_params.rbs +1 -1
  137. data/sig/terminal-shop/models/profile_update_params.rbs +1 -1
  138. data/sig/terminal-shop/models/subscription_create_params.rbs +1 -1
  139. data/sig/terminal-shop/models/subscription_delete_params.rbs +1 -1
  140. data/sig/terminal-shop/models/subscription_get_params.rbs +1 -1
  141. data/sig/terminal-shop/models/subscription_list_params.rbs +1 -1
  142. data/sig/terminal-shop/models/token_create_params.rbs +1 -1
  143. data/sig/terminal-shop/models/token_delete_params.rbs +1 -1
  144. data/sig/terminal-shop/models/token_get_params.rbs +1 -1
  145. data/sig/terminal-shop/models/token_list_params.rbs +1 -1
  146. data/sig/terminal-shop/models/view_init_params.rbs +1 -1
  147. data/sig/terminal-shop/request_options.rbs +0 -10
  148. data/sig/terminal-shop/transport/base_client.rbs +110 -0
  149. data/sig/terminal-shop/transport/pooled_net_requester.rbs +39 -0
  150. data/sig/terminal-shop/type/array_of.rbs +36 -0
  151. data/sig/terminal-shop/type/base_model.rbs +73 -0
  152. data/sig/terminal-shop/type/base_page.rbs +22 -0
  153. data/sig/terminal-shop/type/boolean_model.rbs +18 -0
  154. data/sig/terminal-shop/type/converter.rbs +42 -0
  155. data/sig/terminal-shop/type/enum.rbs +22 -0
  156. data/sig/terminal-shop/type/hash_of.rbs +36 -0
  157. data/sig/terminal-shop/type/request_parameters.rbs +13 -0
  158. data/sig/terminal-shop/type/union.rbs +40 -0
  159. data/sig/terminal-shop/type/unknown.rbs +18 -0
  160. data/sig/terminal-shop/type.rbs +22 -0
  161. data/sig/terminal-shop/version.rbs +1 -1
  162. metadata +41 -17
  163. data/lib/terminal-shop/base_client.rb +0 -457
  164. data/lib/terminal-shop/base_model.rb +0 -1201
  165. data/lib/terminal-shop/base_page.rb +0 -59
  166. data/lib/terminal-shop/extern.rb +0 -7
  167. data/lib/terminal-shop/pooled_net_requester.rb +0 -180
  168. data/rbi/lib/terminal-shop/base_client.rbi +0 -197
  169. data/rbi/lib/terminal-shop/base_model.rbi +0 -645
  170. data/rbi/lib/terminal-shop/base_page.rbi +0 -36
  171. data/rbi/lib/terminal-shop/extern.rbi +0 -7
  172. data/rbi/lib/terminal-shop/pooled_net_requester.rbi +0 -59
  173. data/sig/terminal-shop/base_client.rbs +0 -108
  174. data/sig/terminal-shop/base_model.rbs +0 -262
  175. data/sig/terminal-shop/base_page.rbs +0 -20
  176. data/sig/terminal-shop/extern.rbs +0 -4
  177. data/sig/terminal-shop/pooled_net_requester.rbs +0 -37
@@ -1,1201 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TerminalShop
4
- # @api private
5
- module Converter
6
- # rubocop:disable Lint/UnusedMethodArgument
7
-
8
- # @api private
9
- #
10
- # @param value [Object]
11
- #
12
- # @return [Object]
13
- def coerce(value) = value
14
-
15
- # @api private
16
- #
17
- # @param value [Object]
18
- #
19
- # @return [Object]
20
- def dump(value) = value
21
-
22
- # @api private
23
- #
24
- # @param value [Object]
25
- #
26
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
27
- def try_strict_coerce(value) = (raise NotImplementedError)
28
-
29
- # rubocop:enable Lint/UnusedMethodArgument
30
-
31
- class << self
32
- # @api private
33
- #
34
- # @param spec [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class] .
35
- #
36
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
37
- #
38
- # @option spec [Proc] :enum
39
- #
40
- # @option spec [Proc] :union
41
- #
42
- # @option spec [Boolean] :"nil?"
43
- #
44
- # @return [Proc]
45
- def type_info(spec)
46
- case spec
47
- in Hash
48
- type_info(spec.slice(:const, :enum, :union).first&.last)
49
- in Proc
50
- spec
51
- in TerminalShop::Converter | Module | Symbol
52
- -> { spec }
53
- in true | false
54
- -> { TerminalShop::BooleanModel }
55
- in NilClass | Integer | Float
56
- -> { spec.class }
57
- end
58
- end
59
-
60
- # @api private
61
- #
62
- # Based on `target`, transform `value` into `target`, to the extent possible:
63
- #
64
- # 1. if the given `value` conforms to `target` already, return the given `value`
65
- # 2. if it's possible and safe to convert the given `value` to `target`, then the
66
- # converted value
67
- # 3. otherwise, the given `value` unaltered
68
- #
69
- # @param target [TerminalShop::Converter, Class]
70
- # @param value [Object]
71
- #
72
- # @return [Object]
73
- def coerce(target, value)
74
- case target
75
- in TerminalShop::Converter
76
- target.coerce(value)
77
- in Symbol
78
- case value
79
- in Symbol | String if (val = value.to_sym) == target
80
- val
81
- else
82
- value
83
- end
84
- in Module
85
- case target
86
- in -> { _1 <= NilClass }
87
- nil
88
- in -> { _1 <= Integer }
89
- value.is_a?(Numeric) ? Integer(value) : value
90
- in -> { _1 <= Float }
91
- value.is_a?(Numeric) ? Float(value) : value
92
- in -> { _1 <= Symbol }
93
- value.is_a?(String) ? value.to_sym : value
94
- in -> { _1 <= String }
95
- value.is_a?(Symbol) ? value.to_s : value
96
- in -> { _1 <= Date || _1 <= Time }
97
- value.is_a?(String) ? target.parse(value) : value
98
- in -> { _1 <= IO }
99
- value.is_a?(String) ? StringIO.new(value) : value
100
- else
101
- value
102
- end
103
- end
104
- end
105
-
106
- # @api private
107
- #
108
- # @param target [TerminalShop::Converter, Class]
109
- # @param value [Object]
110
- #
111
- # @return [Object]
112
- def dump(target, value)
113
- case target
114
- in TerminalShop::Converter
115
- target.dump(value)
116
- else
117
- value
118
- end
119
- end
120
-
121
- # @api private
122
- #
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
- # @param target [TerminalShop::Converter, Class]
133
- # @param value [Object]
134
- #
135
- # @return [Object]
136
- def try_strict_coerce(target, value)
137
- case target
138
- in TerminalShop::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
171
- end
172
- end
173
- end
174
-
175
- # @api private
176
- #
177
- # @abstract
178
- #
179
- # When we don't know what to expect for the value.
180
- class Unknown
181
- extend TerminalShop::Converter
182
-
183
- # rubocop:disable Lint/UnusedMethodArgument
184
-
185
- # @param other [Object]
186
- #
187
- # @return [Boolean]
188
- def self.===(other) = true
189
-
190
- # @param other [Object]
191
- #
192
- # @return [Boolean]
193
- def self.==(other) = other.is_a?(Class) && other <= TerminalShop::Unknown
194
-
195
- class << self
196
- # @!parse
197
- # # @api private
198
- # #
199
- # # @param value [Object]
200
- # #
201
- # # @return [Object]
202
- # def coerce(value) = super
203
-
204
- # @!parse
205
- # # @api private
206
- # #
207
- # # @param value [Object]
208
- # #
209
- # # @return [Object]
210
- # 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
- end
222
-
223
- # rubocop:enable Lint/UnusedMethodArgument
224
- end
225
-
226
- # @api private
227
- #
228
- # @abstract
229
- #
230
- # Ruby has no Boolean class; this is something for models to refer to.
231
- class BooleanModel
232
- extend TerminalShop::Converter
233
-
234
- # @param other [Object]
235
- #
236
- # @return [Boolean]
237
- def self.===(other) = other == true || other == false
238
-
239
- # @param other [Object]
240
- #
241
- # @return [Boolean]
242
- def self.==(other) = other.is_a?(Class) && other <= TerminalShop::BooleanModel
243
-
244
- class << self
245
- # @!parse
246
- # # @api private
247
- # #
248
- # # @param value [Boolean, Object]
249
- # #
250
- # # @return [Boolean, Object]
251
- # def coerce(value) = super
252
-
253
- # @!parse
254
- # # @api private
255
- # #
256
- # # @param value [Boolean, Object]
257
- # #
258
- # # @return [Boolean, Object]
259
- # 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
- end
275
- end
276
-
277
- # @api private
278
- #
279
- # A value from among a specified list of options. OpenAPI enum values map to Ruby
280
- # values in the SDK as follows:
281
- #
282
- # 1. boolean => true | false
283
- # 2. integer => Integer
284
- # 3. float => Float
285
- # 4. string => Symbol
286
- #
287
- # We can therefore convert string values to Symbols, but can't convert other
288
- # values safely.
289
- #
290
- # @example
291
- # # `region` is a `TerminalShop::Models::Region`
292
- # case region
293
- # when TerminalShop::Models::Region::EU
294
- # # ...
295
- # when TerminalShop::Models::Region::NA
296
- # # ...
297
- # else
298
- # puts(region)
299
- # end
300
- #
301
- # @example
302
- # case region
303
- # in :eu
304
- # # ...
305
- # in :na
306
- # # ...
307
- # else
308
- # puts(region)
309
- # end
310
- module Enum
311
- include TerminalShop::Converter
312
-
313
- # All of the valid Symbol values for this enum.
314
- #
315
- # @return [Array<NilClass, Boolean, Integer, Float, Symbol>]
316
- def values = (@values ||= constants.map { const_get(_1) })
317
-
318
- # @api private
319
- #
320
- # Guard against thread safety issues by instantiating `@values`.
321
- private def finalize! = values
322
-
323
- # @param other [Object]
324
- #
325
- # @return [Boolean]
326
- def ===(other) = values.include?(other)
327
-
328
- # @param other [Object]
329
- #
330
- # @return [Boolean]
331
- def ==(other)
332
- other.is_a?(Module) && other.singleton_class.ancestors.include?(TerminalShop::Enum) && other.values.to_set == values.to_set
333
- end
334
-
335
- # @api private
336
- #
337
- # @param value [String, Symbol, Object]
338
- #
339
- # @return [Symbol, Object]
340
- def coerce(value)
341
- case value
342
- in Symbol | String if values.include?(val = value.to_sym)
343
- val
344
- else
345
- value
346
- end
347
- end
348
-
349
- # @!parse
350
- # # @api private
351
- # #
352
- # # @param value [Symbol, Object]
353
- # #
354
- # # @return [Symbol, Object]
355
- # def dump(value) = super
356
-
357
- # @api private
358
- #
359
- # @param value [Object]
360
- #
361
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
362
- def try_strict_coerce(value)
363
- return [true, value, 1] if values.include?(value)
364
-
365
- case value
366
- in Symbol | String if values.include?(val = value.to_sym)
367
- [true, val, 1]
368
- else
369
- case [value, values.first]
370
- in [true | false, true | false] | [Integer, Integer] | [Symbol | String, Symbol]
371
- [false, true, 0]
372
- else
373
- [false, false, 0]
374
- end
375
- end
376
- end
377
- end
378
-
379
- # @api private
380
- module Union
381
- include TerminalShop::Converter
382
-
383
- # @api private
384
- #
385
- # All of the specified variant info for this union.
386
- #
387
- # @return [Array<Array(Symbol, Proc)>]
388
- private def known_variants = (@known_variants ||= [])
389
-
390
- # @api private
391
- #
392
- # @return [Array<Array(Symbol, Object)>]
393
- protected def derefed_variants
394
- @known_variants.map { |key, variant_fn| [key, variant_fn.call] }
395
- end
396
-
397
- # All of the specified variants for this union.
398
- #
399
- # @return [Array<Object>]
400
- def variants
401
- derefed_variants.map(&:last)
402
- end
403
-
404
- # @api private
405
- #
406
- # @param property [Symbol]
407
- private def discriminator(property)
408
- case property
409
- in Symbol
410
- @discriminator = property
411
- end
412
- end
413
-
414
- # @api private
415
- #
416
- # @param key [Symbol, Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
417
- #
418
- # @param spec [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class] .
419
- #
420
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
421
- #
422
- # @option spec [Proc] :enum
423
- #
424
- # @option spec [Proc] :union
425
- #
426
- # @option spec [Boolean] :"nil?"
427
- private def variant(key, spec = nil)
428
- variant_info =
429
- case key
430
- in Symbol
431
- [key, TerminalShop::Converter.type_info(spec)]
432
- in Proc | TerminalShop::Converter | Module | Hash
433
- [nil, TerminalShop::Converter.type_info(key)]
434
- end
435
-
436
- known_variants << variant_info
437
- end
438
-
439
- # @api private
440
- #
441
- # @param value [Object]
442
- #
443
- # @return [TerminalShop::Converter, Class, nil]
444
- private def resolve_variant(value)
445
- case [@discriminator, value]
446
- in [_, TerminalShop::BaseModel]
447
- value.class
448
- in [Symbol, Hash]
449
- key =
450
- if value.key?(@discriminator)
451
- value.fetch(@discriminator)
452
- elsif value.key?((discriminator = @discriminator.to_s))
453
- value.fetch(discriminator)
454
- end
455
-
456
- key = key.to_sym if key.is_a?(String)
457
- _, resolved = known_variants.find { |k,| k == key }
458
- resolved.nil? ? TerminalShop::Unknown : resolved.call
459
- else
460
- nil
461
- end
462
- end
463
-
464
- # rubocop:disable Style/HashEachMethods
465
- # rubocop:disable Style/CaseEquality
466
-
467
- # @param other [Object]
468
- #
469
- # @return [Boolean]
470
- def ===(other)
471
- known_variants.any? do |_, variant_fn|
472
- variant_fn.call === other
473
- end
474
- end
475
-
476
- # @param other [Object]
477
- #
478
- # @return [Boolean]
479
- def ==(other)
480
- other.is_a?(Module) && other.singleton_class.ancestors.include?(TerminalShop::Union) && other.derefed_variants == derefed_variants
481
- end
482
-
483
- # @api private
484
- #
485
- # @param value [Object]
486
- #
487
- # @return [Object]
488
- def coerce(value)
489
- if (variant = resolve_variant(value))
490
- return TerminalShop::Converter.coerce(variant, value)
491
- end
492
-
493
- matches = []
494
-
495
- known_variants.each do |_, variant_fn|
496
- variant = variant_fn.call
497
-
498
- case TerminalShop::Converter.try_strict_coerce(variant, value)
499
- in [true, coerced, _]
500
- return coerced
501
- in [false, true, score]
502
- matches << [score, variant]
503
- in [false, false, _]
504
- nil
505
- end
506
- end
507
-
508
- _, variant = matches.sort! { _2.first <=> _1.first }.find { |score,| !score.zero? }
509
- variant.nil? ? value : TerminalShop::Converter.coerce(variant, value)
510
- end
511
-
512
- # @api private
513
- #
514
- # @param value [Object]
515
- #
516
- # @return [Object]
517
- def dump(value)
518
- if (variant = resolve_variant(value))
519
- return TerminalShop::Converter.dump(variant, value)
520
- end
521
-
522
- known_variants.each do |_, variant_fn|
523
- variant = variant_fn.call
524
- if variant === value
525
- return TerminalShop::Converter.dump(variant, value)
526
- end
527
- end
528
- value
529
- end
530
-
531
- # @api private
532
- #
533
- # @param value [Object]
534
- #
535
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
536
- def try_strict_coerce(value)
537
- # TODO(ruby) this will result in super linear decoding behaviour for nested unions
538
- # follow up with a decoding context that captures current strictness levels
539
- if (variant = resolve_variant(value))
540
- return Converter.try_strict_coerce(variant, value)
541
- end
542
-
543
- coercible = false
544
- max_score = 0
545
-
546
- known_variants.each do |_, variant_fn|
547
- variant = variant_fn.call
548
-
549
- case TerminalShop::Converter.try_strict_coerce(variant, value)
550
- in [true, coerced, score]
551
- return [true, coerced, score]
552
- in [false, true, score]
553
- coercible = true
554
- max_score = [max_score, score].max
555
- in [false, false, _]
556
- nil
557
- end
558
- end
559
-
560
- [false, coercible, max_score]
561
- end
562
-
563
- # rubocop:enable Style/CaseEquality
564
- # rubocop:enable Style/HashEachMethods
565
- end
566
-
567
- # @api private
568
- #
569
- # @abstract
570
- #
571
- # Array of items of a given type.
572
- class ArrayOf
573
- include TerminalShop::Converter
574
-
575
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
576
- #
577
- # @param spec [Hash{Symbol=>Object}] .
578
- #
579
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
580
- #
581
- # @option spec [Proc] :enum
582
- #
583
- # @option spec [Proc] :union
584
- #
585
- # @option spec [Boolean] :"nil?"
586
- def self.[](type_info, spec = {}) = new(type_info, spec)
587
-
588
- # @param other [Object]
589
- #
590
- # @return [Boolean]
591
- def ===(other)
592
- type = item_type
593
- case other
594
- in Array
595
- # rubocop:disable Style/CaseEquality
596
- other.all? { type === _1 }
597
- # rubocop:enable Style/CaseEquality
598
- else
599
- false
600
- end
601
- end
602
-
603
- # @param other [Object]
604
- #
605
- # @return [Boolean]
606
- def ==(other) = other.is_a?(TerminalShop::ArrayOf) && other.item_type == item_type
607
-
608
- # @api private
609
- #
610
- # @param value [Enumerable, Object]
611
- #
612
- # @return [Array<Object>, Object]
613
- def coerce(value)
614
- type = item_type
615
- case value
616
- in Enumerable unless value.is_a?(Hash)
617
- value.map { TerminalShop::Converter.coerce(type, _1) }
618
- else
619
- value
620
- end
621
- end
622
-
623
- # @api private
624
- #
625
- # @param value [Enumerable, Object]
626
- #
627
- # @return [Array<Object>, Object]
628
- def dump(value)
629
- type = item_type
630
- case value
631
- in Enumerable unless value.is_a?(Hash)
632
- value.map { TerminalShop::Converter.dump(type, _1) }.to_a
633
- else
634
- value
635
- end
636
- end
637
-
638
- # @api private
639
- #
640
- # @param value [Object]
641
- #
642
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
643
- def try_strict_coerce(value)
644
- case value
645
- in Array
646
- type = item_type
647
- great_success = true
648
- tally = 0
649
-
650
- mapped =
651
- value.map do |item|
652
- case TerminalShop::Converter.try_strict_coerce(type, item)
653
- in [true, coerced, score]
654
- tally += score
655
- coerced
656
- in [false, true, score]
657
- great_success = false
658
- tally += score
659
- item
660
- in [false, false, _]
661
- great_success &&= item.nil?
662
- item
663
- end
664
- end
665
-
666
- if great_success
667
- [true, mapped, tally]
668
- else
669
- [false, true, tally]
670
- end
671
- else
672
- [false, false, 0]
673
- end
674
- end
675
-
676
- # @api private
677
- #
678
- # @return [TerminalShop::Converter, Class]
679
- protected def item_type = @item_type_fn.call
680
-
681
- # @api private
682
- #
683
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
684
- #
685
- # @param spec [Hash{Symbol=>Object}] .
686
- #
687
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
688
- #
689
- # @option spec [Proc] :enum
690
- #
691
- # @option spec [Proc] :union
692
- #
693
- # @option spec [Boolean] :"nil?"
694
- def initialize(type_info, spec = {})
695
- @item_type_fn = TerminalShop::Converter.type_info(type_info || spec)
696
- end
697
- end
698
-
699
- # @api private
700
- #
701
- # @abstract
702
- #
703
- # Hash of items of a given type.
704
- class HashOf
705
- include TerminalShop::Converter
706
-
707
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
708
- #
709
- # @param spec [Hash{Symbol=>Object}] .
710
- #
711
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
712
- #
713
- # @option spec [Proc] :enum
714
- #
715
- # @option spec [Proc] :union
716
- #
717
- # @option spec [Boolean] :"nil?"
718
- def self.[](type_info, spec = {}) = new(type_info, spec)
719
-
720
- # @param other [Object]
721
- #
722
- # @return [Boolean]
723
- def ===(other)
724
- type = item_type
725
- case other
726
- in Hash
727
- other.all? do |key, val|
728
- case [key, val]
729
- in [Symbol | String, ^type]
730
- true
731
- else
732
- false
733
- end
734
- end
735
- else
736
- false
737
- end
738
- end
739
-
740
- # @param other [Object]
741
- #
742
- # @return [Boolean]
743
- def ==(other) = other.is_a?(TerminalShop::HashOf) && other.item_type == item_type
744
-
745
- # @api private
746
- #
747
- # @param value [Hash{Object=>Object}, Object]
748
- #
749
- # @return [Hash{Symbol=>Object}, Object]
750
- def coerce(value)
751
- type = item_type
752
- case value
753
- in Hash
754
- value.to_h do |key, val|
755
- coerced = TerminalShop::Converter.coerce(type, val)
756
- [key.is_a?(String) ? key.to_sym : key, coerced]
757
- end
758
- else
759
- value
760
- end
761
- end
762
-
763
- # @api private
764
- #
765
- # @param value [Hash{Object=>Object}, Object]
766
- #
767
- # @return [Hash{Symbol=>Object}, Object]
768
- def dump(value)
769
- type = item_type
770
- case value
771
- in Hash
772
- value.transform_values do |val|
773
- TerminalShop::Converter.dump(type, val)
774
- end
775
- else
776
- value
777
- end
778
- end
779
-
780
- # @api private
781
- #
782
- # @param value [Object]
783
- #
784
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
785
- def try_strict_coerce(value)
786
- case value
787
- in Hash
788
- type = item_type
789
- great_success = true
790
- tally = 0
791
-
792
- mapped =
793
- value.transform_values do |val|
794
- case TerminalShop::Converter.try_strict_coerce(type, val)
795
- in [true, coerced, score]
796
- tally += score
797
- coerced
798
- in [false, true, score]
799
- great_success = false
800
- tally += score
801
- val
802
- in [false, false, _]
803
- great_success &&= val.nil?
804
- val
805
- end
806
- end
807
-
808
- if great_success
809
- [true, mapped, tally]
810
- else
811
- [false, true, tally]
812
- end
813
- else
814
- [false, false, 0]
815
- end
816
- end
817
-
818
- # @api private
819
- #
820
- # @return [TerminalShop::Converter, Class]
821
- protected def item_type = @item_type_fn.call
822
-
823
- # @api private
824
- #
825
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
826
- #
827
- # @param spec [Hash{Symbol=>Object}] .
828
- #
829
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
830
- #
831
- # @option spec [Proc] :enum
832
- #
833
- # @option spec [Proc] :union
834
- #
835
- # @option spec [Boolean] :"nil?"
836
- def initialize(type_info, spec = {})
837
- @item_type_fn = TerminalShop::Converter.type_info(type_info || spec)
838
- end
839
- end
840
-
841
- # @abstract
842
- #
843
- # @example
844
- # # `product_api` is a `TerminalShop::Models::ProductAPI`
845
- # product_api => {
846
- # id: id,
847
- # description: description,
848
- # name: name
849
- # }
850
- class BaseModel
851
- extend TerminalShop::Converter
852
-
853
- class << self
854
- # @api private
855
- #
856
- # Assumes superclass fields are totally defined before fields are accessed /
857
- # defined on subclasses.
858
- #
859
- # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
860
- def known_fields
861
- @known_fields ||= (self < TerminalShop::BaseModel ? superclass.known_fields.dup : {})
862
- end
863
-
864
- # @api private
865
- #
866
- # @return [Hash{Symbol=>Symbol}]
867
- def reverse_map
868
- @reverse_map ||= (self < TerminalShop::BaseModel ? superclass.reverse_map.dup : {})
869
- end
870
-
871
- # @api private
872
- #
873
- # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
874
- def fields
875
- known_fields.transform_values do |field|
876
- {**field.except(:type_fn), type: field.fetch(:type_fn).call}
877
- end
878
- end
879
-
880
- # @api private
881
- #
882
- # @return [Hash{Symbol=>Proc}]
883
- def defaults = (@defaults ||= {})
884
-
885
- # @api private
886
- #
887
- # @param name_sym [Symbol]
888
- #
889
- # @param required [Boolean]
890
- #
891
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
892
- #
893
- # @param spec [Hash{Symbol=>Object}] .
894
- #
895
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
896
- #
897
- # @option spec [Proc] :enum
898
- #
899
- # @option spec [Proc] :union
900
- #
901
- # @option spec [Boolean] :"nil?"
902
- private def add_field(name_sym, required:, type_info:, spec:)
903
- type_fn, info =
904
- case type_info
905
- in Proc | Module | TerminalShop::Converter
906
- [TerminalShop::Converter.type_info({**spec, union: type_info}), spec]
907
- in Hash
908
- [TerminalShop::Converter.type_info(type_info), type_info]
909
- end
910
-
911
- fallback = info[:const]
912
- defaults[name_sym] = fallback if required && !info[:nil?] && info.key?(:const)
913
-
914
- key = info[:api_name]&.tap { reverse_map[_1] = name_sym } || name_sym
915
- setter = "#{name_sym}="
916
-
917
- if known_fields.key?(name_sym)
918
- [name_sym, setter].each { undef_method(_1) }
919
- end
920
-
921
- known_fields[name_sym] = {mode: @mode, key: key, required: required, type_fn: type_fn}
922
-
923
- define_method(setter) do |val|
924
- @data[key] = val
925
- end
926
-
927
- define_method(name_sym) do
928
- field_type = type_fn.call
929
- value = @data.fetch(key) { self.class.defaults[key] }
930
- TerminalShop::Converter.coerce(field_type, value)
931
- rescue StandardError
932
- name = self.class.name.split("::").last
933
- raise TerminalShop::ConversionError.new(
934
- "Failed to parse #{name}.#{name_sym} as #{field_type.inspect}. " \
935
- "To get the unparsed API response, use #{name}[:#{key}]."
936
- )
937
- end
938
- end
939
-
940
- # @api private
941
- #
942
- # @param name_sym [Symbol]
943
- #
944
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
945
- #
946
- # @param spec [Hash{Symbol=>Object}] .
947
- #
948
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
949
- #
950
- # @option spec [Proc] :enum
951
- #
952
- # @option spec [Proc] :union
953
- #
954
- # @option spec [Boolean] :"nil?"
955
- def required(name_sym, type_info, spec = {})
956
- add_field(name_sym, required: true, type_info: type_info, spec: spec)
957
- end
958
-
959
- # @api private
960
- #
961
- # @param name_sym [Symbol]
962
- #
963
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
964
- #
965
- # @param spec [Hash{Symbol=>Object}] .
966
- #
967
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
968
- #
969
- # @option spec [Proc] :enum
970
- #
971
- # @option spec [Proc] :union
972
- #
973
- # @option spec [Boolean] :"nil?"
974
- def optional(name_sym, type_info, spec = {})
975
- add_field(name_sym, required: false, type_info: type_info, spec: spec)
976
- end
977
-
978
- # @api private
979
- #
980
- # `request_only` attributes not excluded from `.#coerce` when receiving responses
981
- # even if well behaved servers should not send them
982
- #
983
- # @param blk [Proc]
984
- private def request_only(&blk)
985
- @mode = :dump
986
- blk.call
987
- ensure
988
- @mode = nil
989
- end
990
-
991
- # @api private
992
- #
993
- # `response_only` attributes are omitted from `.#dump` when making requests
994
- #
995
- # @param blk [Proc]
996
- private def response_only(&blk)
997
- @mode = :coerce
998
- blk.call
999
- ensure
1000
- @mode = nil
1001
- end
1002
- end
1003
-
1004
- # @param other [Object]
1005
- #
1006
- # @return [Boolean]
1007
- def ==(other)
1008
- case other
1009
- in TerminalShop::BaseModel
1010
- self.class.fields == other.class.fields && @data == other.to_h
1011
- else
1012
- false
1013
- end
1014
- end
1015
-
1016
- class << self
1017
- # @api private
1018
- #
1019
- # @param value [TerminalShop::BaseModel, Hash{Object=>Object}, Object]
1020
- #
1021
- # @return [TerminalShop::BaseModel, Object]
1022
- def coerce(value)
1023
- case TerminalShop::Util.coerce_hash(value)
1024
- in Hash => coerced
1025
- new(coerced)
1026
- else
1027
- value
1028
- end
1029
- end
1030
-
1031
- # @api private
1032
- #
1033
- # @param value [TerminalShop::BaseModel, Object]
1034
- #
1035
- # @return [Hash{Object=>Object}, Object]
1036
- def dump(value)
1037
- unless (coerced = TerminalShop::Util.coerce_hash(value)).is_a?(Hash)
1038
- return value
1039
- end
1040
-
1041
- values = coerced.filter_map do |key, val|
1042
- name = key.to_sym
1043
- case (field = known_fields[name])
1044
- in nil
1045
- [name, val]
1046
- else
1047
- mode, type_fn, api_name = field.fetch_values(:mode, :type_fn, :key)
1048
- case mode
1049
- in :coerce
1050
- next
1051
- else
1052
- target = type_fn.call
1053
- [api_name, TerminalShop::Converter.dump(target, val)]
1054
- end
1055
- end
1056
- end.to_h
1057
-
1058
- defaults.each do |key, val|
1059
- next if values.key?(key)
1060
-
1061
- values[key] = val
1062
- end
1063
-
1064
- values
1065
- end
1066
-
1067
- # @api private
1068
- #
1069
- # @param value [Object]
1070
- #
1071
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
1072
- def try_strict_coerce(value)
1073
- case value
1074
- in Hash | TerminalShop::BaseModel
1075
- value = value.to_h
1076
- else
1077
- return [false, false, 0]
1078
- end
1079
-
1080
- keys = value.keys.to_set
1081
- great_success = true
1082
- tally = 0
1083
- acc = {}
1084
-
1085
- known_fields.each_value do |field|
1086
- mode, required, type_fn, api_name = field.fetch_values(:mode, :required, :type_fn, :key)
1087
- keys.delete(api_name)
1088
-
1089
- case [required && mode != :dump, value.key?(api_name)]
1090
- in [_, true]
1091
- target = type_fn.call
1092
- item = value.fetch(api_name)
1093
- case TerminalShop::Converter.try_strict_coerce(target, item)
1094
- in [true, coerced, score]
1095
- tally += score
1096
- acc[api_name] = coerced
1097
- in [false, true, score]
1098
- great_success = false
1099
- tally += score
1100
- acc[api_name] = item
1101
- in [false, false, _]
1102
- great_success &&= item.nil?
1103
- end
1104
- in [true, false]
1105
- great_success = false
1106
- in [false, false]
1107
- nil
1108
- end
1109
- end
1110
-
1111
- keys.each do |key|
1112
- acc[key] = value.fetch(key)
1113
- end
1114
-
1115
- great_success ? [true, new(acc), tally] : [false, true, tally]
1116
- end
1117
- end
1118
-
1119
- # Returns the raw value associated with the given key, if found. Otherwise, nil is
1120
- # returned.
1121
- #
1122
- # It is valid to lookup keys that are not in the API spec, for example to access
1123
- # undocumented features. This method does not parse response data into
1124
- # higher-level types. Lookup by anything other than a Symbol is an ArgumentError.
1125
- #
1126
- # @param key [Symbol]
1127
- #
1128
- # @return [Object, nil]
1129
- def [](key)
1130
- unless key.instance_of?(Symbol)
1131
- raise ArgumentError.new("Expected symbol key for lookup, got #{key.inspect}")
1132
- end
1133
-
1134
- @data[key]
1135
- end
1136
-
1137
- # Returns a Hash of the data underlying this object. O(1)
1138
- #
1139
- # Keys are Symbols and values are the raw values from the response. The return
1140
- # value indicates which values were ever set on the object. i.e. there will be a
1141
- # key in this hash if they ever were, even if the set value was nil.
1142
- #
1143
- # This method is not recursive. The returned value is shared by the object, so it
1144
- # should not be mutated.
1145
- #
1146
- # @return [Hash{Symbol=>Object}]
1147
- def to_h = @data
1148
-
1149
- alias_method :to_hash, :to_h
1150
-
1151
- # @param keys [Array<Symbol>, nil]
1152
- #
1153
- # @return [Hash{Symbol=>Object}]
1154
- def deconstruct_keys(keys)
1155
- (keys || self.class.known_fields.keys).filter_map do |k|
1156
- unless self.class.known_fields.key?(k)
1157
- next
1158
- end
1159
-
1160
- [k, method(k).call]
1161
- end
1162
- .to_h
1163
- end
1164
-
1165
- # Create a new instance of a model.
1166
- #
1167
- # @param data [Hash{Symbol=>Object}, TerminalShop::BaseModel]
1168
- def initialize(data = {})
1169
- case TerminalShop::Util.coerce_hash(data)
1170
- in Hash => coerced
1171
- @data = coerced.to_h do |key, value|
1172
- name = key.to_sym
1173
- mapped = self.class.reverse_map.fetch(name, name)
1174
- type = self.class.fields[mapped]&.fetch(:type)
1175
- stored =
1176
- case [type, value]
1177
- in [Module, Hash] if type <= TerminalShop::BaseModel
1178
- type.new(value)
1179
- in [TerminalShop::ArrayOf, Array] | [TerminalShop::HashOf, Hash]
1180
- type.coerce(value)
1181
- else
1182
- value
1183
- end
1184
- [name, stored]
1185
- end
1186
- else
1187
- raise ArgumentError.new("Expected a #{Hash} or #{TerminalShop::BaseModel}, got #{data.inspect}")
1188
- end
1189
- end
1190
-
1191
- # @return [String]
1192
- def to_s = @data.to_s
1193
-
1194
- # @return [String]
1195
- def inspect
1196
- "#<#{self.class.name}:0x#{object_id.to_s(16)} #{deconstruct_keys(nil).map do |k, v|
1197
- "#{k}=#{v.inspect}"
1198
- end.join(' ')}>"
1199
- end
1200
- end
1201
- end