terminal-shop 0.1.0.pre.alpha.15 → 1.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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -19
  3. data/lib/terminal-shop/base_client.rb +35 -55
  4. data/lib/terminal-shop/base_model.rb +457 -438
  5. data/lib/terminal-shop/base_page.rb +2 -2
  6. data/lib/terminal-shop/client.rb +1 -1
  7. data/lib/terminal-shop/errors.rb +3 -2
  8. data/lib/terminal-shop/models/address_get_params.rb +18 -0
  9. data/lib/terminal-shop/models/address_get_response.rb +20 -0
  10. data/lib/terminal-shop/models/app_create_params.rb +14 -2
  11. data/lib/terminal-shop/models/card_get_params.rb +18 -0
  12. data/lib/terminal-shop/models/card_get_response.rb +20 -0
  13. data/lib/terminal-shop/models/cart.rb +35 -2
  14. data/lib/terminal-shop/models/cart_convert_params.rb +11 -1
  15. data/lib/terminal-shop/models/cart_redeem_gift_card_params.rb +24 -0
  16. data/lib/terminal-shop/models/cart_redeem_gift_card_response.rb +48 -0
  17. data/lib/terminal-shop/models/cart_remove_gift_card_params.rb +18 -0
  18. data/lib/terminal-shop/models/cart_remove_gift_card_response.rb +19 -0
  19. data/lib/terminal-shop/models/product.rb +72 -46
  20. data/lib/terminal-shop/models/product_get_params.rb +18 -0
  21. data/lib/terminal-shop/models/product_get_response.rb +20 -0
  22. data/lib/terminal-shop/models/profile_update_params.rb +7 -9
  23. data/lib/terminal-shop/models/subscription.rb +0 -10
  24. data/lib/terminal-shop/models/subscription_get_params.rb +18 -0
  25. data/lib/terminal-shop/models/subscription_get_response.rb +20 -0
  26. data/lib/terminal-shop/pooled_net_requester.rb +14 -9
  27. data/lib/terminal-shop/resources/address.rb +19 -0
  28. data/lib/terminal-shop/resources/app.rb +2 -6
  29. data/lib/terminal-shop/resources/card.rb +19 -0
  30. data/lib/terminal-shop/resources/cart.rb +43 -1
  31. data/lib/terminal-shop/resources/product.rb +19 -0
  32. data/lib/terminal-shop/resources/profile.rb +3 -3
  33. data/lib/terminal-shop/resources/subscription.rb +19 -0
  34. data/lib/terminal-shop/util.rb +135 -1
  35. data/lib/terminal-shop/version.rb +1 -1
  36. data/lib/terminal-shop.rb +13 -1
  37. data/rbi/lib/terminal-shop/base_client.rbi +29 -35
  38. data/rbi/lib/terminal-shop/base_model.rbi +235 -217
  39. data/rbi/lib/terminal-shop/base_page.rbi +2 -2
  40. data/rbi/lib/terminal-shop/client.rbi +2 -2
  41. data/rbi/lib/terminal-shop/errors.rbi +11 -10
  42. data/rbi/lib/terminal-shop/models/address.rbi +2 -2
  43. data/rbi/lib/terminal-shop/models/address_create_params.rbi +3 -13
  44. data/rbi/lib/terminal-shop/models/address_create_response.rbi +2 -2
  45. data/rbi/lib/terminal-shop/models/address_delete_params.rbi +5 -2
  46. data/rbi/lib/terminal-shop/models/address_delete_response.rbi +2 -2
  47. data/rbi/lib/terminal-shop/models/address_get_params.rbi +21 -0
  48. data/rbi/lib/terminal-shop/models/address_get_response.rbi +23 -0
  49. data/rbi/lib/terminal-shop/models/address_list_params.rbi +5 -2
  50. data/rbi/lib/terminal-shop/models/address_list_response.rbi +2 -2
  51. data/rbi/lib/terminal-shop/models/app.rbi +2 -2
  52. data/rbi/lib/terminal-shop/models/app_create_params.rbi +29 -4
  53. data/rbi/lib/terminal-shop/models/app_create_response.rbi +4 -4
  54. data/rbi/lib/terminal-shop/models/app_delete_params.rbi +5 -2
  55. data/rbi/lib/terminal-shop/models/app_delete_response.rbi +2 -2
  56. data/rbi/lib/terminal-shop/models/app_get_params.rbi +5 -2
  57. data/rbi/lib/terminal-shop/models/app_get_response.rbi +2 -2
  58. data/rbi/lib/terminal-shop/models/app_list_params.rbi +5 -2
  59. data/rbi/lib/terminal-shop/models/app_list_response.rbi +2 -2
  60. data/rbi/lib/terminal-shop/models/card.rbi +4 -4
  61. data/rbi/lib/terminal-shop/models/card_collect_params.rbi +5 -2
  62. data/rbi/lib/terminal-shop/models/card_collect_response.rbi +4 -4
  63. data/rbi/lib/terminal-shop/models/card_create_params.rbi +2 -2
  64. data/rbi/lib/terminal-shop/models/card_create_response.rbi +2 -2
  65. data/rbi/lib/terminal-shop/models/card_delete_params.rbi +5 -2
  66. data/rbi/lib/terminal-shop/models/card_delete_response.rbi +2 -2
  67. data/rbi/lib/terminal-shop/models/card_get_params.rbi +21 -0
  68. data/rbi/lib/terminal-shop/models/card_get_response.rbi +23 -0
  69. data/rbi/lib/terminal-shop/models/card_list_params.rbi +5 -2
  70. data/rbi/lib/terminal-shop/models/card_list_response.rbi +2 -2
  71. data/rbi/lib/terminal-shop/models/cart.rbi +45 -9
  72. data/rbi/lib/terminal-shop/models/cart_convert_params.rbi +17 -3
  73. data/rbi/lib/terminal-shop/models/cart_convert_response.rbi +2 -2
  74. data/rbi/lib/terminal-shop/models/cart_get_params.rbi +5 -2
  75. data/rbi/lib/terminal-shop/models/cart_get_response.rbi +2 -2
  76. data/rbi/lib/terminal-shop/models/cart_redeem_gift_card_params.rbi +32 -0
  77. data/rbi/lib/terminal-shop/models/cart_redeem_gift_card_response.rbi +66 -0
  78. data/rbi/lib/terminal-shop/models/cart_remove_gift_card_params.rbi +21 -0
  79. data/rbi/lib/terminal-shop/models/cart_remove_gift_card_response.rbi +23 -0
  80. data/rbi/lib/terminal-shop/models/cart_set_address_params.rbi +2 -2
  81. data/rbi/lib/terminal-shop/models/cart_set_address_response.rbi +2 -2
  82. data/rbi/lib/terminal-shop/models/cart_set_card_params.rbi +2 -2
  83. data/rbi/lib/terminal-shop/models/cart_set_card_response.rbi +2 -2
  84. data/rbi/lib/terminal-shop/models/cart_set_item_params.rbi +2 -2
  85. data/rbi/lib/terminal-shop/models/cart_set_item_response.rbi +2 -2
  86. data/rbi/lib/terminal-shop/models/email_create_params.rbi +2 -2
  87. data/rbi/lib/terminal-shop/models/email_create_response.rbi +2 -2
  88. data/rbi/lib/terminal-shop/models/order.rbi +10 -10
  89. data/rbi/lib/terminal-shop/models/order_create_params.rbi +2 -2
  90. data/rbi/lib/terminal-shop/models/order_create_response.rbi +2 -2
  91. data/rbi/lib/terminal-shop/models/order_get_params.rbi +5 -2
  92. data/rbi/lib/terminal-shop/models/order_get_response.rbi +2 -2
  93. data/rbi/lib/terminal-shop/models/order_list_params.rbi +5 -2
  94. data/rbi/lib/terminal-shop/models/order_list_response.rbi +2 -2
  95. data/rbi/lib/terminal-shop/models/product.rbi +88 -27
  96. data/rbi/lib/terminal-shop/models/product_get_params.rbi +21 -0
  97. data/rbi/lib/terminal-shop/models/product_get_response.rbi +23 -0
  98. data/rbi/lib/terminal-shop/models/product_list_params.rbi +5 -2
  99. data/rbi/lib/terminal-shop/models/product_list_response.rbi +2 -2
  100. data/rbi/lib/terminal-shop/models/product_variant.rbi +2 -2
  101. data/rbi/lib/terminal-shop/models/profile.rbi +4 -4
  102. data/rbi/lib/terminal-shop/models/profile_me_params.rbi +5 -2
  103. data/rbi/lib/terminal-shop/models/profile_me_response.rbi +2 -2
  104. data/rbi/lib/terminal-shop/models/profile_update_params.rbi +9 -14
  105. data/rbi/lib/terminal-shop/models/profile_update_response.rbi +2 -2
  106. data/rbi/lib/terminal-shop/models/subscription.rbi +15 -13
  107. data/rbi/lib/terminal-shop/models/subscription_create_params.rbi +5 -2
  108. data/rbi/lib/terminal-shop/models/subscription_create_response.rbi +2 -2
  109. data/rbi/lib/terminal-shop/models/subscription_delete_params.rbi +5 -2
  110. data/rbi/lib/terminal-shop/models/subscription_delete_response.rbi +2 -2
  111. data/rbi/lib/terminal-shop/models/subscription_get_params.rbi +21 -0
  112. data/rbi/lib/terminal-shop/models/subscription_get_response.rbi +23 -0
  113. data/rbi/lib/terminal-shop/models/subscription_list_params.rbi +5 -2
  114. data/rbi/lib/terminal-shop/models/subscription_list_response.rbi +2 -2
  115. data/rbi/lib/terminal-shop/models/token.rbi +2 -2
  116. data/rbi/lib/terminal-shop/models/token_create_params.rbi +5 -2
  117. data/rbi/lib/terminal-shop/models/token_create_response.rbi +4 -4
  118. data/rbi/lib/terminal-shop/models/token_delete_params.rbi +5 -2
  119. data/rbi/lib/terminal-shop/models/token_delete_response.rbi +2 -2
  120. data/rbi/lib/terminal-shop/models/token_get_params.rbi +5 -2
  121. data/rbi/lib/terminal-shop/models/token_get_response.rbi +2 -2
  122. data/rbi/lib/terminal-shop/models/token_list_params.rbi +5 -2
  123. data/rbi/lib/terminal-shop/models/token_list_response.rbi +2 -2
  124. data/rbi/lib/terminal-shop/models/view_init_params.rbi +5 -2
  125. data/rbi/lib/terminal-shop/models/view_init_response.rbi +5 -15
  126. data/rbi/lib/terminal-shop/pooled_net_requester.rbi +12 -10
  127. data/rbi/lib/terminal-shop/request_options.rbi +4 -0
  128. data/rbi/lib/terminal-shop/resources/address.rbi +12 -2
  129. data/rbi/lib/terminal-shop/resources/app.rbi +3 -5
  130. data/rbi/lib/terminal-shop/resources/card.rbi +12 -2
  131. data/rbi/lib/terminal-shop/resources/cart.rbi +24 -4
  132. data/rbi/lib/terminal-shop/resources/email.rbi +2 -2
  133. data/rbi/lib/terminal-shop/resources/order.rbi +2 -2
  134. data/rbi/lib/terminal-shop/resources/product.rbi +12 -2
  135. data/rbi/lib/terminal-shop/resources/profile.rbi +5 -5
  136. data/rbi/lib/terminal-shop/resources/subscription.rbi +12 -2
  137. data/rbi/lib/terminal-shop/resources/token.rbi +2 -2
  138. data/rbi/lib/terminal-shop/resources/view.rbi +2 -2
  139. data/rbi/lib/terminal-shop/util.rbi +153 -104
  140. data/rbi/lib/terminal-shop/version.rbi +1 -1
  141. data/sig/terminal-shop/base_client.rbs +3 -7
  142. data/sig/terminal-shop/base_page.rbs +1 -1
  143. data/sig/terminal-shop/errors.rbs +2 -1
  144. data/sig/terminal-shop/models/address_get_params.rbs +19 -0
  145. data/sig/terminal-shop/models/address_get_response.rbs +18 -0
  146. data/sig/terminal-shop/models/app_create_params.rbs +12 -3
  147. data/sig/terminal-shop/models/card_get_params.rbs +18 -0
  148. data/sig/terminal-shop/models/card_get_response.rbs +18 -0
  149. data/sig/terminal-shop/models/cart.rbs +27 -2
  150. data/sig/terminal-shop/models/cart_convert_params.rbs +10 -2
  151. data/sig/terminal-shop/models/cart_redeem_gift_card_params.rbs +25 -0
  152. data/sig/terminal-shop/models/cart_redeem_gift_card_response.rbs +47 -0
  153. data/sig/terminal-shop/models/cart_remove_gift_card_params.rbs +19 -0
  154. data/sig/terminal-shop/models/cart_remove_gift_card_response.rbs +18 -0
  155. data/sig/terminal-shop/models/product.rbs +58 -17
  156. data/sig/terminal-shop/models/product_get_params.rbs +19 -0
  157. data/sig/terminal-shop/models/product_get_response.rbs +18 -0
  158. data/sig/terminal-shop/models/profile_update_params.rbs +5 -5
  159. data/sig/terminal-shop/models/subscription_get_params.rbs +19 -0
  160. data/sig/terminal-shop/models/subscription_get_response.rbs +19 -0
  161. data/sig/terminal-shop/request_options.rbs +4 -0
  162. data/sig/terminal-shop/resources/address.rbs +10 -0
  163. data/sig/terminal-shop/resources/app.rbs +0 -2
  164. data/sig/terminal-shop/resources/card.rbs +10 -0
  165. data/sig/terminal-shop/resources/cart.rbs +20 -0
  166. data/sig/terminal-shop/resources/product.rbs +10 -0
  167. data/sig/terminal-shop/resources/profile.rbs +3 -4
  168. data/sig/terminal-shop/resources/subscription.rbs +11 -0
  169. data/sig/terminal-shop/util.rbs +19 -0
  170. data/sig/terminal-shop/version.rbs +1 -1
  171. metadata +40 -4
