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.
- checksums.yaml +7 -0
- data/.ignore +2 -0
- data/README.md +1 -0
- data/lib/pangea/client.rb +51 -0
- data/lib/pangea/errors.rb +113 -0
- data/lib/pangea/internal/transport/base_client.rb +346 -0
- data/lib/pangea/internal/transport/pooled_net_requester.rb +191 -0
- data/lib/pangea/internal/type/array_of.rb +119 -0
- data/lib/pangea/internal/type/base_model.rb +289 -0
- data/lib/pangea/internal/type/boolean.rb +44 -0
- data/lib/pangea/internal/type/converter.rb +228 -0
- data/lib/pangea/internal/type/hash_of.rb +166 -0
- data/lib/pangea/internal/type/request_parameters.rb +38 -0
- data/lib/pangea/internal/type/union.rb +66 -0
- data/lib/pangea/internal/type/unknown.rb +50 -0
- data/lib/pangea/internal/util.rb +429 -0
- data/lib/pangea/internal.rb +12 -0
- data/lib/pangea/models/ai_guard/classification_result.rb +33 -0
- data/lib/pangea/models/ai_guard/hardening_result.rb +27 -0
- data/lib/pangea/models/ai_guard/language_result.rb +20 -0
- data/lib/pangea/models/ai_guard/malicious_entity_result.rb +42 -0
- data/lib/pangea/models/ai_guard/prompt_injection_result.rb +33 -0
- data/lib/pangea/models/ai_guard/redact_entity_result.rb +43 -0
- data/lib/pangea/models/ai_guard/single_entity_result.rb +21 -0
- data/lib/pangea/models/ai_guard/text_guard_message_param.rb +19 -0
- data/lib/pangea/models/ai_guard/text_guard_params.rb +24 -0
- data/lib/pangea/models/ai_guard/text_guard_result.rb +308 -0
- data/lib/pangea/models/ai_guard/topic_result.rb +33 -0
- data/lib/pangea/models/pangea_response.rb +67 -0
- data/lib/pangea/request_options.rb +35 -0
- data/lib/pangea/services/ai_guard.rb +62 -0
- data/lib/pangea/version.rb +5 -0
- data/lib/pangea.rb +45 -0
- data/manifest.yaml +6 -0
- data/rbi/lib/pangea/client.rbi +25 -0
- data/rbi/lib/pangea/internal/internal.rbi +28 -0
- data/rbi/lib/pangea/internal/transport/base_client.rbi +18 -0
- data/rbi/lib/pangea/internal/type/array_of.rbi +66 -0
- data/rbi/lib/pangea/internal/type/base_model.rbi +33 -0
- data/rbi/lib/pangea/internal/type/boolean.rbi +46 -0
- data/rbi/lib/pangea/internal/type/converter.rbi +38 -0
- data/rbi/lib/pangea/internal/type/request_parameters.rbi +20 -0
- data/rbi/lib/pangea/internal/type/union.rbi +21 -0
- data/rbi/lib/pangea/internal/type/unknown.rbi +20 -0
- data/rbi/lib/pangea/internal.rbi +7 -0
- data/rbi/lib/pangea/models/ai_guard/text_guard_message_param.rbi +15 -0
- data/rbi/lib/pangea/models/ai_guard/text_guard_result.rbi +13 -0
- data/rbi/lib/pangea/models/pangea_response.rbi +31 -0
- data/rbi/lib/pangea/request_options.rbi +17 -0
- data/rbi/lib/pangea/services/ai_guard.rbi +28 -0
- data/rbi/lib/pangea/version.rbi +5 -0
- data/sig/pangea/client.rbs +12 -0
- data/sig/pangea/internal/transport/base_client.rbs +14 -0
- data/sig/pangea/internal/type/base_model.rbs +12 -0
- data/sig/pangea/internal/type/boolean.rbs +10 -0
- data/sig/pangea/internal/type/converter.rbs +9 -0
- data/sig/pangea/internal/type/request_parameters.rbs +15 -0
- data/sig/pangea/models/pangea_response.rbs +12 -0
- data/sig/pangea/models/text_guard_result.rbs +19 -0
- data/sig/pangea/request_options.rbs +34 -0
- data/sig/pangea/services/ai_guard.rbs +12 -0
- data/sig/pangea/version.rbs +3 -0
- 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
|