Skip to content

Commit da71413

Browse files
fix(model)!: base model should recursively store coerced base models (#165)
1 parent 8b4921d commit da71413

File tree

4 files changed

+44
-2
lines changed

4 files changed

+44
-2
lines changed

lib/orb/base_model.rb

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,13 @@ def known_fields
913913
@known_fields ||= (self < Orb::BaseModel ? superclass.known_fields.dup : {})
914914
end
915915

916+
# @api private
917+
#
918+
# @return [Hash{Symbol=>Symbol}]
919+
def reverse_map
920+
@reverse_map ||= (self < Orb::BaseModel ? superclass.reverse_map.dup : {})
921+
end
922+
916923
# @api private
917924
#
918925
# @return [Hash{Symbol=>Hash{Symbol=>Object}}]
@@ -956,7 +963,7 @@ def defaults = (@defaults ||= {})
956963
fallback = info[:const]
957964
defaults[name_sym] = fallback if required && !info[:nil?] && info.key?(:const)
958965

959-
key = info.fetch(:api_name, name_sym)
966+
key = info[:api_name]&.tap { reverse_map[_1] = name_sym } || name_sym
960967
setter = "#{name_sym}="
961968

962969
if known_fields.key?(name_sym)
@@ -1213,7 +1220,21 @@ def deconstruct_keys(keys)
12131220
def initialize(data = {})
12141221
case Orb::Util.coerce_hash(data)
12151222
in Hash => coerced
1216-
@data = coerced.transform_keys(&:to_sym)
1223+
@data = coerced.to_h do |key, value|
1224+
name = key.to_sym
1225+
mapped = self.class.reverse_map.fetch(name, name)
1226+
type = self.class.fields[mapped]&.fetch(:type)
1227+
stored =
1228+
case [type, value]
1229+
in [Class, Hash] if type <= Orb::BaseModel
1230+
type.new(value)
1231+
in [Orb::ArrayOf, Array] | [Orb::HashOf, Hash]
1232+
type.coerce(value)
1233+
else
1234+
value
1235+
end
1236+
[name, stored]
1237+
end
12171238
else
12181239
raise ArgumentError.new("Expected a #{Hash} or #{Orb::BaseModel}, got #{data.inspect}")
12191240
end

rbi/lib/orb/base_model.rbi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ module Orb
454454
def known_fields
455455
end
456456

457+
# @api private
458+
sig { returns(T::Hash[Symbol, Symbol]) }
459+
def reverse_map
460+
end
461+
457462
# @api private
458463
sig { returns(T::Hash[Symbol, T.all(Orb::BaseModel::KnownFieldShape, {type: Orb::Converter::Input})]) }
459464
def fields

sig/orb/base_model.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ module Orb
173173
def self.known_fields: -> ::Hash[Symbol, (Orb::BaseModel::known_field
174174
& { type_fn: (^-> Orb::Converter::input) })]
175175

176+
def self.reverse_map: -> ::Hash[Symbol, Symbol]
177+
176178
def self.fields: -> ::Hash[Symbol, (Orb::BaseModel::known_field
177179
& { type: Orb::Converter::input })]
178180

test/orb/base_model_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,20 @@ def test_nested_model_dump
222222
end
223223
end
224224

225+
class M4 < M2
226+
required :c, M1
227+
required :d, Orb::ArrayOf[M4]
228+
required :e, M2, api_name: :f
229+
end
230+
231+
def test_model_to_h
232+
model = M4.new(a: "wow", c: {}, d: [{}, 2, {c: {}}], f: {})
233+
assert_pattern do
234+
model.to_h => {a: "wow", c: M1, d: [M4, 2, M4 => child], f: M2}
235+
assert_equal({c: M1.new}, child.to_h)
236+
end
237+
end
238+
225239
A3 = Orb::ArrayOf[A1]
226240

227241
class M3 < M1

0 commit comments

Comments
 (0)