@@ -155,7 +155,7 @@ module TerminalShop
155
155
  in [-> { _1 <= Date || _1 <= Time }, String]
156
156
  Kernel.then do
157
157
  [true, target.parse(value), 1]
158
- rescue ArgumentError, Date::Error
158
+ rescue ArgumentError
159
159
  [false, false, 0]
160
160
  end
161
161
  in [_, ^target]
@@ -192,33 +192,35 @@ module TerminalShop
192
192
  #
193
193
  def self.==(other) = other.is_a?(Class) && other <= TerminalShop::Unknown
194
194
 
195
- # @!parse
196
- # # @private
197
- # #
198
- # # @param value [Object]
199
- # #
200
- # # @return [Object]
201
- # #
202
- # def self.coerce(value) = super
203
-
204
- # @!parse
205
- # # @private
206
- # #
207
- # # @param value [Object]
208
- # #
209
- # # @return [Object]
210
- # #
211
- # def self.dump(value) = super
195
+ class << self
196
+ # @!parse
197
+ # # @private
198
+ # #
199
+ # # @param value [Object]
200
+ # #
201
+ # # @return [Object]
202
+ # #
203
+ # def coerce(value) = super
204
+
205
+ # @!parse
206
+ # # @private
207
+ # #
208
+ # # @param value [Object]
209
+ # #
210
+ # # @return [Object]
211
+ # #
212
+ # def dump(value) = super
212
213
 
