terminal-shop 0.1.0.pre.alpha.5

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