pangea-sdk 0.0.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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/.ignore +2 -0
  3. data/README.md +1 -0
  4. data/lib/pangea/client.rb +51 -0
  5. data/lib/pangea/errors.rb +113 -0
  6. data/lib/pangea/internal/transport/base_client.rb +346 -0
  7. data/lib/pangea/internal/transport/pooled_net_requester.rb +191 -0
  8. data/lib/pangea/internal/type/array_of.rb +119 -0
  9. data/lib/pangea/internal/type/base_model.rb +289 -0
  10. data/lib/pangea/internal/type/boolean.rb +44 -0
  11. data/lib/pangea/internal/type/converter.rb +228 -0
  12. data/lib/pangea/internal/type/hash_of.rb +166 -0
  13. data/lib/pangea/internal/type/request_parameters.rb +38 -0
  14. data/lib/pangea/internal/type/union.rb +66 -0
  15. data/lib/pangea/internal/type/unknown.rb +50 -0
  16. data/lib/pangea/internal/util.rb +429 -0
  17. data/lib/pangea/internal.rb +12 -0
  18. data/lib/pangea/models/ai_guard/classification_result.rb +33 -0
  19. data/lib/pangea/models/ai_guard/hardening_result.rb +27 -0
  20. data/lib/pangea/models/ai_guard/language_result.rb +20 -0
  21. data/lib/pangea/models/ai_guard/malicious_entity_result.rb +42 -0
  22. data/lib/pangea/models/ai_guard/prompt_injection_result.rb +33 -0
  23. data/lib/pangea/models/ai_guard/redact_entity_result.rb +43 -0
  24. data/lib/pangea/models/ai_guard/single_entity_result.rb +21 -0
  25. data/lib/pangea/models/ai_guard/text_guard_message_param.rb +19 -0
  26. data/lib/pangea/models/ai_guard/text_guard_params.rb +24 -0
  27. data/lib/pangea/models/ai_guard/text_guard_result.rb +308 -0
  28. data/lib/pangea/models/ai_guard/topic_result.rb +33 -0
  29. data/lib/pangea/models/pangea_response.rb +67 -0
  30. data/lib/pangea/request_options.rb +35 -0
  31. data/lib/pangea/services/ai_guard.rb +62 -0
  32. data/lib/pangea/version.rb +5 -0
  33. data/lib/pangea.rb +45 -0
  34. data/manifest.yaml +6 -0
  35. data/rbi/lib/pangea/client.rbi +25 -0
  36. data/rbi/lib/pangea/internal/internal.rbi +28 -0
  37. data/rbi/lib/pangea/internal/transport/base_client.rbi +18 -0
  38. data/rbi/lib/pangea/internal/type/array_of.rbi +66 -0
  39. data/rbi/lib/pangea/internal/type/base_model.rbi +33 -0
  40. data/rbi/lib/pangea/internal/type/boolean.rbi +46 -0
  41. data/rbi/lib/pangea/internal/type/converter.rbi +38 -0
  42. data/rbi/lib/pangea/internal/type/request_parameters.rbi +20 -0
  43. data/rbi/lib/pangea/internal/type/union.rbi +21 -0
  44. data/rbi/lib/pangea/internal/type/unknown.rbi +20 -0
  45. data/rbi/lib/pangea/internal.rbi +7 -0
  46. data/rbi/lib/pangea/models/ai_guard/text_guard_message_param.rbi +15 -0
  47. data/rbi/lib/pangea/models/ai_guard/text_guard_result.rbi +13 -0
  48. data/rbi/lib/pangea/models/pangea_response.rbi +31 -0
  49. data/rbi/lib/pangea/request_options.rbi +17 -0
  50. data/rbi/lib/pangea/services/ai_guard.rbi +28 -0
  51. data/rbi/lib/pangea/version.rbi +5 -0
  52. data/sig/pangea/client.rbs +12 -0
  53. data/sig/pangea/internal/transport/base_client.rbs +14 -0
  54. data/sig/pangea/internal/type/base_model.rbs +12 -0
  55. data/sig/pangea/internal/type/boolean.rbs +10 -0
  56. data/sig/pangea/internal/type/converter.rbs +9 -0
  57. data/sig/pangea/internal/type/request_parameters.rbs +15 -0
  58. data/sig/pangea/models/pangea_response.rbs +12 -0
  59. data/sig/pangea/models/text_guard_result.rbs +19 -0
  60. data/sig/pangea/request_options.rbs +34 -0
  61. data/sig/pangea/services/ai_guard.rbs +12 -0
  62. data/sig/pangea/version.rbs +3 -0
  63. metadata +126 -0
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pangea
4
+ module Internal
5
+ module Transport
6
+ # @api private
7
+ class PooledNetRequester
8
+ KEEP_ALIVE_TIMEOUT = 30
9
+
10
+ class << self
11
+ # @api private
12
+ #
13
+ # @param url [URI::Generic]
14
+ #
15
+ # @return [Net::HTTP]
16
+ def connect(url)
17
+ port =
18
+ case [url.port, url.scheme]
19
+ in [Integer, _]
20
+ url.port
21
+ in [nil, "http" | "ws"]
22
+ Net::HTTP.http_default_port
23
+ in [nil, "https" | "wss"]
24
+ Net::HTTP.https_default_port
25
+ end
26
+
27
+ Net::HTTP.new(url.host, port).tap do
28
+ _1.use_ssl = %w[https wss].include?(url.scheme)
29
+ _1.max_retries = 0
30
+ end
31
+ end
32
+
33
+ # @api private
34
+ #
35
+ # @param conn [Net::HTTP]
36
+ # @param deadline [Float]
37
+ def calibrate_socket_timeout(conn, deadline)
38
+ timeout = deadline - Pangea::Internal::Util.monotonic_secs
39
+ conn.open_timeout = conn.read_timeout = conn.write_timeout = conn.continue_timeout = timeout
40
+ end
41
+
42
+ # @api private
43
+ #
44
+ # @param request [Hash{Symbol=>Object}] .
45
+ #
46
+ # @option request [Symbol] :method
47
+ #
48
+ # @option request [URI::Generic] :url
49
+ #
50
+ # @option request [Hash{String=>String}] :headers
51
+ #
52
+ # @param blk [Proc]
53
+ #
54
+ # @yieldparam [String]
55
+ # @return [Array(Net::HTTPGenericRequest, Proc)]
56
+ def build_request(request, &blk)
57
+ method, url, headers, body = request.fetch_values(:method, :url, :headers, :body)
58
+ req = Net::HTTPGenericRequest.new(
59
+ method.to_s.upcase,
60
+ !body.nil?,
61
+ method != :head,
62
+ URI(url.to_s) # ensure we construct a URI class of the right scheme
63
+ )
64
+
65
+ headers.each { req[_1] = _2 }
66
+
67
+ case body
68
+ in nil
69
+ nil
70
+ in String
71
+ req["content-length"] ||= body.bytesize.to_s unless req["transfer-encoding"]
72
+ req.body_stream = Pangea::Internal::Util::ReadIOAdapter.new(body, &blk)
73
+ in StringIO
74
+ req["content-length"] ||= body.size.to_s unless req["transfer-encoding"]
75
+ req.body_stream = Pangea::Internal::Util::ReadIOAdapter.new(body, &blk)
76
+ in Pathname | IO | Enumerator
77
+ req["transfer-encoding"] ||= "chunked" unless req["content-length"]
78
+ req.body_stream = Pangea::Internal::Util::ReadIOAdapter.new(body, &blk)
79
+ end
80
+
81
+ [req, req.body_stream&.method(:close)]
82
+ end
83
+ end
84
+
85
+ # @api private
86
+ #
87
+ # @param url [URI::Generic]
88
+ # @param deadline [Float]
89
+ # @param blk [Proc]
90
+ #
91
+ # @raise [Timeout::Error]
92
+ # @yieldparam [Net::HTTP]
93
+ private def with_pool(url, deadline:, &blk)
94
+ origin = Pangea::Internal::Util.uri_origin(url)
95
+ timeout = deadline - Pangea::Internal::Util.monotonic_secs
96
+ pool =
97
+ @mutex.synchronize do
98
+ @pools[origin] ||= ConnectionPool.new(size: @size) do
99
+ self.class.connect(url)
100
+ end
101
+ end
102
+
103
+ pool.with(timeout: timeout, &blk)
104
+ end
105
+
106
+ # @api private
107
+ #
108
+ # @param request [Hash{Symbol=>Object}] .
109
+ #
110
+ # @option request [Symbol] :method
111
+ #
112
+ # @option request [URI::Generic] :url
113
+ #
114
+ # @option request [Hash{String=>String}] :headers
115
+ #
116
+ # @option request [Object] :body
117
+ #
118
+ # @option request [Float] :deadline
119
+ #
120
+ # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
121
+ def execute(request)
122
+ url, deadline = request.fetch_values(:url, :deadline)
123
+
124
+ req = nil
125
+ eof = false
126
+ finished = false
127
+ closing = nil
128
+
129
+ # rubocop:disable Metrics/BlockLength
130
+ enum = Enumerator.new do |y|
131
+ with_pool(url, deadline: deadline) do |conn|
132
+ next if finished
133
+
134
+ req, closing = self.class.build_request(request) do
135
+ self.class.calibrate_socket_timeout(conn, deadline)
136
+ end
137
+
138
+ self.class.calibrate_socket_timeout(conn, deadline)
139
+ unless conn.started?
140
+ conn.keep_alive_timeout = self.class::KEEP_ALIVE_TIMEOUT
141
+ conn.start
142
+ end
143
+
144
+ self.class.calibrate_socket_timeout(conn, deadline)
145
+ conn.request(req) do |rsp|
146
+ y << [conn, req, rsp]
147
+ break if finished
148
+
149
+ rsp.read_body do |bytes|
150
+ y << bytes.force_encoding(Encoding::BINARY)
151
+ break if finished
152
+
153
+ self.class.calibrate_socket_timeout(conn, deadline)
154
+ end
155
+ eof = true
156
+ end
157
+ end
158
+ rescue Timeout::Error
159
+ raise Pangea::Errors::APITimeoutError.new(url: url, request: req)
160
+ rescue StandardError
161
+ raise Pangea::Errors::APIConnectionError.new(url: url, request: req)
162
+ end
163
+ # rubocop:enable Metrics/BlockLength
164
+
165
+ conn, _, response = enum.next
166
+ body = Pangea::Internal::Util.fused_enum(enum, external: true) do
167
+ finished = true
168
+ tap do
169
+ enum.next
170
+ rescue StopIteration
171
+ nil
172
+ end
173
+ ensure
174
+ conn.finish if !eof && conn&.started?
175
+ closing&.call
176
+ end
177
+ [Integer(response.code), response, (response.body = body)]
178
+ end
179
+
180
+ # @api private
181
+ #
182
+ # @param size [Integer]
183
+ def initialize(size: Etc.nprocessors)
184
+ @mutex = Mutex.new
185
+ @size = size
186
+ @pools = {}
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pangea
4
+ module Internal
5
+ module Type
6
+ # @api private
7
+ #
8
+ # @abstract
9
+ #
10
+ # @generic Elem
11
+ #
12
+ # Array of items of a given type.
13
+ class ArrayOf
14
+ include Pangea::Internal::Type::Converter
15
+
16
+ private_class_method :new
17
+
18
+ # @overload [](type_info, spec = {})
19
+ #
20
+ # @param type_info [Hash{Symbol=>Object}, Proc, Pangea::Internal::Type::Converter, Class]
21
+ #
22
+ # @param spec [Hash{Symbol=>Object}] .
23
+ #
24
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
25
+ #
26
+ # @option spec [Proc] :enum
27
+ #
28
+ # @option spec [Proc] :union
29
+ #
30
+ # @option spec [Boolean] :"nil?"
31
+ def self.[](...) = new(...)
32
+
33
+ # @api public
34
+ #
35
+ # @param other [Object]
36
+ #
37
+ # @return [Boolean]
38
+ def ===(other) = other.is_a?(Array) && other.all?(item_type)
39
+
40
+ # @api public
41
+ #
42
+ # @param other [Object]
43
+ #
44
+ # @return [Boolean]
45
+ def ==(other)
46
+ other.is_a?(Pangea::Internal::Type::ArrayOf) && other.nilable? == nilable? && other.item_type == item_type
47
+ end
48
+
49
+ # @api public
50
+ #
51
+ # @return [Integer]
52
+ def hash = [self.class, item_type].hash
53
+
54
+ # @api private
55
+ #
56
+ # @param value [Array<Object>, Object]
57
+ #
58
+ # @param state [Hash{Symbol=>Object}] .
59
+ #
60
+ # @option state [Boolean, :strong] :strictness
61
+ #
62
+ # @option state [Hash{Symbol=>Object}] :exactness
63
+ #
64
+ # @option state [Integer] :branched
65
+ #
66
+ # @return [Array<Object>, Object]
67
+ def coerce(value, state:)
68
+ exactness = state.fetch(:exactness)
69
+
70
+ unless value.is_a?(Array)
71
+ exactness[:no] += 1
72
+ return value
73
+ end
74
+
75
+ target = item_type
76
+ exactness[:yes] += 1
77
+ value
78
+ .map do |item|
79
+ case [nilable?, item]
80
+ in [true, nil]
81
+ exactness[:yes] += 1
82
+ nil
83
+ else
84
+ Pangea::Internal::Type::Converter.coerce(target, item, state: state)
85
+ end
86
+ end
87
+ end
88
+
89
+ # @api private
90
+ #
91
+ # @return [generic<Elem>]
92
+ protected def item_type = @item_type_fn.call
93
+
94
+ # @api private
95
+ #
96
+ # @return [Boolean]
97
+ protected def nilable? = @nilable
98
+
99
+ # @api private
100
+ #
101
+ # @param type_info [Hash{Symbol=>Object}, Proc, Pangea::Internal::Type::Converter, Class]
102
+ #
103
+ # @param spec [Hash{Symbol=>Object}] .
104
+ #
105
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
106
+ #
107
+ # @option spec [Proc] :enum
108
+ #
109
+ # @option spec [Proc] :union
110
+ #
111
+ # @option spec [Boolean] :"nil?"
112
+ def initialize(type_info, spec = {})
113
+ @item_type_fn = Pangea::Internal::Type::Converter.type_info(type_info || spec)
114
+ @nilable = spec.fetch(:nil?, false)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pangea
4
+ module Internal
5
+ module Type
6
+ # @abstract
7
+ class BaseModel
8
+ extend Pangea::Internal::Type::Converter
9
+
10
+ # @param other [Object]
11
+ #
12
+ # @return [Boolean]
13
+ def ==(other) = self.class == other.class && @data == other.to_h
14
+
15
+ # @return [Hash{Symbol=>Object}]
16
+ def to_h = @data
17
+
18
+ alias_method :to_hash, :to_h
19
+
20
+ # @param keys [Array<Symbol>, nil]
21
+ #
22
+ # @return [Hash{Symbol=>Object}]
23
+ def deconstruct_keys(keys)
24
+ (keys || self.class.known_fields.keys)
25
+ .filter_map do |k|
26
+ unless self.class.known_fields.key?(k)
27
+ next
28
+ end
29
+
30
+ [k, public_send(k)]
31
+ end
32
+ .to_h
33
+ end
34
+
35
+ class << self
36
+ # @api private
37
+ #
38
+ # @param value [Pangea::Internal::Type::BaseModel, Hash{Object=>Object}, Object]
39
+ #
40
+ # @param state [Hash{Symbol=>Object}] .
41
+ #
42
+ # @option state [Boolean, :strong] :strictness
43
+ #
44
+ # @option state [Hash{Symbol=>Object}] :exactness
45
+ #
46
+ # @option state [Integer] :branched
47
+ #
48
+ # @return [Pangea::Internal::Type::BaseModel, Object]
49
+ def coerce(value, state:)
50
+ exactness = state.fetch(:exactness)
51
+
52
+ if value.is_a?(self.class)
53
+ exactness[:yes] += 1
54
+ return value
55
+ end
56
+
57
+ unless (val = Pangea::Internal::Util.coerce_hash(value)).is_a?(Hash)
58
+ exactness[:no] += 1
59
+ return value
60
+ end
61
+ exactness[:yes] += 1
62
+
63
+ keys = val.keys.to_set
64
+ instance = new
65
+ data = instance.to_h
66
+
67
+ # rubocop:disable Metrics/BlockLength
68
+ fields.each do |name, field|
69
+ mode, required, target = field.fetch_values(:mode, :required, :type)
70
+ api_name, nilable, const = field.fetch_values(:api_name, :nilable, :const)
71
+
72
+ unless val.key?(api_name)
73
+ if required && mode != :dump && const == Pangea::Internal::OMIT
74
+ exactness[nilable ? :maybe : :no] += 1
75
+ else
76
+ exactness[:yes] += 1
77
+ end
78
+ next
79
+ end
80
+
81
+ item = val.fetch(api_name)
82
+ keys.delete(api_name)
83
+
84
+ converted =
85
+ if item.nil? && (nilable || !required)
86
+ exactness[nilable ? :yes : :maybe] += 1
87
+ nil
88
+ else
89
+ coerced = Pangea::Internal::Type::Converter.coerce(target, item, state: state)
90
+ case target
91
+ in Pangea::Internal::Type::Converter | Symbol
92
+ coerced
93
+ else
94
+ item
95
+ end
96
+ end
97
+ data.store(name, converted)
98
+ end
99
+ # rubocop:enable Metrics/BlockLength
100
+
101
+ keys.each { data.store(_1, val.fetch(_1)) }
102
+ instance
103
+ end
104
+
105
+ # @api private
106
+ #
107
+ # @param value [Pangea::Internal::Type::BaseModel, Object]
108
+ #
109
+ # @param state [Hash{Symbol=>Object}] .
110
+ #
111
+ # @option state [Boolean] :can_retry
112
+ #
113
+ # @return [Hash{Object=>Object}, Object]
114
+ def dump(value, state:)
115
+ unless (coerced = Pangea::Internal::Util.coerce_hash(value)).is_a?(Hash)
116
+ return super
117
+ end
118
+
119
+ acc = {}
120
+
121
+ coerced.each do |key, val|
122
+ name = key.is_a?(String) ? key.to_sym : key
123
+ case (field = known_fields[name])
124
+ in nil
125
+ acc.store(name, super(val, state: state))
126
+ else
127
+ api_name, mode, type_fn = field.fetch_values(:api_name, :mode, :type_fn)
128
+ case mode
129
+ in :coerce
130
+ next
131
+ else
132
+ target = type_fn.call
133
+ acc.store(api_name, Pangea::Internal::Type::Converter.dump(target, val, state: state))
134
+ end
135
+ end
136
+ end
137
+
138
+ known_fields.each_value do |field|
139
+ api_name, mode, const = field.fetch_values(:api_name, :mode, :const)
140
+ next if mode == :coerce || acc.key?(api_name) || const == Pangea::Internal::OMIT
141
+ acc.store(api_name, const)
142
+ end
143
+
144
+ acc
145
+ end
146
+
147
+ # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
148
+ def known_fields
149
+ @known_fields ||= (self < Pangea::Internal::Type::BaseModel ? superclass.known_fields.dup : {})
150
+ end
151
+
152
+ # @api private
153
+ #
154
+ # @return [Hash{Symbol=>Hash{Symbol=>Object}}]
155
+ def fields
156
+ known_fields.transform_values do |field|
157
+ {**field.except(:type_fn), type: field.fetch(:type_fn).call}
158
+ end
159
+ end
160
+
161
+ # @api private
162
+ #
163
+ # @param name_sym [Symbol]
164
+ #
165
+ # @param type_info [Hash{Symbol=>Object}, Proc, Pangea::Internal::Type::Converter, Class]
166
+ #
167
+ # @param spec [Hash{Symbol=>Object}] .
168
+ #
169
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
170
+ #
171
+ # @option spec [Proc] :enum
172
+ #
173
+ # @option spec [Proc] :union
174
+ #
175
+ # @option spec [Boolean] :"nil?"
176
+ def required(name_sym, type_info, spec = {})
177
+ add_field(name_sym, required: true, type_info: type_info, spec: spec)
178
+ end
179
+
180
+ # @api private
181
+ #
182
+ # @param name_sym [Symbol]
183
+ #
184
+ # @param type_info [Hash{Symbol=>Object}, Proc, Pangea::Internal::Type::Converter, Class]
185
+ #
186
+ # @param spec [Hash{Symbol=>Object}] .
187
+ #
188
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
189
+ #
190
+ # @option spec [Proc] :enum
191
+ #
192
+ # @option spec [Proc] :union
193
+ #
194
+ # @option spec [Boolean] :"nil?"
195
+ def optional(name_sym, type_info, spec = {})
196
+ add_field(name_sym, required: false, type_info: type_info, spec: spec)
197
+ end
198
+
199
+ # @api private
200
+ #
201
+ # @param name_sym [Symbol]
202
+ #
203
+ # @param required [Boolean]
204
+ #
205
+ # @param type_info [Hash{Symbol=>Object}, Proc, Pangea::Internal::Type::Converter, Class]
206
+ #
207
+ # @param spec [Hash{Symbol=>Object}] .
208
+ #
209
+ # @option spec [NilClass, TrueClass, FalseClass, Integer, Float, Symbol] :const
210
+ #
211
+ # @option spec [Proc] :enum
212
+ #
213
+ # @option spec [Proc] :union
214
+ #
215
+ # @option spec [Boolean] :"nil?"
216
+ private def add_field(name_sym, required:, type_info:, spec:)
217
+ type_fn, info =
218
+ case type_info
219
+ in Proc | Pangea::Internal::Type::Converter | Class
220
+ [Pangea::Internal::Type::Converter.type_info({**spec, union: type_info}), spec]
221
+ in Hash
222
+ [Pangea::Internal::Type::Converter.type_info(type_info), type_info]
223
+ end
224
+
225
+ setter = "#{name_sym}="
226
+ api_name = info.fetch(:api_name, name_sym)
227
+ nilable = info.fetch(:nil?, false)
228
+ const = required && !nilable ? info.fetch(:const, Pangea::Internal::OMIT) : Pangea::Internal::OMIT
229
+
230
+ [name_sym, setter].each { undef_method(_1) } if known_fields.key?(name_sym)
231
+
232
+ known_fields[name_sym] =
233
+ {
234
+ mode: @mode,
235
+ api_name: api_name,
236
+ required: required,
237
+ nilable: nilable,
238
+ const: const,
239
+ type_fn: type_fn
240
+ }
241
+
242
+ define_method(setter) { @data.store(name_sym, _1) }
243
+
244
+ define_method(name_sym) do
245
+ target = type_fn.call
246
+ value = @data.fetch(name_sym) { const == Pangea::Internal::OMIT ? nil : const }
247
+ state = {strictness: :strong, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
248
+ if (nilable || !required) && value.nil?
249
+ nil
250
+ else
251
+ Pangea::Internal::Type::Converter.coerce(
252
+ target,
253
+ value,
254
+ state: state
255
+ )
256
+ end
257
+ rescue StandardError => e
258
+ cls = self.class.name.split("::").last
259
+ message = [
260
+ "Failed to parse #{cls}.#{__method__} from #{value.class} to #{target.inspect}.",
261
+ "To get the unparsed API response, use #{cls}[#{__method__.inspect}].",
262
+ "Cause: #{e.message}"
263
+ ].join(" ")
264
+ raise Pangea::Errors::ConversionError.new(message)
265
+ end
266
+ end
267
+ end
268
+
269
+ # Create a new instance of a model.
270
+ #
271
+ # @param data [Hash{Symbol=>Object}, Pangea::Internal::Type::BaseModel]
272
+ def initialize(data = {})
273
+ case Pangea::Internal::Util.coerce_hash(data)
274
+ in Hash => coerced
275
+ @data = coerced
276
+ else
277
+ message = "Expected a #{Hash} or #{Pangea::Internal::Type::BaseModel}, got #{data.inspect}"
278
+ raise ArgumentError.new(message)
279
+ end
280
+ end
281
+
282
+ # @api private
283
+ #
284
+ # @return [String]
285
+ def inspect = "#<#{self.class}:0x#{object_id.to_s(16)} #{self}>"
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pangea
4
+ module Internal
5
+ module Type
6
+ # @api private
7
+ class Boolean
8
+ extend Pangea::Internal::Type::Converter
9
+
10
+ private_class_method :new
11
+
12
+ # @param other [Object]
13
+ #
14
+ # @return [Boolean]
15
+ def self.===(other) = other == true || other == false
16
+
17
+ # @param other [Object]
18
+ #
19
+ # @return [Boolean]
20
+ def self.==(other) = other.is_a?(Class) && other <= Pangea::Internal::Type::Boolean
21
+
22
+ class << self
23
+ # @api private
24
+ #
25
+ # @param value [Boolean, Object]
26
+ #
27
+ # @param state [Hash{Symbol=>Object}] .
28
+ #
29
+ # @option state [Boolean, :strong] :strictness
30
+ #
31
+ # @option state [Hash{Symbol=>Object}] :exactness
32
+ #
33
+ # @option state [Integer] :branched
34
+ #
35
+ # @return [Boolean, Object]
36
+ def coerce(value, state:)
37
+ state.fetch(:exactness)[value == true || value == false ? :yes : :no] += 1
38
+ value
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end