213
- # @private
214
- #
215
- # @param value [Object]
216
- #
217
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
218
- #
219
- def self.try_strict_coerce(value)
220
- # prevent unknown variant from being chosen during the first coercion pass
221
- [false, true, 0]
214
+ # @private
215
+ #
216
+ # @param value [Object]
217
+ #
218
+ # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
219
+ #
220
+ def try_strict_coerce(value)
221
+ # prevent unknown variant from being chosen during the first coercion pass
222
+ [false, true, 0]
223
+ end
222
224
  end
223
225
 
224
226
  # rubocop:enable Lint/UnusedMethodArgument
@@ -246,36 +248,38 @@ module TerminalShop
246
248
  #
247
249
  def self.==(other) = other.is_a?(Class) && other <= TerminalShop::BooleanModel
248
250
 
249
- # @!parse
250
- # # @private
251
- # #
252
- # # @param value [Boolean, Object]
253
- # #
254
- # # @return [Boolean, Object]
255
- # #
256
- # def self.coerce(value) = super
257
-
258
- # @!parse
259
- # # @private
260
- # #
261
- # # @param value [Boolean, Object]
262
- # #
263
- # # @return [Boolean, Object]
264
- # #
265
- # def self.dump(value) = super
251
+ class << self
252
+ # @!parse
253
+ # # @private
254
+ # #
255
+ # # @param value [Boolean, Object]
256
+ # #
257
+ # # @return [Boolean, Object]
258
+ # #
259
+ # def coerce(value) = super
260
+
261
+ # @!parse
262
+ # # @private
263
+ # #
264
+ # # @param value [Boolean, Object]
265
+ # #
266
+ # # @return [Boolean, Object]
267
+ # #
268
+ # def dump(value) = super
266
269
 
