Skip to content

fix(ruby): forward-declare union classes - #3553

Open
EloB wants to merge 1 commit into
glideapps:masterfrom
EloB:fix/ruby-forward-declare-unions
Open

EloB wants to merge 1 commit into
glideapps:masterfrom
EloB:fix/ruby-forward-declare-unions

Conversation

@EloB

@EloB EloB commented Nov 15, 2026

Copy link
Copy Markdown

Description

RubyRenderer.canBeForwardDeclared now accepts a union that needs a name
(unionNeedsName) as well as a class. Such a union is emitted as a
Dry::Struct class (emitUnion), so it can be forward-declared the same
way: class XElement < Dry::Struct; end.

Related Issue

Fixes #3548

Motivation and Context

In a cycle of classes and unions, only the classes were forward-declared,
and a class could refer to a union class that was declared after it.
test/inputs/schema/recursive-union-flattening.schema generated a file
that fails to load with uninitialized constant X::XElement (NameError).

Previous Behaviour / Output

# (forward declaration)
class XClass < Dry::Struct; end

class X < Dry::Struct
  ...
  attribute :union_array,  Types.Array(Types.Instance(XElement)).optional

and require fails with NameError.

New Behaviour / Output

# (forward declaration)
class XClass < Dry::Struct; end

# (forward declaration)
class XElement < Dry::Struct; end

# (forward declaration)
class X < Dry::Struct; end

The file loads and round-trips recursive-union-flattening.1.json.

Other Ruby output changes too: across the repository's schema inputs,
mutually-recursive.schema, rust-cycle-breaker-union.schema,
union-list.schema and vega-lite.schema get forward declarations for
their union classes, and their types are declared in a different order.
They loaded before and still do. No other language changes.

How Has This Been Tested?

The existing input recursive-union-flattening.schema covers the bug in
the schema-ruby fixture; no new input was needed. CI runs
ruby,schema-ruby-syntax, which only checks the syntax, so it does not
run this case; the full schema-ruby fixture does.

Environment: macOS 26.6 (arm64), Node 24.16.0, Ruby 4.0.6 with the
fixture's locked gems plus bigdecimal and logger (Ruby 4 no longer
loads them by default, and dry-types needs them).

On this branch:

  • npm ci, npm run build, npm run lint (passes), npm run test:unit
    (54 files, 241 tests pass).
  • QUICKTEST=true FIXTURE=ruby,schema-ruby,schema-ruby-syntax npm run test:fixtures:
    all 143 tests pass (54 ruby, 88 schema-ruby, 1 schema-ruby-syntax).

On master (c39af7d):

  • QUICKTEST=true FIXTURE=schema-ruby npm run test:fixtures -- test/inputs/schema/recursive-union-flattening.schema
    fails with uninitialized constant X::XElement (NameError); on this
    branch it passes.

Screenshots (if appropriate):

n/a


Written by Claude (an AI assistant made by Anthropic) and posted on behalf of the account owner (EloB). The reproduction, fix and tests were checked by running them as described above.

🤖 Generated with Claude Code

A class could refer to a union class that was declared after it: in
`recursive-union-flattening.schema`, `class X` has
`Types.Array(Types.Instance(XElement))`, and loading the file failed
with `uninitialized constant X::XElement (NameError)`. Only classes
were forward-declared, but a union that needs a name is emitted as a
`Dry::Struct` class too, so it can be forward-declared the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Ruby output fails to load with NameError when a class refers to a union class declared later

1 participant