orb-billing 1.0.1 → 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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +1 -1
  4. data/lib/orb/errors.rb +22 -0
  5. data/lib/orb/internal/type/array_of.rb +6 -1
  6. data/lib/orb/internal/type/base_model.rb +79 -25
  7. data/lib/orb/internal/type/boolean.rb +7 -1
  8. data/lib/orb/internal/type/converter.rb +42 -34
  9. data/lib/orb/internal/type/enum.rb +10 -2
  10. data/lib/orb/internal/type/file_input.rb +6 -1
  11. data/lib/orb/internal/type/hash_of.rb +6 -1
  12. data/lib/orb/internal/type/union.rb +12 -7
  13. data/lib/orb/internal/type/unknown.rb +7 -1
  14. data/lib/orb/models/customers/credits/ledger_create_entry_by_external_id_params.rb +4 -4
  15. data/lib/orb/models/customers/credits/ledger_create_entry_params.rb +4 -4
  16. data/lib/orb/models/invoice.rb +4 -1
  17. data/lib/orb/models/invoice_fetch_upcoming_response.rb +4 -1
  18. data/lib/orb/models/invoice_line_item_create_response.rb +4 -1
  19. data/lib/orb/models/new_plan_bps_price.rb +10 -1
  20. data/lib/orb/models/new_plan_bulk_bps_price.rb +10 -1
  21. data/lib/orb/models/new_plan_bulk_price.rb +10 -1
  22. data/lib/orb/models/new_plan_bulk_with_proration_price.rb +10 -1
  23. data/lib/orb/models/new_plan_cumulative_grouped_bulk_price.rb +10 -1
  24. data/lib/orb/models/new_plan_grouped_allocation_price.rb +10 -1
  25. data/lib/orb/models/new_plan_grouped_tiered_package_price.rb +10 -1
  26. data/lib/orb/models/new_plan_grouped_tiered_price.rb +10 -1
  27. data/lib/orb/models/new_plan_grouped_with_metered_minimum_price.rb +10 -1
  28. data/lib/orb/models/new_plan_grouped_with_prorated_minimum_price.rb +10 -1
  29. data/lib/orb/models/new_plan_matrix_price.rb +10 -1
  30. data/lib/orb/models/new_plan_matrix_with_allocation_price.rb +10 -1
  31. data/lib/orb/models/new_plan_matrix_with_display_name_price.rb +10 -1
  32. data/lib/orb/models/new_plan_max_group_tiered_package_price.rb +10 -1
  33. data/lib/orb/models/new_plan_package_price.rb +10 -1
  34. data/lib/orb/models/new_plan_package_with_allocation_price.rb +10 -1
  35. data/lib/orb/models/new_plan_scalable_matrix_with_tiered_pricing_price.rb +10 -1
  36. data/lib/orb/models/new_plan_scalable_matrix_with_unit_pricing_price.rb +10 -1
  37. data/lib/orb/models/new_plan_threshold_total_amount_price.rb +10 -1
  38. data/lib/orb/models/new_plan_tier_with_proration_price.rb +10 -1
  39. data/lib/orb/models/new_plan_tiered_bps_price.rb +10 -1
  40. data/lib/orb/models/new_plan_tiered_package_price.rb +10 -1
  41. data/lib/orb/models/new_plan_tiered_package_with_minimum_price.rb +10 -1
  42. data/lib/orb/models/new_plan_tiered_price.rb +10 -1
  43. data/lib/orb/models/new_plan_tiered_with_minimum_price.rb +10 -1
  44. data/lib/orb/models/new_plan_unit_price.rb +10 -1
  45. data/lib/orb/models/new_plan_unit_with_percent_price.rb +10 -1
  46. data/lib/orb/models/new_plan_unit_with_proration_price.rb +10 -1
  47. data/lib/orb/models/plan_create_params.rb +185 -37
  48. data/lib/orb/resources/customers/credits/ledger.rb +6 -6
  49. data/lib/orb/resources/plans.rb +6 -2
  50. data/lib/orb/version.rb +1 -1
  51. data/rbi/orb/errors.rbi +16 -0
  52. data/rbi/orb/internal/type/boolean.rbi +2 -0
  53. data/rbi/orb/internal/type/converter.rbi +15 -15
  54. data/rbi/orb/internal/type/union.rbi +5 -0
  55. data/rbi/orb/internal/type/unknown.rbi +2 -0
  56. data/rbi/orb/models/customers/credits/ledger_create_entry_by_external_id_params.rbi +3 -3
  57. data/rbi/orb/models/customers/credits/ledger_create_entry_params.rbi +3 -3
  58. data/rbi/orb/models/invoice.rbi +2 -0
  59. data/rbi/orb/models/invoice_fetch_upcoming_response.rbi +2 -0
  60. data/rbi/orb/models/invoice_line_item_create_response.rbi +2 -0
  61. data/rbi/orb/models/new_plan_bps_price.rbi +13 -3
  62. data/rbi/orb/models/new_plan_bulk_bps_price.rbi +13 -3
  63. data/rbi/orb/models/new_plan_bulk_price.rbi +13 -3
  64. data/rbi/orb/models/new_plan_bulk_with_proration_price.rbi +13 -3
  65. data/rbi/orb/models/new_plan_cumulative_grouped_bulk_price.rbi +13 -3
  66. data/rbi/orb/models/new_plan_grouped_allocation_price.rbi +13 -3
  67. data/rbi/orb/models/new_plan_grouped_tiered_package_price.rbi +13 -3
  68. data/rbi/orb/models/new_plan_grouped_tiered_price.rbi +13 -3
  69. data/rbi/orb/models/new_plan_grouped_with_metered_minimum_price.rbi +13 -3
  70. data/rbi/orb/models/new_plan_grouped_with_prorated_minimum_price.rbi +13 -3
  71. data/rbi/orb/models/new_plan_matrix_price.rbi +13 -3
  72. data/rbi/orb/models/new_plan_matrix_with_allocation_price.rbi +13 -3
  73. data/rbi/orb/models/new_plan_matrix_with_display_name_price.rbi +13 -3
  74. data/rbi/orb/models/new_plan_max_group_tiered_package_price.rbi +13 -3
  75. data/rbi/orb/models/new_plan_package_price.rbi +13 -3
  76. data/rbi/orb/models/new_plan_package_with_allocation_price.rbi +13 -3
  77. data/rbi/orb/models/new_plan_scalable_matrix_with_tiered_pricing_price.rbi +13 -3
  78. data/rbi/orb/models/new_plan_scalable_matrix_with_unit_pricing_price.rbi +13 -3
  79. data/rbi/orb/models/new_plan_threshold_total_amount_price.rbi +13 -3
  80. data/rbi/orb/models/new_plan_tier_with_proration_price.rbi +13 -3
  81. data/rbi/orb/models/new_plan_tiered_bps_price.rbi +13 -3
  82. data/rbi/orb/models/new_plan_tiered_package_price.rbi +13 -3
  83. data/rbi/orb/models/new_plan_tiered_package_with_minimum_price.rbi +13 -3
  84. data/rbi/orb/models/new_plan_tiered_price.rbi +13 -3
  85. data/rbi/orb/models/new_plan_tiered_with_minimum_price.rbi +13 -3
  86. data/rbi/orb/models/new_plan_unit_price.rbi +13 -3
  87. data/rbi/orb/models/new_plan_unit_with_percent_price.rbi +13 -3
  88. data/rbi/orb/models/new_plan_unit_with_proration_price.rbi +13 -3
  89. data/rbi/orb/models/plan_create_params.rbi +415 -135
  90. data/rbi/orb/resources/customers/credits/ledger.rbi +6 -6
  91. data/rbi/orb/resources/plans.rbi +11 -33
  92. data/sig/orb/errors.rbs +9 -0
  93. data/sig/orb/internal/type/converter.rbs +7 -1
  94. data/sig/orb/models/customers/credits/ledger_create_entry_by_external_id_params.rbs +1 -1
  95. data/sig/orb/models/customers/credits/ledger_create_entry_params.rbs +1 -1
  96. data/sig/orb/models/new_plan_bps_price.rbs +8 -3
  97. data/sig/orb/models/new_plan_bulk_bps_price.rbs +8 -3
  98. data/sig/orb/models/new_plan_bulk_price.rbs +8 -3
  99. data/sig/orb/models/new_plan_bulk_with_proration_price.rbs +8 -3
  100. data/sig/orb/models/new_plan_cumulative_grouped_bulk_price.rbs +8 -3
  101. data/sig/orb/models/new_plan_grouped_allocation_price.rbs +8 -3
  102. data/sig/orb/models/new_plan_grouped_tiered_package_price.rbs +8 -3
  103. data/sig/orb/models/new_plan_grouped_tiered_price.rbs +8 -3
  104. data/sig/orb/models/new_plan_grouped_with_metered_minimum_price.rbs +8 -3
  105. data/sig/orb/models/new_plan_grouped_with_prorated_minimum_price.rbs +8 -3
  106. data/sig/orb/models/new_plan_matrix_price.rbs +8 -3
  107. data/sig/orb/models/new_plan_matrix_with_allocation_price.rbs +8 -3
  108. data/sig/orb/models/new_plan_matrix_with_display_name_price.rbs +8 -3
  109. data/sig/orb/models/new_plan_max_group_tiered_package_price.rbs +8 -3
  110. data/sig/orb/models/new_plan_package_price.rbs +8 -3
  111. data/sig/orb/models/new_plan_package_with_allocation_price.rbs +8 -3
  112. data/sig/orb/models/new_plan_scalable_matrix_with_tiered_pricing_price.rbs +8 -3
  113. data/sig/orb/models/new_plan_scalable_matrix_with_unit_pricing_price.rbs +8 -3
  114. data/sig/orb/models/new_plan_threshold_total_amount_price.rbs +8 -3
  115. data/sig/orb/models/new_plan_tier_with_proration_price.rbs +8 -3
  116. data/sig/orb/models/new_plan_tiered_bps_price.rbs +8 -3
  117. data/sig/orb/models/new_plan_tiered_package_price.rbs +8 -3
  118. data/sig/orb/models/new_plan_tiered_package_with_minimum_price.rbs +8 -3
  119. data/sig/orb/models/new_plan_tiered_price.rbs +8 -3
  120. data/sig/orb/models/new_plan_tiered_with_minimum_price.rbs +8 -3
  121. data/sig/orb/models/new_plan_unit_price.rbs +8 -3
  122. data/sig/orb/models/new_plan_unit_with_percent_price.rbs +8 -3
  123. data/sig/orb/models/new_plan_unit_with_proration_price.rbs +8 -3
  124. data/sig/orb/models/plan_create_params.rbs +156 -37
  125. data/sig/orb/resources/customers/credits/ledger.rbs +2 -2
  126. data/sig/orb/resources/plans.rbs +3 -1
  127. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56d8c2db075fccdc29ea3e9876e216cd41b8fdd6ecb2cf014b6fbcefd6a4b718