267
- # @private
268
- #
269
- # @param value [Object]
270
- #
271
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
272
- #
273
- def self.try_strict_coerce(value)
274
- case value
275
- in true | false
276
- [true, value, 1]
277
- else
278
- [false, false, 0]
270
+ # @private
271
+ #
272
+ # @param value [Object]
273
+ #
274
+ # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
275
+ #
276
+ def try_strict_coerce(value)
277
+ case value
278
+ in true | false
279
+ [true, value, 1]
280
+ else
281
+ [false, false, 0]
282
+ end
279
283
  end
280
284
  end
281
285
  end
@@ -297,17 +301,19 @@ module TerminalShop
297
301
  class Enum
298
302
  extend TerminalShop::Converter
299
303
 
300
- # All of the valid Symbol values for this enum.
301
- #
302
- # @return [Array<NilClass, Boolean, Integer, Float, Symbol>]
303
- #
304
- def self.values = (@values ||= constants.map { const_get(_1) })
304
+ class << self
305
+ # All of the valid Symbol values for this enum.
306
+ #
307
+ # @return [Array<NilClass, Boolean, Integer, Float, Symbol>]
308
+ #
309
+ def values = (@values ||= constants.map { const_get(_1) })
305
310
 
306
- # @private
307
- #
308
- # Guard against thread safety issues by instantiating `@values`.
309
- #
310
- private_class_method def self.finalize! = values
311
+ # @private
312
+ #
313
+ # Guard against thread safety issues by instantiating `@values`.
314
+ #
315
+ private def finalize! = values
316
+ end
311
317
 
312
318
  private_class_method :new
313
319
 
@@ -325,41 +331,43 @@ module TerminalShop
325
331
  other.is_a?(Class) && other <= TerminalShop::Enum && other.values.to_set == values.to_set
326
332
  end
327
333
 
328
- # @private
329
- #
330
- # @param value [String, Symbol, Object]
331
- #
332
- # @return [Symbol, Object]
333
- #
334
- def self.coerce(value) = (value.is_a?(String) ? value.to_sym : value)
334
+ class << self
335
+ # @private
336
+ #
337
+ # @param value [String, Symbol, Object]
338
+ #
339
+ # @return [Symbol, Object]
340
+ #
341
+ def coerce(value) = (value.is_a?(String) ? value.to_sym : value)
335
342
 
336
- # @!parse
337
- # # @private
338
- # #
339
- # # @param value [Symbol, Object]
340
- # #
341
- # # @return [Symbol, Object]
342
- # #
343
- # def self.dump(value) = super
343
+ # @!parse
344
+ # # @private
345
+ # #
346
+ # # @param value [Symbol, Object]
347
+ # #
348
+ # # @return [Symbol, Object]
349
+ # #
350
+ # def dump(value) = super
344
351
 
345
- # @private
346
- #
347
- # @param value [Object]
348
- #
349
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
350
- #
351
- def self.try_strict_coerce(value)
352
- return [true, value, 1] if values.include?(value)
352
+ # @private
353
+ #
354
+ # @param value [Object]
355
+ #
356
+ # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
357
+ #
358
+ def try_strict_coerce(value)
359
+ return [true, value, 1] if values.include?(value)
353
360
 
354
- case value
355
- in String if values.include?(val = value.to_sym)
356
- [true, val, 1]
357
- else
358
- case [value, values.first]
359
- in [true | false, true | false] | [Integer, Integer] | [Symbol | String, Symbol]
360
- [false, true, 0]
361
+ case value
362
+ in String if values.include?(val = value.to_sym)
363
+ [true, val, 1]
361
364
  else
362
- [false, false, 0]
365
+ case [value, values.first]
366
+ in [true | false, true | false] | [Integer, Integer] | [Symbol | String, Symbol]
367
+ [false, true, 0]
368
+ else
369
+ [false, false, 0]
370
+ end
363
371
  end
364
372
  end
365
373
  end
@@ -372,15 +380,15 @@ module TerminalShop
372
380
  class Union
373
381
  extend TerminalShop::Converter
374
382
 
375
- # @private
376
- #
377
- # All of the specified variant info for this union.
378
- #
379
- # @return [Array<Array(Symbol, Proc)>]
380
- #
381
- private_class_method def self.known_variants = (@known_variants ||= [])
382
-
383
383
  class << self
384
+ # @private
385
+ #
386
+ # All of the specified variant info for this union.
387
+ #
388
+ # @return [Array<Array(Symbol, Proc)>]
389
+ #
390
+ private def known_variants = (@known_variants ||= [])
391
+
384
392
  # @private
385
393
  #
386
394
  # All of the specified variants for this union.
@@ -390,68 +398,68 @@ module TerminalShop
390
398
  protected def variants
391
399
  @known_variants.map { |key, variant_fn| [key, variant_fn.call] }
392
400
  end
393
- end
394
401
 
395
- # @private
396
- #
397
- # @param property [Symbol]
398
- #
399
- private_class_method def self.discriminator(property)
400
- case property
401
- in Symbol
402
- @discriminator = property
403
- end
404
- end
405
-
406
- # @private
407
- #
408
- # @param key [Symbol, Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
409
- #
410
- # @param spec [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class] .
411
- #
412
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
413
- #
414
- # @option spec [Proc] :enum
415
- #
416
- # @option spec [Proc] :union
417
- #
418
- # @option spec [Boolean] :"nil?"
419
- #
420
- private_class_method def self.variant(key, spec = nil)
421
- variant_info =
422
- case key
402
+ # @private
403
+ #
404
+ # @param property [Symbol]
405
+ #
406
+ private def discriminator(property)
407
+ case property
423
408
  in Symbol
424
- [key, TerminalShop::Converter.type_info(spec)]
425
- in Proc | TerminalShop::Converter | Class | Hash
426
- [nil, TerminalShop::Converter.type_info(key)]
409
+ @discriminator = property
427
410
  end
411
+ end
428
412
 
429
- known_variants << variant_info
430
- end
431
-
432
- # @private
433
- #
434
- # @param value [Object]
435
- #
436
- # @return [TerminalShop::Converter, Class, nil]
437
- #
438
- private_class_method def self.resolve_variant(value)
439
- case [@discriminator, value]
440
- in [_, TerminalShop::BaseModel]
441
- value.class
442
- in [Symbol, Hash]
443
- key =
444
- if value.key?(@discriminator)
445
- value.fetch(@discriminator)
446
- elsif value.key?((discriminator = @discriminator.to_s))
447
- value.fetch(discriminator)
413
+ # @private
414
+ #
415
+ # @param key [Symbol, Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
416
+ #
417
+ # @param spec [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class] .
418
+ #
419
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
420
+ #
421
+ # @option spec [Proc] :enum
422
+ #
423
+ # @option spec [Proc] :union
424
+ #
425
+ # @option spec [Boolean] :"nil?"
426
+ #
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 | Class | Hash
433
+ [nil, TerminalShop::Converter.type_info(key)]
448
434
  end
449
435
 
450
- key = key.to_sym if key.is_a?(String)
451
- _, resolved = known_variants.find { |k,| k == key }
452
- resolved.nil? ? TerminalShop::Unknown : resolved.call
453
- else
454
- nil
436
+ known_variants << variant_info
437
+ end
438
+
439
+ # @private
440
+ #
441
+ # @param value [Object]
442
+ #
443
+ # @return [TerminalShop::Converter, Class, nil]
444
+ #
445
+ private def resolve_variant(value)
446
+ case [@discriminator, value]
447
+ in [_, TerminalShop::BaseModel]
448
+ value.class
449
+ in [Symbol, Hash]
450
+ key =
451
+ if value.key?(@discriminator)
452
+ value.fetch(@discriminator)
453
+ elsif value.key?((discriminator = @discriminator.to_s))
454
+ value.fetch(discriminator)
455
+ end
456
+
457
+ key = key.to_sym if key.is_a?(String)
458
+ _, resolved = known_variants.find { |k,| k == key }
459
+ resolved.nil? ? TerminalShop::Unknown : resolved.call
460
+ else
461
+ nil
462
+ end
455
463
  end
456
464
  end
457
465
 
@@ -478,87 +486,89 @@ module TerminalShop
478
486
  other.is_a?(Class) && other <= TerminalShop::Union && other.variants == variants
479
487
  end
480
488
 
481
- # @private
482
- #
483
- # @param value [Object]
484
- #
485
- # @return [Object]
486
- #
487
- def self.coerce(value)
488
- if (variant = resolve_variant(value))
489
- return TerminalShop::Converter.coerce(variant, value)
490
- end
489
+ class << self
490
+ # @private
491
+ #
492
+ # @param value [Object]
493
+ #
494
+ # @return [Object]
495
+ #
496
+ def coerce(value)
497
+ if (variant = resolve_variant(value))
498
+ return TerminalShop::Converter.coerce(variant, value)
499
+ end
491
500
 
492
- matches = []
501
+ matches = []
493
502
 
494
- known_variants.each do |_, variant_fn|
495
- variant = variant_fn.call
503
+ known_variants.each do |_, variant_fn|
504
+ variant = variant_fn.call
496
505
 
497
- case TerminalShop::Converter.try_strict_coerce(variant, value)
498
- in [true, coerced, _]
499
- return coerced
500
- in [false, true, score]
501
- matches << [score, variant]
502
- in [false, false, _]
503
- nil
506
+ case TerminalShop::Converter.try_strict_coerce(variant, value)
507
+ in [true, coerced, _]
508
+ return coerced
509
+ in [false, true, score]
510
+ matches << [score, variant]
511
+ in [false, false, _]
512
+ nil
513
+ end
504
514
  end
505
- end
506
-
507
- _, variant = matches.sort! { _2.first <=> _1.first }.find { |score,| !score.zero? }
508
- variant.nil? ? value : TerminalShop::Converter.coerce(variant, value)
509
- end
510
515
 
511
- # @private
512
- #
513
- # @param value [Object]
514
- #
515
- # @return [Object]
516
- #
517
- def self.dump(value)
518
- if (variant = resolve_variant(value))
519
- return TerminalShop::Converter.dump(variant, value)
516
+ _, variant = matches.sort! { _2.first <=> _1.first }.find { |score,| !score.zero? }
517
+ variant.nil? ? value : TerminalShop::Converter.coerce(variant, value)
520
518
  end
521
519
 
522
- known_variants.each do |_, variant_fn|
523
- variant = variant_fn.call
524
- if variant === value
520
+ # @private
521
+ #
522
+ # @param value [Object]
523
+ #
524
+ # @return [Object]
525
+ #
526
+ def dump(value)
527
+ if (variant = resolve_variant(value))
525
528
  return TerminalShop::Converter.dump(variant, value)
526
529
  end
527
- end
528
- value
529
- end
530
530
 
531
- # @private
532
- #
533
- # @param value [Object]
534
- #
535
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
536
- #
537
- def self.try_strict_coerce(value)
538
- # TODO(ruby) this will result in super linear decoding behaviour for nested unions
539
- # follow up with a decoding context that captures current strictness levels
540
- if (variant = resolve_variant(value))
541
- return Converter.try_strict_coerce(variant, value)
531
+ known_variants.each do |_, variant_fn|
532
+ variant = variant_fn.call
533
+ if variant === value
534
+ return TerminalShop::Converter.dump(variant, value)
535
+ end
536
+ end
537
+ value
542
538
  end
543
539
 
