[ruby] fix: build_from_hash drops every attribute inherited from an allOf parent
A discriminator-based allOf child generates as a subclass (class Cat < Animal), but its build_from_hash maps only the child's own attributes:
- a required inherited attribute makes deserialization raise;
- an optional inherited attribute silently comes back
nil.
Petstore::Cat.build_from_hash({ 'className' => 'Cat', 'color' => 'black', 'declawed' => true })
# master: ArgumentError: class_name cannot be nil
# this PR: #<Petstore::Cat class_name="Cat", color="black", declawed=true>
to_hash has the mirror defect: a round-tripped child loses its parent's attributes on the way out. Reproduced on current master.
Why super(attributes) didn't help
openapi_types and attribute_map dispatch on the child class even in the parent's frame, so the super call only built a second instance from the child's own attributes and discarded it. It is removed.
Fix
partial_model_generic.mustache adds two class methods that use the same idiom as the existing acceptable_attribute_map (the nearest declaration wins):
def self.acceptable_openapi_types # without a generated parent: openapi_types
superclass.acceptable_openapi_types.merge(openapi_types)
end
def self.acceptable_openapi_nullable # without a generated parent: openapi_nullable
(superclass.acceptable_openapi_nullable - attribute_map.keys) | openapi_nullable
end
The delegation to superclass is gated on {{#parentModel}} (a generated parent), not {{#parent}}. This covers these helpers, acceptable_attribute_map and the initializer's super(attributes). An alias model such as ArrayAlias < Array has a parent but no generated parent, so every model gets a base case; array_alias.rb in the alias-as-model sample is the only file this gate changes.
In base_object.mustache, build_from_hash iterates acceptable_openapi_types and looks keys up in acceptable_attribute_map, the map the generated initializer validates against, so new only receives attributes it accepts. to_hash iterates acceptable_attribute_map and checks acceptable_openapi_nullable. A consumer subclass that narrows attribute_map behaves as on master.
Tests
RubyClientCodegenTest#testBuildFromHashMapsInheritedAttributesgeneratesLizard < Reptile < Petfrom3_0/allOf_composition_discriminator.yamland asserts the merge methods, their use inbuild_from_hash/to_hash, and that the discardedsuper(attributes)is gone. Fails without the fix.RubyClientCodegenTest#testAliasModelDoesNotDelegateToItsBuiltInSuperclassgenerates3_0/features/generate-alias-as-model.yamland asserts thatArrayAlias < Arrayhas base-case helpers and nosuperclass/super(attributes)delegation.spec/custom/base_object_spec.rbin the typhoeus, faraday and httpx petstore samples: aCatround trip that keepsclassName/color, and a regression guard for a subclass that narrowsattribute_map.
PR checklist
- Read the contribution guidelines.
- Built the project and updated samples (
./bin/generate-samples.sh ./bin/configs/ruby*.yaml). Every ruby-client model changes, so the sample diff is broad but mechanical.ruby-nextgenis unaffected. - Technical committee: @cliffano @zlx @autopp
Summary by cubic
Fixes build_from_hash and to_hash in the Ruby client dropping attributes a model inherits from its allOf parents. A requirement bug where the attribute is:
- A discriminator has its parent's attributes (fixed and a parent's approach)
child < parentraisesbuild_class:
Petstore::Cat.build_from_hash({ 'className' => 'Cat', 'color' => 'black', 'declawed' => true })
# master: ArgumentError: class_name cannot be nil # this PR: #<Petstore::Cat class_name="Cat", color="black", declawed=true>
This approach adds two class methods with the same parent-gated idiom as the existing acceptable_attribute_map (stable attribute for classes whose superclass is parentModel); the children).---
Generated with Claude Code