4
- data.tar.gz: d7ebca79e96774d90abbf00e2dfdb310969c174d3b420b56cf52dd9a4b018de7
3
+ metadata.gz: 72bf8cc708079b89812a37a34a92505d31c7a8e4fb7be72449ef1cefa3a68433
4
+ data.tar.gz: c94583819d1a3c5e6544dc35378a645f5b823e6591a6fc8a18e0a02c070e68da
5
5
  SHA512:
6
- metadata.gz: 7240cea77f6aa12233521b00b3c56c60b8d503cc02f609f75d9f84643f6a56f33440fa9728eaefa205cab1d6234e771e9b936a75c756f32e26327be23c853488
7
- data.tar.gz: 4d9c4f316347ac17e06dbb59ba22de711ae0a04849f3fa9780de807ff8277b9739c989b75b2a3a010ea850ede8b5ec09b8dd40d0a09a4163d653fdfb0ae9a32d
6
+ metadata.gz: 3955626d5164c7909179bd3d87b0bdea7660cc532390ecff4586642a79072014f039fe0e1ecf05a9c1a52ed2a5088800bac24bea9ae02fe0110692c6579cf0f1
7
+ data.tar.gz: b440576069f58911b5fb0640bdd8e92a0210b8027b9cf7640aee6c2094d94ff56b8285e3e870630c825684b3902f8eb4085ec8b79a233b5eb4d3ca9557e9f898
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 (2025-06-18)
4
+
5
+ Full Changelog: [v1.0.1...v1.1.0](https://github.com/orbcorp/orb-ruby/compare/v1.0.1...v1.1.0)
6
+
7
+ ### Features
8
+
9
+ * **api:** api update ([c557aa7](https://github.com/orbcorp/orb-ruby/commit/c557aa72779ba1d99e5983b8b3bedb9d4ea6f53a))
10
+ * **api:** api update ([9d2ed65](https://github.com/orbcorp/orb-ruby/commit/9d2ed65a2ee93d7138f9ee4e7e0cf375f2b78b67))
11
+ * **api:** api update ([1d97f15](https://github.com/orbcorp/orb-ruby/commit/1d97f159f6fc72e33037b8ac8775ae48fa5a75d9))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * issue where we cannot mutate arrays on base model derivatives ([b53b6cc](https://github.com/orbcorp/orb-ruby/commit/b53b6cce364fc3bc4fead546b889e58e12f8862a))
17
+
18
+
19
+ ### Chores
20
+
21
+ * **ci:** enable for pull requests ([0d16f9f](https://github.com/orbcorp/orb-ruby/commit/0d16f9f5a238d651c9e5f68a5368a1835d22604f))
22
+ * **ci:** link to correct github repo ([0d079fe](https://github.com/orbcorp/orb-ruby/commit/0d079fe8e8a6b862ea6a33ba091478011658252d))
23
+ * **internal:** version bump ([4956d97](https://github.com/orbcorp/orb-ruby/commit/4956d97d24d4a2e388e4c417e18e09eef1361dad))
24
+
3
25
  ## 1.0.1 (2025-06-09)
4
26
 
5
27
  Full Changelog: [v1.0.0...v1.0.1](https://github.com/orbcorp/orb-ruby/compare/v1.0.0...v1.0.1)
data/README.md CHANGED
@@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
15
15
  <!-- x-release-please-start-version -->
16
16
 
17
17
  ```ruby
18
- gem "orb-billing", "~> 1.0.1"
18
+ gem "orb-billing", "~> 1.1.0"
19
19
  ```
20
20
 
21
21
  <!-- x-release-please-end -->
data/lib/orb/errors.rb CHANGED
@@ -9,6 +9,28 @@ module Orb
9
9
  end
10
10
 
11
11
  class ConversionError < Orb::Errors::Error
12
+ # @return [StandardError, nil]
13
+ def cause = @cause.nil? ? super : @cause
14
+
15
+ # @api private
16
+ #
17
+ # @param on [Class<StandardError>]
18
+ # @param method [Symbol]
19
+ # @param target [Object]
20
+ # @param value [Object]
21
+ # @param cause [StandardError, nil]
22
+ def initialize(on:, method:, target:, value:, cause: nil)
23
+ cls = on.name.split("::").last
24
+
25
+ message = [
26
+ "Failed to parse #{cls}.#{method} from #{value.class} to #{target.inspect}.",
27
+ "To get the unparsed API response, use #{cls}[#{method.inspect}].",
28
+ cause && "Cause: #{cause.message}"
29
+ ].filter(&:itself).join(" ")
30
+
31
+ @cause = cause
32
+ super(message)
33
+ end
12
34
  end
13
35
 
14
36
  class APIError < Orb::Errors::Error
@@ -62,10 +62,14 @@ module Orb
62
62
  #
63
63
  # @param state [Hash{Symbol=>Object}] .
64
64
  #
65
- # @option state [Boolean, :strong] :strictness
65
+ # @option state [Boolean] :translate_names
66
+ #
67
+ # @option state [Boolean] :strictness
66
68
  #
67
69
  # @option state [Hash{Symbol=>Object}] :exactness
68
70
  #
71
+ # @option state [Class<StandardError>] :error
72
+ #
69
73
  # @option state [Integer] :branched
70
74
  #
71
75
  # @return [Array<Object>, Object]
@@ -74,6 +78,7 @@ module Orb
74
78
 
75
79
  unless value.is_a?(Array)
76
80
  exactness[:no] += 1
81
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Array}")
77
82
  return value
78
83
  end
79
84
 
@@ -60,7 +60,7 @@ module Orb
60
60
  [Orb::Internal::Type::Converter.type_info(type_info), type_info]
61
61
  end
62
62
 
63
- setter = "#{name_sym}="
63
+ setter = :"#{name_sym}="
64
64
  api_name = info.fetch(:api_name, name_sym)
65
65
  nilable = info.fetch(:nil?, false)
66
66
  const = required && !nilable ? info.fetch(:const, Orb::Internal::OMIT) : Orb::Internal::OMIT
@@ -77,30 +77,63 @@ module Orb
77
77
  type_fn: type_fn
78
78
  }
79
79
 
80
- define_method(setter) { @data.store(name_sym, _1) }
80
+ define_method(setter) do |value|
81
+ target = type_fn.call
82
+ state = Orb::Internal::Type::Converter.new_coerce_state(translate_names: false)
83
+ coerced = Orb::Internal::Type::Converter.coerce(target, value, state: state)
84
+ status = @coerced.store(name_sym, state.fetch(:error) || true)
85
+ stored =
86
+ case [target, status]
87
+ in [Orb::Internal::Type::Converter | Symbol, true]
88
+ coerced
89
+ else
90
+ value
91
+ end
92
+ @data.store(name_sym, stored)
93
+ end
81
94
 
95
+ # rubocop:disable Style/CaseEquality
96
+ # rubocop:disable Metrics/BlockLength
82
97
  define_method(name_sym) do
83
98
  target = type_fn.call
84
- value = @data.fetch(name_sym) { const == Orb::Internal::OMIT ? nil : const }
85
- state = {strictness: :strong, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
86
- if (nilable || !required) && value.nil?
87
- nil
88
- else
89
- Orb::Internal::Type::Converter.coerce(
90
- target,
91
- value,
92
- state: state
99
+
100
+ case @coerced[name_sym]
101
+ in true | false if Orb::Internal::Type::Converter === target
102
+ @data.fetch(name_sym)
103
+ in ::StandardError => e
104
+ raise Orb::Errors::ConversionError.new(
105
+ on: self.class,
106
+ method: __method__,
107
+ target: target,
108
+ value: @data.fetch(name_sym),
109
+ cause: e
93
110
  )
111
+ else
112
+ Kernel.then do
113
+ value = @data.fetch(name_sym) { const == Orb::Internal::OMIT ? nil : const }
114
+ state = Orb::Internal::Type::Converter.new_coerce_state(translate_names: false)
115
+ if (nilable || !required) && value.nil?
116
+ nil
117
+ else
118
+ Orb::Internal::Type::Converter.coerce(
119
+ target,
120
+ value,
121
+ state: state
122
+ )
123
+ end
124
+ rescue StandardError => e
125
+ raise Orb::Errors::ConversionError.new(
126
+ on: self.class,
127
+ method: __method__,
128
+ target: target,
129
+ value: value,
130
+ cause: e
131
+ )
132
+ end
94
133
  end
95
- rescue StandardError => e
96
- cls = self.class.name.split("::").last
97
- message = [
98
- "Failed to parse #{cls}.#{__method__} from #{value.class} to #{target.inspect}.",
99
- "To get the unparsed API response, use #{cls}[#{__method__.inspect}].",
100
- "Cause: #{e.message}"
101
- ].join(" ")
102
- raise Orb::Errors::ConversionError.new(message)
103
134
  end
135
+ # rubocop:enable Metrics/BlockLength
136
+ # rubocop:enable Style/CaseEquality
104
137
  end
105
138
 
106
139
  # @api private
@@ -200,23 +233,28 @@ module Orb
200
233
  #
201
234
  # @param state [Hash{Symbol=>Object}] .
202
235
  #
203
- # @option state [Boolean, :strong] :strictness
236
+ # @option state [Boolean] :translate_names
237
+ #
238
+ # @option state [Boolean] :strictness
204
239
  #
205
240
  # @option state [Hash{Symbol=>Object}] :exactness
206
241
  #
242
+ # @option state [Class<StandardError>] :error
243
+ #
207
244
  # @option state [Integer] :branched
208
245
  #
209
246
  # @return [self, Object]
210
247
  def coerce(value, state:)
211
248
  exactness = state.fetch(:exactness)
212
249
 
213
- if value.is_a?(self.class)
250
+ if value.is_a?(self)
214
251
  exactness[:yes] += 1
215
252
  return value
216
253
  end
217
254
 
218
255
  unless (val = Orb::Internal::Util.coerce_hash(value)).is_a?(Hash)
219
256
  exactness[:no] += 1
257
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Hash}")
220
258
  return value
221
259
  end
222
260
  exactness[:yes] += 1
@@ -224,13 +262,15 @@ module Orb
224
262
  keys = val.keys.to_set
225
263
  instance = new
226
264
  data = instance.to_h
265
+ status = instance.instance_variable_get(:@coerced)
227
266
 
228
267
  # rubocop:disable Metrics/BlockLength
229
268
  fields.each do |name, field|
230
269
  mode, required, target = field.fetch_values(:mode, :required, :type)
231
270
  api_name, nilable, const = field.fetch_values(:api_name, :nilable, :const)
271
+ src_name = state.fetch(:translate_names) ? api_name : name
232
272
 
233
- unless val.key?(api_name)
273
+ unless val.key?(src_name)
234
274
  if required && mode != :dump && const == Orb::Internal::OMIT
235
275
  exactness[nilable ? :maybe : :no] += 1
236
276
  else
@@ -239,9 +279,10 @@ module Orb
239
279
  next
240
280
  end
241
281
 
242
- item = val.fetch(api_name)
243
- keys.delete(api_name)
282
+ item = val.fetch(src_name)
283
+ keys.delete(src_name)
244
284
 
285
+ state[:error] = nil
245
286
  converted =
246
287
  if item.nil? && (nilable || !required)
247
288
  exactness[nilable ? :yes : :maybe] += 1
@@ -255,6 +296,8 @@ module Orb
255
296
  item
256
297
  end
257
298
  end
299
+
300
+ status.store(name, state.fetch(:error) || true)
258
301
  data.store(name, converted)
259
302
  end
260
303
  # rubocop:enable Metrics/BlockLength
@@ -430,7 +473,18 @@ module Orb
430
473
  # Create a new instance of a model.
431
474
  #
432
475
  # @param data [Hash{Symbol=>Object}, self]
433
- def initialize(data = {}) = (@data = Orb::Internal::Util.coerce_hash!(data).to_h)
476
+ def initialize(data = {})
477
+ @data = {}
478
+ @coerced = {}
479
+ Orb::Internal::Util.coerce_hash!(data).each do
480
+ if self.class.known_fields.key?(_1)
481
+ public_send(:"#{_1}=", _2)
482
+ else
483
+ @data.store(_1, _2)
484
+ @coerced.store(_1, false)
485
+ end
486
+ end
487
+ end
434
488
 
435
489
  class << self
436
490
  # @api private
@@ -31,14 +31,20 @@ module Orb
31
31
  class << self
32
32
  # @api private
33
33
  #
34
+ # Coerce value to Boolean if possible, otherwise return the original value.
35
+ #
34
36
  # @param value [Boolean, Object]
35
37
  #
36
38
  # @param state [Hash{Symbol=>Object}] .
37
39
  #
38
- # @option state [Boolean, :strong] :strictness
40
+ # @option state [Boolean] :translate_names
41
+ #
42
+ # @option state [Boolean] :strictness
39
43
  #
40
44
  # @option state [Hash{Symbol=>Object}] :exactness
41
45
  #
46
+ # @option state [Class<StandardError>] :error
47
+ #
42
48
  # @option state [Integer] :branched
43
49
  #
44
50
  # @return [Boolean, Object]
@@ -15,10 +15,14 @@ module Orb
15
15
  #
16
16
  # @param state [Hash{Symbol=>Object}] .
17
17
  #
18
- # @option state [Boolean, :strong] :strictness
18
+ # @option state [Boolean] :translate_names
19
+ #
20
+ # @option state [Boolean] :strictness
19
21
  #
20
22
  # @option state [Hash{Symbol=>Object}] :exactness
21
23
  #
24
+ # @option state [Class<StandardError>] :error
25
+ #
22
26
  # @option state [Integer] :branched
23
27
  #
24
28
  # @return [Object]
@@ -94,6 +98,21 @@ module Orb
94
98
  end
95
99
  end
96
100
 
101
+ # @api private
102
+ #
103
+ # @param translate_names [Boolean]
104
+ #
105
+ # @return [Hash{Symbol=>Object}]
106
+ def new_coerce_state(translate_names: true)
107
+ {
108
+ translate_names: translate_names,
109
+ strictness: true,
110
+ exactness: {yes: 0, no: 0, maybe: 0},
111
+ error: nil,
112
+ branched: 0
113
+ }
114
+ end
115
+
97
116
  # @api private
98
117
  #
99
118
  # Based on `target`, transform `value` into `target`, to the extent possible:
@@ -110,14 +129,11 @@ module Orb
110
129
  #
111
130
  # @param value [Object]
112
131
  #
113
- # @param state [Hash{Symbol=>Object}] The `strictness` is one of `true`, `false`, or `:strong`. This informs the
114
- # coercion strategy when we have to decide between multiple possible conversion
115
- # targets:
132
+ # @param state [Hash{Symbol=>Object}] The `strictness` is one of `true`, `false`. This informs the coercion strategy
133
+ # when we have to decide between multiple possible conversion targets:
116
134
  #
117
135
  # - `true`: the conversion must be exact, with minimum coercion.
118
136
  # - `false`: the conversion can be approximate, with some coercion.
119
- # - `:strong`: the conversion must be exact, with no coercion, and raise an error
120
- # if not possible.
121
137
  #
122
138
  # The `exactness` is `Hash` with keys being one of `yes`, `no`, or `maybe`. For
123
139
  # any given conversion attempt, the exactness will be updated based on how closely
@@ -130,21 +146,20 @@ module Orb
130
146
  #
131
147
  # See implementation below for more details.
132
148
  #
133
- # @option state [Boolean, :strong] :strictness
149
+ # @option state [Boolean] :translate_names
150
+ #
151
+ # @option state [Boolean] :strictness
134
152
  #
135
153
  # @option state [Hash{Symbol=>Object}] :exactness
136
154
  #
155
+ # @option state [Class<StandardError>] :error
156
+ #
137
157
  # @option state [Integer] :branched
138
158
  #
139
159
  # @return [Object]
140
- def coerce(
141
- target,
142
- value,
143
- state: {strictness: true, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
144
- )
145
- # rubocop:disable Lint/SuppressedException
160
+ def coerce(target, value, state: Orb::Internal::Type::Converter.new_coerce_state)
146
161
  # rubocop:disable Metrics/BlockNesting
147
- strictness, exactness = state.fetch_values(:strictness, :exactness)
162
+ exactness = state.fetch(:exactness)
148
163
 
149
164
  case target
150
165
  in Orb::Internal::Type::Converter
@@ -160,29 +175,26 @@ module Orb
160
175
  exactness[value.nil? ? :yes : :maybe] += 1
161
176
  return nil
162
177
  in -> { _1 <= Integer }
163
- if value.is_a?(Integer)
178
+ case value
179
+ in Integer
164
180
  exactness[:yes] += 1
165
181
  return value
166
- elsif strictness == :strong && Integer(value, exception: false) != value
167
- message = "no implicit conversion of #{value.class} into #{target.inspect}"
168
- raise value.is_a?(Numeric) ? ArgumentError.new(message) : TypeError.new(message)
169
182
  else
170
183
  Kernel.then do
171
184
  return Integer(value).tap { exactness[:maybe] += 1 }
172
- rescue ArgumentError, TypeError
185
+ rescue ArgumentError, TypeError => e
186
+ state[:error] = e
173
187
  end
174
188
  end
175
189
  in -> { _1 <= Float }
176
190
  if value.is_a?(Numeric)
177
191
  exactness[:yes] += 1
178
192
  return Float(value)
179
- elsif strictness == :strong
180
- message = "no implicit conversion of #{value.class} into #{target.inspect}"
181
- raise TypeError.new(message)
182
193
  else
183
194
  Kernel.then do
184
195
  return Float(value).tap { exactness[:maybe] += 1 }
185
- rescue ArgumentError, TypeError
196
+ rescue ArgumentError, TypeError => e
197
+ state[:error] = e
186
198
  end
187
199
  end
188
200
  in -> { _1 <= String }
@@ -194,16 +206,13 @@ module Orb
194
206
  exactness[:yes] += 1
195
207
  return value.string
196
208
  else
197
- if strictness == :strong
198
- message = "no implicit conversion of #{value.class} into #{target.inspect}"
199
- raise TypeError.new(message)
200
- end
209
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{String}")
201
210
  end
202
211
  in -> { _1 <= Date || _1 <= Time }
203
212
  Kernel.then do
204
213
  return target.parse(value).tap { exactness[:yes] += 1 }
205
214
  rescue ArgumentError, TypeError => e
206
- raise e if strictness == :strong
215
+ state[:error] = e
207
216
  end
208
217
  in -> { _1 <= StringIO } if value.is_a?(String)
209
218
  exactness[:yes] += 1
@@ -221,10 +230,8 @@ module Orb
221
230
  return value
222
231
  end
223
232
  else
224
- if strictness == :strong
225
- message = "cannot convert non-matching #{value.class} into #{target.inspect}"
226
- raise ArgumentError.new(message)
227
- end
233
+ message = "cannot convert non-matching #{value.class} into #{target.inspect}"
234
+ state[:error] = ArgumentError.new(message)
228
235
  end
229
236
  else
230
237
  end
@@ -232,7 +239,6 @@ module Orb
232
239
  exactness[:no] += 1
233
240
  value
234
241
  # rubocop:enable Metrics/BlockNesting
235
- # rubocop:enable Lint/SuppressedException
236
242
  end
237
243
 
238
244
  # @api private
@@ -277,8 +283,10 @@ module Orb
277
283
  define_sorbet_constant!(:CoerceState) do
278
284
  T.type_alias do
279
285
  {
280
- strictness: T.any(T::Boolean, Symbol),
286
+ translate_names: T::Boolean,
287
+ strictness: T::Boolean,
281
288
  exactness: {yes: Integer, no: Integer, maybe: Integer},
289
+ error: T::Class[StandardError],
282
290
  branched: Integer
283
291
  }
284
292
  end
@@ -77,10 +77,14 @@ module Orb
77
77
  #
78
78
  # @param state [Hash{Symbol=>Object}] .
79
79
  #
80
- # @option state [Boolean, :strong] :strictness
80
+ # @option state [Boolean] :translate_names
81
+ #
82
+ # @option state [Boolean] :strictness
81
83
  #
82
84
  # @option state [Hash{Symbol=>Object}] :exactness
83
85
  #
86
+ # @option state [Class<StandardError>] :error
87
+ #
84
88
  # @option state [Integer] :branched
85
89
  #
86
90
  # @return [Symbol, Object]
@@ -91,8 +95,12 @@ module Orb
91
95
  if values.include?(val)
92
96
  exactness[:yes] += 1
93
97
  val
98
+ elsif values.first&.class == val.class
99
+ exactness[:maybe] += 1
100
+ value
94
101
  else
95
- exactness[values.first&.class == val.class ? :maybe : :no] += 1
102
+ exactness[:no] += 1
103
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{self}")
96
104
  value
97
105
  end
98
106
  end
@@ -44,10 +44,14 @@ module Orb
44
44
  #
45
45
  # @param state [Hash{Symbol=>Object}] .
46
46
  #
47
- # @option state [Boolean, :strong] :strictness
47
+ # @option state [Boolean] :translate_names
48
+ #
49
+ # @option state [Boolean] :strictness
48
50
  #
49
51
  # @option state [Hash{Symbol=>Object}] :exactness
50
52
  #
53
+ # @option state [Class<StandardError>] :error
54
+ #
51
55
  # @option state [Integer] :branched
52
56
  #
53
57
  # @return [StringIO, Object]
@@ -61,6 +65,7 @@ module Orb
61
65
  exactness[:yes] += 1
62
66
  value
63
67
  else
68
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{StringIO}")
64
69
  exactness[:no] += 1
65
70
  value
66
71
  end
@@ -77,10 +77,14 @@ module Orb
77
77
  #
78
78
  # @param state [Hash{Symbol=>Object}] .
79
79
  #
80
- # @option state [Boolean, :strong] :strictness
80
+ # @option state [Boolean] :translate_names
81
+ #
82
+ # @option state [Boolean] :strictness
81
83
  #
82
84
  # @option state [Hash{Symbol=>Object}] :exactness
83
85
  #
86
+ # @option state [Class<StandardError>] :error
87
+ #
84
88
  # @option state [Integer] :branched
85
89
  #
86
90
  # @return [Hash{Symbol=>Object}, Object]
@@ -89,6 +93,7 @@ module Orb
89
93
 
90
94
  unless value.is_a?(Hash)
91
95
  exactness[:no] += 1
96
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Hash}")
92
97
  return value
93
98
  end
94
99
 
@@ -150,14 +150,23 @@ module Orb
150
150
 
151
151
  # @api private
152
152
  #
153
+ # Tries to efficiently coerce the given value to one of the known variants.
154
+ #
155
+ # If the value cannot match any of the known variants, the coercion is considered
156
+ # non-viable and returns the original value.
157
+ #
153
158
  # @param value [Object]
154
159
  #
155
160
  # @param state [Hash{Symbol=>Object}] .
156
161
  #
157
- # @option state [Boolean, :strong] :strictness
162
+ # @option state [Boolean] :translate_names
163
+ #
164
+ # @option state [Boolean] :strictness
158
165
  #
159
166
  # @option state [Hash{Symbol=>Object}] :exactness
160
167
  #
168
+ # @option state [Class<StandardError>] :error
169
+ #
161
170
  # @option state [Integer] :branched
162
171
  #
163
172
  # @return [Object]
@@ -168,7 +177,6 @@ module Orb
168
177
 
169
178
  strictness = state.fetch(:strictness)
170
179
  exactness = state.fetch(:exactness)
171
- state[:strictness] = strictness == :strong ? true : strictness
172
180
 
173
181
  alternatives = []
174
182
  known_variants.each do |_, variant_fn|
@@ -187,13 +195,10 @@ module Orb
187
195
  end
188
196
  end
189
197
 
190
- case alternatives.sort_by(&:first)
198
+ case alternatives.sort_by!(&:first)
191
199
  in []
192
200
  exactness[:no] += 1
193
- if strictness == :strong
194
- message = "no possible conversion of #{value.class} into a variant of #{target.inspect}"
195
- raise ArgumentError.new(message)
196
- end
201
+ state[:error] = ArgumentError.new("no matching variant for #{value.inspect}")
197
202
  value
198
203
  in [[_, exact, coerced], *]
199
204
  exact.each { exactness[_1] += _2 }
@@ -33,14 +33,20 @@ module Orb
33
33
  class << self
34
34
  # @api private
35
35
  #
36
+ # No coercion needed for Unknown type.
37
+ #
36
38
  # @param value [Object]
37
39
  #
38
40
  # @param state [Hash{Symbol=>Object}] .
39
41
  #
40
- # @option state [Boolean, :strong] :strictness
42
+ # @option state [Boolean] :translate_names
43
+ #
44
+ # @option state [Boolean] :strictness
41
45
  #
42
46
  # @option state [Hash{Symbol=>Object}] :exactness
43
47
  #
48
+ # @option state [Class<StandardError>] :error
49
+ #
44
50
  # @option state [Integer] :branched
45
51
  #
46
52
  # @return [Object]
@@ -50,7 +50,7 @@ module Orb
50
50
  # An ISO 8601 format date that identifies the origination credit block to expire
51
51
  #
52
52
  # @return [Time, nil]
53
- required :expiry_date, Time, nil?: true
53
+ optional :expiry_date, Time, nil?: true
54
54
 
55
55
  # @!attribute invoice_settings
56
56
  # Passing `invoice_settings` automatically generates an invoice for the newly
@@ -100,7 +100,7 @@ module Orb
100
100
  enum: -> { Orb::Customers::Credits::LedgerCreateEntryByExternalIDParams::VoidReason },
101
101
  nil?: true
102
102
 
103
- # @!method initialize(amount:, entry_type:, expiry_date:, target_expiry_date:, block_id:, currency: nil, description: nil, effective_date: nil, invoice_settings: nil, metadata: nil, per_unit_cost_basis: nil, void_reason: nil, request_options: {})
103
+ # @!method initialize(amount:, entry_type:, target_expiry_date:, block_id:, currency: nil, description: nil, effective_date: nil, expiry_date: nil, invoice_settings: nil, metadata: nil, per_unit_cost_basis: nil, void_reason: nil, request_options: {})
104
104
  # Some parameter documentations has been truncated, see
105
105
  # {Orb::Models::Customers::Credits::LedgerCreateEntryByExternalIDParams} for more
106
106
  # details.
@@ -109,8 +109,6 @@ module Orb
109
109
  #
110
110
  # @param entry_type [Symbol, Orb::Models::Customers::Credits::LedgerCreateEntryByExternalIDParams::EntryType]
111
111
  #
112
- # @param expiry_date [Time, nil] An ISO 8601 format date that identifies the origination credit block to expire
113
- #
114
112
  # @param target_expiry_date [Date] A future date (specified in YYYY-MM-DD format) used for expiration change, denot
115
113
  #
116
114
  # @param block_id [String] The ID of the block to reverse a decrement from.
@@ -121,6 +119,8 @@ module Orb
121
119
  #
122
120
  # @param effective_date [Time, nil] An ISO 8601 format date that denotes when this credit balance should become avai
123
121
  #
122
+ # @param expiry_date [Time, nil] An ISO 8601 format date that identifies the origination credit block to expire
123
+ #
124
124
  # @param invoice_settings [Orb::Models::Customers::Credits::LedgerCreateEntryByExternalIDParams::InvoiceSettings, nil] Passing `invoice_settings` automatically generates an invoice for the newly adde
125
125
  #
126
126
  # @param metadata [Hash{Symbol=>String, nil}, nil] User-specified key/value pairs for the resource. Individual keys can be removed