544
- coercible = false
545
- max_score = 0
540
+ # @private
541
+ #
542
+ # @param value [Object]
543
+ #
544
+ # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
545
+ #
546
+ def try_strict_coerce(value)
547
+ # TODO(ruby) this will result in super linear decoding behaviour for nested unions
548
+ # follow up with a decoding context that captures current strictness levels
549
+ if (variant = resolve_variant(value))
550
+ return Converter.try_strict_coerce(variant, value)
551
+ end
546
552
 
547
- known_variants.each do |_, variant_fn|
548
- variant = variant_fn.call
553
+ coercible = false
554
+ max_score = 0
549
555
 
550
- case TerminalShop::Converter.try_strict_coerce(variant, value)
551
- in [true, coerced, score]
552
- return [true, coerced, score]
553
- in [false, true, score]
554
- coercible = true
555
- max_score = [max_score, score].max
556
- in [false, false, _]
557
- nil
556
+ known_variants.each do |_, variant_fn|
557
+ variant = variant_fn.call
558
+
559
+ case TerminalShop::Converter.try_strict_coerce(variant, value)
560
+ in [true, coerced, score]
561
+ return [true, coerced, score]
562
+ in [false, true, score]
563
+ coercible = true
564
+ max_score = [max_score, score].max
565
+ in [false, false, _]
566
+ nil
567
+ end
558
568
  end
559
- end
560
569
 
561
- [false, coercible, max_score]
570
+ [false, coercible, max_score]
571
+ end
562
572
  end
563
573
 
564
574
  # rubocop:enable Style/CaseEquality
@@ -839,23 +849,30 @@ module TerminalShop
839
849
  #
840
850
  # @abstract
841
851
  #
852
+ # @example
853
+ # ```ruby
854
+ # # `product_api` is a `TerminalShop::Models::ProductAPI`
855
+ # product_api => {
856
+ # id: id,
857
+ # description: description,
858
+ # name: name
859
+ # }
860
+ # ```
842
861
  class BaseModel
843
862
  extend TerminalShop::Converter
844
863
 
845
- # @private
846
- #
847
- # Assumes superclass fields are totally defined before fields are accessed /
848
- # defined on subclasses.
849
- #
850
- # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
851
- #
852
- def self.known_fields
853
- @known_fields ||= (self < TerminalShop::BaseModel ? superclass.known_fields.dup : {})
854
- end
855
-
856
864
  class << self
857
865
  # @private
858
866
  #
867
+ # Assumes superclass fields are totally defined before fields are accessed /
868
+ # defined on subclasses.
869
+ #
870
+ # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
871
+ #
872
+ def known_fields
873
+ @known_fields ||= (self < TerminalShop::BaseModel ? superclass.known_fields.dup : {})
874
+ end
875
+
859
876
  # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
860
877
  #
861
878
  def fields
@@ -863,135 +880,135 @@ module TerminalShop
863
880
  {**field.except(:type_fn), type: field.fetch(:type_fn).call}
864
881
  end
865
882
  end
866
- end
867
883
 
868
- # @private
869
- #
870
- # @return [Hash{Symbol=>Proc}]
871
- #
872
- def self.defaults = (@defaults ||= {})
884
+ # @private
885
+ #
886
+ # @return [Hash{Symbol=>Proc}]
887
+ #
888
+ def defaults = (@defaults ||= {})
873
889
 
874
- # @private
875
- #
876
- # @param name_sym [Symbol]
877
- #
878
- # @param required [Boolean]
879
- #
880
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
881
- #
882
- # @param spec [Hash{Symbol=>Object}] .
883
- #
884
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
885
- #
886
- # @option spec [Proc] :enum
887
- #
888
- # @option spec [Proc] :union
889
- #
890
- # @option spec [Boolean] :"nil?"
891
- #
892
- private_class_method def self.add_field(name_sym, required:, type_info:, spec:)
893
- type_fn, info =
894
- case type_info
895
- in Proc | Class | TerminalShop::Converter
896
- [TerminalShop::Converter.type_info({**spec, union: type_info}), spec]
897
- in Hash
898
- [TerminalShop::Converter.type_info(type_info), type_info]
899
- end
890
+ # @private
891
+ #
892
+ # @param name_sym [Symbol]
893
+ #
894
+ # @param required [Boolean]
895
+ #
896
+ # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
897
+ #
898
+ # @param spec [Hash{Symbol=>Object}] .
899
+ #
900
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
901
+ #
902
+ # @option spec [Proc] :enum
903
+ #
904
+ # @option spec [Proc] :union
905
+ #
906
+ # @option spec [Boolean] :"nil?"
907
+ #
908
+ private def add_field(name_sym, required:, type_info:, spec:)
909
+ type_fn, info =
910
+ case type_info
911
+ in Proc | Class | TerminalShop::Converter
912
+ [TerminalShop::Converter.type_info({**spec, union: type_info}), spec]
913
+ in Hash
914
+ [TerminalShop::Converter.type_info(type_info), type_info]
915
+ end
900
916
 
901
- fallback = info[:const]
902
- defaults[name_sym] = fallback if required && !info[:nil?] && info.key?(:const)
917
+ fallback = info[:const]
918
+ defaults[name_sym] = fallback if required && !info[:nil?] && info.key?(:const)
903
919
 
904
- key = info.fetch(:api_name, name_sym)
905
- setter = "#{name_sym}="
920
+ key = info.fetch(:api_name, name_sym)
921
+ setter = "#{name_sym}="
906
922
 
907
- if known_fields.key?(name_sym)
908
- [name_sym, setter].each { undef_method(_1) }
909
- end
923
+ if known_fields.key?(name_sym)
924
+ [name_sym, setter].each { undef_method(_1) }
925
+ end
910
926
 
911
- known_fields[name_sym] = {mode: @mode, key: key, required: required, type_fn: type_fn}
927
+ known_fields[name_sym] = {mode: @mode, key: key, required: required, type_fn: type_fn}
912
928
 
