Skip to content

Commit 734833c

Browse files
feat: expose recursive #to_h conversion
1 parent 918ac6b commit 734833c

File tree

3 files changed

+71
-40
lines changed

3 files changed

+71
-40
lines changed

lib/openai/internal/type/base_model.rb

+46-25
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,39 @@ def dump(value, state:)
306306
end
307307
end
308308

309+
class << self
310+
# @api private
311+
#
312+
# @param model [OpenAI::Internal::Type::BaseModel]
313+
# @param convert [Boolean]
314+
#
315+
# @return [Hash{Symbol=>Object}]
316+
def recursively_to_h(model, convert:)
317+
rec = ->(x) do
318+
case x
319+
in OpenAI::Internal::Type::BaseModel
320+
if convert
321+
fields = x.class.known_fields
322+
x.to_h.to_h do |key, val|
323+
[key, rec.call(fields.key?(key) ? x.public_send(key) : val)]
324+
rescue OpenAI::Errors::ConversionError
325+
[key, rec.call(val)]
326+
end
327+
else
328+
rec.call(x.to_h)
329+
end
330+
in Hash
331+
x.transform_values(&rec)
332+
in Array
333+
x.map(&rec)
334+
else
335+
x
336+
end
337+
end
338+
rec.call(model)
339+
end
340+
end
341+
309342
# @api public
310343
#
311344
# Returns the raw value associated with the given key, if found. Otherwise, nil is
@@ -342,6 +375,14 @@ def to_h = @data
342375

343376
alias_method :to_hash, :to_h
344377

378+
# @api public
379+
#
380+
# In addition to the behaviour of `#to_h`, this method will recursively call
381+
# `#to_h` on nested models.
382+
#
383+
# @return [Hash{Symbol=>Object}]
384+
def deep_to_h = self.class.recursively_to_h(@data, convert: false)
385+
345386
# @param keys [Array<Symbol>, nil]
346387
#
347388
# @return [Hash{Symbol=>Object}]
@@ -357,29 +398,6 @@ def deconstruct_keys(keys)
357398
.to_h
358399
end
359400

360-
class << self
361-
# @api private
362-
#
363-
# @param model [OpenAI::Internal::Type::BaseModel]
364-
#
365-
# @return [Hash{Symbol=>Object}]
366-
def walk(model)
367-
walk = ->(x) do
368-
case x
369-
in OpenAI::Internal::Type::BaseModel
370-
walk.call(x.to_h)
371-
in Hash
372-
x.transform_values(&walk)
373-
in Array
374-
x.map(&walk)
375-
else
376-
x
377-
end
378-
end
379-
walk.call(model)
380-
end
381-
end
382-
383401
# @api public
384402
#
385403
# @param a [Object]
@@ -425,12 +443,15 @@ def inspect(depth: 0)
425443
# @api public
426444
#
427445
# @return [String]
428-
def to_s = self.class.walk(@data).to_s
446+
def to_s = deep_to_h.to_s
429447

430448
# @api private
431449
#
432450
# @return [String]
433-
def inspect = "#<#{self.class}:0x#{object_id.to_s(16)} #{self}>"
451+
def inspect
452+
converted = self.class.recursively_to_h(self, convert: true)
453+
"#<#{self.class}:0x#{object_id.to_s(16)} #{converted}>"
454+
end
434455

435456
define_sorbet_constant!(:KnownField) do
436457
T.type_alias { {mode: T.nilable(Symbol), required: T::Boolean, nilable: T::Boolean} }

rbi/openai/internal/type/base_model.rbi

+18-11
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ module OpenAI
192192
end
193193
end
194194

195+
class << self
196+
# @api private
197+
sig do
198+
params(
199+
model: OpenAI::Internal::Type::BaseModel,
200+
convert: T::Boolean
201+
).returns(OpenAI::Internal::AnyHash)
202+
end
203+
def recursively_to_h(model, convert:)
204+
end
205+
end
206+
195207
# Returns the raw value associated with the given key, if found. Otherwise, nil is
196208
# returned.
197209
#
@@ -226,6 +238,12 @@ module OpenAI
226238
def to_hash
227239
end
228240

241+
# In addition to the behaviour of `#to_h`, this method will recursively call
242+
# `#to_h` on nested models.
243+
sig { overridable.returns(OpenAI::Internal::AnyHash) }
244+
def deep_to_h
245+
end
246+
229247
sig do
230248
params(keys: T.nilable(T::Array[Symbol])).returns(
231249
OpenAI::Internal::AnyHash
@@ -234,17 +252,6 @@ module OpenAI
234252
def deconstruct_keys(keys)
235253
end
236254

237-
class << self
238-
# @api private
239-
sig do
240-
params(model: OpenAI::Internal::Type::BaseModel).returns(
241-
OpenAI::Internal::AnyHash
242-
)
243-
end
244-
def walk(model)
245-
end
246-
end
247-
248255
sig { params(a: T.anything).returns(String) }
249256
def to_json(*a)
250257
end

sig/openai/internal/type/base_model.rbs

+7-4
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,20 @@ module OpenAI
6868
state: OpenAI::Internal::Type::Converter::dump_state
6969
) -> (::Hash[top, top] | top)
7070

71+
def self.recursively_to_h: (
72+
OpenAI::Internal::Type::BaseModel model,
73+
convert: bool
74+
) -> ::Hash[Symbol, top]
75+
7176
def []: (Symbol key) -> top?
7277

7378
def to_h: -> ::Hash[Symbol, top]
7479

7580
alias to_hash to_h
7681

77-
def deconstruct_keys: (::Array[Symbol]? keys) -> ::Hash[Symbol, top]
82+
def deep_to_h: -> ::Hash[Symbol, top]
7883

79-
def self.walk: (
80-
OpenAI::Internal::Type::BaseModel model
81-
) -> ::Hash[Symbol, top]
84+
def deconstruct_keys: (::Array[Symbol]? keys) -> ::Hash[Symbol, top]
8285

8386
def to_json: (*top a) -> String
8487

0 commit comments

Comments
 (0)