Skip to content

Code Generation

Paul Welch edited this page May 30, 2023 · 4 revisions

Twirp services are defined with a DSL in Ruby. But that DSL can be autogenerated from the Proto file. This is good because whenever the service definition is modified in the Proto file, you can just re-generate the service definition in Ruby with a single command. However, there are a few steps to properly setup code-generation.

Install tools for code-generation

protoc

Protocol Buffers v3 provides the protoc command that is used to auto-generate code.

Protoc is able to read .proto files and generate the message parsers in multiple languages, including Ruby (using the --ruby_out option). But it can not generate Twirp services and clients.

Twirp-Ruby Plugin

Twirp-Ruby includes a Golang plugin to allow protoc generate .twirp.rb files.

Install Golang: https://golang.org/doc/install (in Mac you can also do brew install go).

Then use go get to install the twirp_ruby protoc plugin:

go get github.com/arthurnn/twirp-ruby/protoc-gen-twirp_ruby

Code Generation

Once protoc and the twirp_ruby plugins are installed, you can generate messages and service definitions with the command:

protoc --proto_path=. --ruby_out=. --twirp_ruby_out=. ./path/to/service.proto

Example

Given a .proto file hello_world/service.proto

syntax = "proto3";
package example;

service HelloWorld {
    rpc Hello(HelloRequest) returns (HelloResponse);
}

message HelloRequest {
    string name = 1;
}

message HelloResponse {
    string message = 1;
}

Run protoc pointing to the .proto file:

protoc --proto_path=. --ruby_out=. --twirp_ruby_out=. ./hello_world/service.proto

That will generate two files in the same folder.

Message Definitions:

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: hello_world/service.proto

require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_message "example.HelloRequest" do
    optional :name, :string, 1
  end
  add_message "example.HelloResponse" do
    optional :message, :string, 1
  end
end

module Example
  HelloRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("example.HelloRequest").msgclass
  HelloResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("example.HelloResponse").msgclass
end

Twirp Service:

# Code generated by protoc-gen-twirp_ruby, DO NOT EDIT.
require 'twirp'

module Example
  class HelloWorldService < Twirp::Service
    package "example"
    service "HelloWorld"
    rpc :Hello, HelloRequest, HelloResponse, :ruby_method => :hello
  end

  class HelloWorldClient < Twirp::Client
    client_for HelloWorldService
  end
end