913
- define_method(setter) do |val|
914
- @data[key] = val
915
- end
929
+ define_method(setter) do |val|
930
+ @data[key] = val
931
+ end
916
932
 
917
- define_method(name_sym) do
918
- field_type = type_fn.call
919
- value = @data.fetch(key) { self.class.defaults[key] }
920
- TerminalShop::Converter.coerce(field_type, value)
921
- rescue StandardError
922
- name = self.class.name.split("::").last
923
- raise TerminalShop::ConversionError.new(
924
- "Failed to parse #{name}.#{name_sym} as #{field_type.inspect}. " \
925
- "To get the unparsed API response, use #{name}[:#{key}]."
926
- )
933
+ define_method(name_sym) do
934
+ field_type = type_fn.call
935
+ value = @data.fetch(key) { self.class.defaults[key] }
936
+ TerminalShop::Converter.coerce(field_type, value)
937
+ rescue StandardError
938
+ name = self.class.name.split("::").last
939
+ raise TerminalShop::ConversionError.new(
940
+ "Failed to parse #{name}.#{name_sym} as #{field_type.inspect}. " \
941
+ "To get the unparsed API response, use #{name}[:#{key}]."
942
+ )
943
+ end
927
944
  end
928
- end
929
945
 
930
- # @private
931
- #
932
- # @param name_sym [Symbol]
933
- #
934
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
935
- #
936
- # @param spec [Hash{Symbol=>Object}] .
937
- #
938
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
939
- #
940
- # @option spec [Proc] :enum
941
- #
942
- # @option spec [Proc] :union
943
- #
944
- # @option spec [Boolean] :"nil?"
945
- #
946
- def self.required(name_sym, type_info, spec = {})
947
- add_field(name_sym, required: true, type_info: type_info, spec: spec)
948
- end
946
+ # @private
947
+ #
948
+ # @param name_sym [Symbol]
949
+ #
950
+ # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
951
+ #
952
+ # @param spec [Hash{Symbol=>Object}] .
953
+ #
954
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
955
+ #
956
+ # @option spec [Proc] :enum
957
+ #
958
+ # @option spec [Proc] :union
959
+ #
960
+ # @option spec [Boolean] :"nil?"
961
+ #
962
+ def required(name_sym, type_info, spec = {})
963
+ add_field(name_sym, required: true, type_info: type_info, spec: spec)
964
+ end
949
965
 
950
- # @private
951
- #
952
- # @param name_sym [Symbol]
953
- #
954
- # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
955
- #
956
- # @param spec [Hash{Symbol=>Object}] .
957
- #
958
- # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
959
- #
960
- # @option spec [Proc] :enum
961
- #
962
- # @option spec [Proc] :union
963
- #
964
- # @option spec [Boolean] :"nil?"
965
- #
966
- def self.optional(name_sym, type_info, spec = {})
967
- add_field(name_sym, required: false, type_info: type_info, spec: spec)
968
- end
966
+ # @private
967
+ #
968
+ # @param name_sym [Symbol]
969
+ #
970
+ # @param type_info [Hash{Symbol=>Object}, Proc, TerminalShop::Converter, Class]
971
+ #
972
+ # @param spec [Hash{Symbol=>Object}] .
973
+ #
974
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
975
+ #
976
+ # @option spec [Proc] :enum
977
+ #
978
+ # @option spec [Proc] :union
979
+ #
980
+ # @option spec [Boolean] :"nil?"
981
+ #
982
+ def optional(name_sym, type_info, spec = {})
983
+ add_field(name_sym, required: false, type_info: type_info, spec: spec)
984
+ end
969
985
 
970
- # @private
971
- #
972
- # `request_only` attributes not excluded from `.#coerce` when receiving responses
973
- # even if well behaved servers should not send them
974
- #
975
- # @param blk [Proc]
976
- #
977
- private_class_method def self.request_only(&blk)
978
- @mode = :dump
979
- blk.call
980
- ensure
981
- @mode = nil
982
- end
986
+ # @private
987
+ #
988
+ # `request_only` attributes not excluded from `.#coerce` when receiving responses
989
+ # even if well behaved servers should not send them
990
+ #
991
+ # @param blk [Proc]
992
+ #
993
+ private def request_only(&blk)
994
+ @mode = :dump
995
+ blk.call
996
+ ensure
997
+ @mode = nil
998
+ end
983
999
 
984
- # @private
985
- #
986
- # `response_only` attributes are omitted from `.#dump` when making requests
987
- #
988
- # @param blk [Proc]
989
- #
990
- private_class_method def self.response_only(&blk)
991
- @mode = :coerce
992
- blk.call
993
- ensure
994
- @mode = nil
1000
+ # @private
1001
+ #
1002
+ # `response_only` attributes are omitted from `.#dump` when making requests
1003
+ #
1004
+ # @param blk [Proc]
1005
+ #
1006
+ private def response_only(&blk)
1007
+ @mode = :coerce
1008
+ blk.call
1009
+ ensure
1010
+ @mode = nil
1011
+ end
995
1012
  end
996
1013
 
997
1014
  # @param other [Object]
@@ -1007,108 +1024,110 @@ module TerminalShop
1007
1024
  end
1008
1025
  end
1009
1026
 
1010
- # @private
1011
- #
1012
- # @param value [TerminalShop::BaseModel, Hash{Object=>Object}, Object]
1013
- #
1014
- # @return [TerminalShop::BaseModel, Object]
1015
- #
1016
- def self.coerce(value)
1017
- case TerminalShop::Util.coerce_hash(value)
1018
- in Hash => coerced
1019
- new(coerced)
1020
- else
1021
- value
1027
+ class << self
1028
+ # @private
1029
+ #
1030
+ # @param value [TerminalShop::BaseModel, Hash{Object=>Object}, Object]
1031
+ #
1032
+ # @return [TerminalShop::BaseModel, Object]
1033
+ #
1034
+ def coerce(value)
1035
+ case TerminalShop::Util.coerce_hash(value)
1036
+ in Hash => coerced
1037
+ new(coerced)
1038
+ else
1039
+ value
1040
+ end
1022
1041
  end
