feat(ruby): add clientDefault support to Ruby SDK generator#15445
feat(ruby): add clientDefault support to Ruby SDK generator#15445Swimburger merged 3 commits intomainfrom
Conversation
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 EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
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.
SDK Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on Full benchmark table (click to expand)
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 |
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>
| // 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
🔴 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`.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Description
Implements
clientDefaultsupport 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
clientDefaultvalue in the IR (fromx-fern-defaultOpenAPI extension orclient-defaultFern 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.rbwith endpoint methods.Changes Made
DefaultValueExtractor.ts: AddedextractClientDefault()method that extracts a Ruby code string from aLiteralvalue (string or boolean), with proper string escapingWrappedEndpointRequest.ts: Updated query parameter and header code blocks to checkclientDefaulton each parameter viaparams.fetch(:key, default)HttpEndpointGenerator.ts: UpdatedgetPathParameterReferences()to useclientDefaultas fallback for path parameters viaparams.fetch(:name, default)RootClientGenerator.ts: Added global header parameters withclientDefaultto 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: AddedgetPackageForServiceId()andisRootService()helpers so root services work in path/module resolutionWrappedRequestGenerator.ts: UsesgetPackageForServiceId()instead ofgetSubpackageForServiceId()to support root service requestsTesting
pnpm compile --filter @fern-api/ruby-sdk...)x-fern-default,exhaustive,imdb,versionclient.rbwith clientDefault values applied:params.fetch(:limit, "100")— query param clientDefaultparams.fetch(:region, "us-east-1")— path param clientDefaultapi_version: "2024-02-08"— global header clientDefault as constructor paramLink to Devin session: https://app.devin.ai/sessions/91f742878e6249fab63239e8f46b4fb7
Requested by: @Swimburger