Skip to content

feat: generated-service layer (GenericService) + grpc_tools_ruby_protoc codegen - #3

Merged
tannevaled merged 1 commit into
mainfrom
feat/generic-service-codegen
Jul 27, 2026
Merged

feat: generated-service layer (GenericService) + grpc_tools_ruby_protoc codegen#3
tannevaled merged 1 commit into
mainfrom
feat/generic-service-codegen

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Closes the service-codegen surface of Ruby's grpc gem on top of the existing pure-Go runtime (RpcServer / ClientStub / ActiveCall were already ported). Two halves:

GenericService — the generated Service base (GRPC::GenericService)

  • NewGenericService(name) + RPC(RpcDesc{...}) mirror self.service_name = ... and the rpc :Name, In, Out class macro; RpcDescs, LookupRPC, chainable declaration, duplicate-replace.
  • BuildService(Handlers) pairs the declarations with handlers into a runtime Service for RpcServer.Handle (mirrors implementing the generated subclass' instance methods).
  • StubClass(*ClientStub) derives the client *GenericStub — the gem's rpc_stub_class. The generated stub carries each rpc's marshal/unmarshal, so a caller supplies only the request(s): RequestResponse / ClientStreamer / ServerStreamer / BidiStreamer.

GenerateRubyServices — the grpc_tools_ruby_protoc equivalent

Given a .proto's service block (ServiceFile/ServiceGen/MethodGen), emits the exact *_services_pb.rb source byte-for-byte as the gem's generator: unary + all streaming shapes (stream(...)), multiple services per file, dotted & underscored packages, nested and cross-package message types, and package-less files.

gRPC surface implemented vs residual

server client
unary ✅ (BuildService → Handle) ✅ (GenericStub.RequestResponse)
client-streaming
server-streaming
bidi-streaming

Codegen: ✅ server base + Stub, all cardinalities, byte-faithful.

Residual (named): message-.proto parsing / message codegen stay in go-ruby-protobuf (runtime descriptor builder); a Ruby constant for a nested type imported from another package is approximated (same-package and flat cross-package are byte-exact); TLS/ChannelCredentials, xDS, channelz, and health/reflection services are follow-ups.

Interop / differential oracle

  • Codegen: the real grpc_tools_ruby_protoc (grpc-tools gem) is run per-.proto and our output is asserted equal to the byte; skip-gated when the gem is absent, with inline goldens pinning the format either way. Verified live against grpc-tools 1.83 (helloworld unary + route_guide all-streaming).
  • Runtime: a GenericStub calls a stock google.golang.org/grpc server over the in-memory transport with real protobuf messages — wire interop, not asserted shape.

House rules

CGO=0, go 1.26.4, BSD-3 + SPDX headers, gofmt + go vet clean, 100% coverage, -race clean, builds green on amd64/arm64/riscv64/loong64/ppc64le/s390x + js/wasm + wasip1/wasm.

🤖 Generated with Claude Code

…oc codegen

Close the service-codegen surface of the grpc gem on top of the existing
pure-Go runtime.

GenericService ports GRPC::GenericService — the mixin a generated
*_services_pb.rb Service base class includes: NewGenericService(name) + RPC(...)
(the `rpc :Name, In, Out` macro), BuildService pairing declarations with
handlers into a runtime Service to Handle, and StubClass deriving the client
GenericStub (the gem's rpc_stub_class). The generated stub carries each rpc's
marshal/unmarshal, so a caller supplies only the request(s); all four
cardinalities are covered and driven end-to-end over the in-memory transport,
plus a wire-interop oracle where a GenericStub calls a stock
google.golang.org/grpc server with real protobuf messages.

GenerateRubyServices is the grpc_tools_ruby_protoc equivalent: given a .proto's
service block it emits the exact *_services_pb.rb source — byte-for-byte as the
gem's generator — covering unary + all streaming shapes (stream(...)), multiple
services per file, dotted/underscored packages, nested and cross-package message
types, and package-less files. A differential oracle runs the real
grpc_tools_ruby_protoc (grpc-tools gem) and asserts byte-equality, skip-gated
when the gem is absent; inline goldens pin the format either way.

CGO=0, gofmt + go vet clean, 100% coverage, -race clean, and green across the
six 64-bit targets + js/wasm + wasip1/wasm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit befa80f into main Jul 27, 2026
11 checks passed
@tannevaled
tannevaled deleted the feat/generic-service-codegen branch July 27, 2026 14:33
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.

1 participant