1023
- end
1024
1042
 
1025
- # @private
1026
- #
1027
- # @param value [TerminalShop::BaseModel, Object]
1028
- #
1029
- # @return [Hash{Object=>Object}, Object]
1030
- #
1031
- def self.dump(value)
1032
- unless (coerced = TerminalShop::Util.coerce_hash(value)).is_a?(Hash)
1033
- return value
1034
- end
1043
+ # @private
1044
+ #
1045
+ # @param value [TerminalShop::BaseModel, Object]
1046
+ #
1047
+ # @return [Hash{Object=>Object}, Object]
1048
+ #
1049
+ def dump(value)
1050
+ unless (coerced = TerminalShop::Util.coerce_hash(value)).is_a?(Hash)
1051
+ return value
1052
+ end
1035
1053
 
1036
- values = coerced.filter_map do |key, val|
1037
- name = key.to_sym
1038
- case (field = known_fields[name])
1039
- in nil
1040
- [name, val]
1041
- else
1042
- mode, type_fn, api_name = field.fetch_values(:mode, :type_fn, :key)
1043
- case mode
1044
- in :coerce
1045
- next
1054
+ values = coerced.filter_map do |key, val|
1055
+ name = key.to_sym
1056
+ case (field = known_fields[name])
1057
+ in nil
1058
+ [name, val]
1046
1059
  else
1047
- target = type_fn.call
1048
- [api_name, TerminalShop::Converter.dump(target, val)]
1060
+ mode, type_fn, api_name = field.fetch_values(:mode, :type_fn, :key)
1061
+ case mode
1062
+ in :coerce
1063
+ next
1064
+ else
1065
+ target = type_fn.call
1066
+ [api_name, TerminalShop::Converter.dump(target, val)]
1067
+ end
1049
1068
  end
1050
- end
1051
- end.to_h
1069
+ end.to_h
1052
1070
 
1053
- defaults.each do |key, val|
1054
- next if values.key?(key)
1071
+ defaults.each do |key, val|
1072
+ next if values.key?(key)
1055
1073
 
1056
- values[key] = val
1057
- end
1058
-
1059
- values
1060
- end
1074
+ values[key] = val
1075
+ end
1061
1076
 
1062
- # @private
1063
- #
1064
- # @param value [Object]
1065
- #
1066
- # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
1067
- #
1068
- def self.try_strict_coerce(value)
1069
- case value
1070
- in Hash | TerminalShop::BaseModel
1071
- value = value.to_h
1072
- else
1073
- return [false, false, 0]
1077
+ values
1074
1078
  end
1075
1079
 
1076
- keys = value.keys.to_set
1077
- great_success = true
1078
- tally = 0
1079
- acc = {}
1080
+ # @private
1081
+ #
1082
+ # @param value [Object]
1083
+ #
1084
+ # @return [Array(true, Object, nil), Array(false, Boolean, Integer)]
1085
+ #
1086
+ def try_strict_coerce(value)
1087
+ case value
1088
+ in Hash | TerminalShop::BaseModel
1089
+ value = value.to_h
1090
+ else
1091
+ return [false, false, 0]
1092
+ end
1093
+
1094
+ keys = value.keys.to_set
1095
+ great_success = true
1096
+ tally = 0
1097
+ acc = {}
1080
1098
 
1081
- known_fields.each_value do |field|
1082
- mode, required, type_fn, api_name = field.fetch_values(:mode, :required, :type_fn, :key)
1083
- keys.delete(api_name)
1099
+ known_fields.each_value do |field|
1100
+ mode, required, type_fn, api_name = field.fetch_values(:mode, :required, :type_fn, :key)
1101
+ keys.delete(api_name)
1084
1102
 
1085
- case [required && mode != :dump, value.key?(api_name)]
1086
- in [_, true]
1087
- target = type_fn.call
1088
- item = value.fetch(api_name)
1089
- case TerminalShop::Converter.try_strict_coerce(target, item)
1090
- in [true, coerced, score]
1091
- tally += score
1092
- acc[api_name] = coerced
1093
- in [false, true, score]
1103
+ case [required && mode != :dump, value.key?(api_name)]
1104
+ in [_, true]
1105
+ target = type_fn.call
1106
+ item = value.fetch(api_name)
1107
+ case TerminalShop::Converter.try_strict_coerce(target, item)
1108
+ in [true, coerced, score]
1109
+ tally += score
1110
+ acc[api_name] = coerced
1111
+ in [false, true, score]
1112
+ great_success = false
1113
+ tally += score
1114
+ acc[api_name] = item
1115
+ in [false, false, _]
1116
+ great_success &&= item.nil?
1117
+ end
1118
+ in [true, false]
1094
1119
  great_success = false
1095
- tally += score
1096
- acc[api_name] = item
1097
- in [false, false, _]
1098
- great_success &&= item.nil?
1120
+ in [false, false]
1121
+ nil
1099
1122
  end
1100
- in [true, false]
1101
- great_success = false
1102
- in [false, false]
1103
- nil
1104
1123
  end
1105
- end
1106
1124
 
1107
- keys.each do |key|
1108
- acc[key] = value.fetch(key)
1109
- end
1125
+ keys.each do |key|
1126
+ acc[key] = value.fetch(key)
1127
+ end
1110
1128
 
1111
- great_success ? [true, new(acc), tally] : [false, true, tally]
1129
+ great_success ? [true, new(acc), tally] : [false, true, tally]
1130
+ end
1112
1131
  end
1113
1132
 
1114
1133
  # Returns the raw value associated with the given key, if found. Otherwise, nil is