Skip to content

feat(ruby): add clientDefault support to Ruby SDK generator#15445

Merged
Swimburger merged 3 commits intomainfrom
devin/1777324222-ruby-client-default
Apr 27, 2026
Merged

feat(ruby): add clientDefault support to Ruby SDK generator#15445
Swimburger merged 3 commits intomainfrom
devin/1777324222-ruby-client-default

Conversation

@Swimburger
Copy link
Copy Markdown
Member

@Swimburger Swimburger commented Apr 27, 2026

Description

Implements clientDefault support in the Ruby-v2 SDK generator, matching the behavior already implemented in TypeScript (PR #15216), Python (PR #14440), Go (PR #15215), and Java (PR #15214).

When a header, query parameter, or path parameter has a clientDefault value in the IR (from x-fern-default OpenAPI extension or client-default Fern Definition field), the generated Ruby SDK uses that value as a fallback when the caller doesn't provide one.

Also fixes root service endpoint generation — APIs with only a root service (no subpackages) now correctly generate client.rb with endpoint methods.

Changes Made

  • DefaultValueExtractor.ts: Added extractClientDefault() method that extracts a Ruby code string from a Literal value (string or boolean), with proper string escaping
  • WrappedEndpointRequest.ts: Updated query parameter and header code blocks to check clientDefault on each parameter via params.fetch(:key, default)
  • HttpEndpointGenerator.ts: Updated getPathParameterReferences() to use clientDefault as fallback for path parameters via params.fetch(:name, default)
  • RootClientGenerator.ts: Added global header parameters with clientDefault to the root client constructor (precedence: caller > env var > clientDefault). Also added root service endpoint method generation directly on the root client class.
  • SdkGeneratorCli.ts: Moved root client generation outside the subpackages loop so it's always generated. Added root service request generation.
  • SdkGeneratorContext.ts: Added getPackageForServiceId() and isRootService() helpers so root services work in path/module resolution
  • WrappedRequestGenerator.ts: Uses getPackageForServiceId() instead of getSubpackageForServiceId() to support root service requests
  • Added unreleased changelog entries for both ruby-v2 and ruby generators

Testing

  • Compilation passes (pnpm compile --filter @fern-api/ruby-sdk...)
  • Seed tests pass: x-fern-default, exhaustive, imdb, version
  • x-fern-default seed output now includes client.rb with clientDefault values applied:
    • params.fetch(:limit, "100") — query param clientDefault
    • params.fetch(:region, "us-east-1") — path param clientDefault
    • api_version: "2024-02-08" — global header clientDefault as constructor param
  • No regressions on existing fixtures (exhaustive, imdb only have require ordering change)

Link to Devin session: https://app.devin.ai/sessions/91f742878e6249fab63239e8f46b4fb7
Requested by: @Swimburger


Open in Devin Review

Support x-fern-default as fallback value for parameters in generated Ruby SDKs.
When a header, query parameter, or path parameter has a clientDefault value
in the IR, the generated Ruby SDK uses that value as a fallback when the
caller doesn't provide one.

Changes:
- DefaultValueExtractor: Add extractClientDefault() for Literal values
- WrappedEndpointRequest: Use clientDefault for query params and headers
- HttpEndpointGenerator: Use clientDefault for path parameters
- RootClientGenerator: Add global header params with clientDefault support

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-04-23T04:59:11Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ruby-sdk-v2 square 130s (n=5) 159s (n=5) 131s +1s (+0.8%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-23T04:59:11Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-27 22:09 UTC

Swimburger and others added 2 commits April 27, 2026 21:42
Using || treats false/nil as falsy and would override explicit values.
fetch only falls back when the key is absent, consistent with query
params and headers.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Previously, the root client was only generated inside the subpackages
loop. APIs with only a root service (no subpackages) wouldn't get a
client.rb generated. Now the root client is always generated, and root
service endpoints are included directly on the root client class.

Also adds getPackageForServiceId/isRootService helpers to handle root
services in path/module resolution.

Seed snapshot changes:
- x-fern-default: new client.rb with clientDefault values applied
- exhaustive/imdb: require ordering change (root client generated last)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 8 additional findings in Devin Review.

Open in Devin Review

Comment on lines +36 to +49
// Add root service endpoint methods directly on the root client
const rootServiceId = this.context.ir.rootPackage.service;
if (rootServiceId != null) {
const rootService = this.context.getHttpServiceOrThrow(rootServiceId);
for (const endpoint of rootService.endpoints) {
const generatedMethods = this.context.endpointGenerator.generate({
endpoint,
serviceId: rootServiceId,
rawClientReference: "",
rawClient: new RawClient(this.context)
});
class_.addStatements(generatedMethods);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Root service endpoint methods reference @client but root client stores raw client as @raw_client

The newly added root service endpoint generation in RootClientGenerator adds HTTP endpoint methods directly to the root client class. However, these generated methods reference @client (hardcoded in HttpEndpointGenerator.generateRequestProcedure at generators/ruby-v2/sdk/src/endpoint/http/HttpEndpointGenerator.ts:304), while the root client's initialize method stores the raw client as @raw_client (generators/ruby-v2/sdk/src/root-client/RootClientGenerator.ts:194). In contrast, SubPackageClientGenerator stores it as @client (generators/ruby-v2/sdk/src/subpackage-client/SubPackageClientGenerator.ts:72), so subpackage endpoints work fine. Any call to a root service endpoint method will fail at runtime with a Ruby NoMethodError because @client is nil on the root client instance.

Prompt for agents
The root service endpoint methods are generated by HttpEndpointGenerator which hardcodes @client as the instance variable for the raw HTTP client (see HttpEndpointGenerator.ts line 304: `response = @client.send(request)`). However, RootClientGenerator stores the raw client as @raw_client (line 194). SubPackageClientGenerator stores it as @client (line 72), which is why subpackage endpoints work.

To fix this, either:
1. Add an `@client = @raw_client` alias assignment in the root client's initialize method so the generated endpoint code can find it via @client, OR
2. Modify HttpEndpointGenerator to accept the client variable name as a parameter and use it when generating the send call, OR
3. Keep @raw_client as is for the subclient getter pattern, but also assign @client = @raw_client so endpoint methods work.

Option 1 is simplest: in RootClientGenerator.getInitializeMethod(), after the @raw_client assignment, add a statement like `@client = @raw_client`.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in follow-up PR #15466. Added @client = @raw_client alias in the root client's initialize method (gated on rootPackage.service != null) so the generated endpoint methods can find the HTTP client via @client.

@Swimburger Swimburger enabled auto-merge (squash) April 27, 2026 22:06
@Swimburger Swimburger merged commit e8f6d3f into main Apr 27, 2026
71 checks passed
@Swimburger Swimburger deleted the devin/1777324222-ruby-client-default branch April 27, 2026 22:10
devin-ai-integration Bot added a commit that referenced this pull request Apr 28, 2026
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants