rbs_protobuf is a RBS generator for Protocol Buffer messages. It parses .proto files and generates RBS type signature.
It works as a protoc plugin and generates RBSs for protobuf gem. (We plan to support google-protobuf gem too.)
This is an example .proto file.
syntax = "proto2";
package protobuf.example;
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}rbs_protobuf will generate the following RBS file including method definitions for each attribute with correct types.
module Protobuf
module Example
class SearchRequest < ::Protobuf::Message
attr_reader query(): ::String
attr_writer query(): ::String?
attr_reader page_number(): ::Integer
attr_writer page_number(): ::Integer?
attr_reader result_per_page(): ::Integer
attr_writer result_per_page(): ::Integer?
def initialize: (?query: ::String?, ?page_number: ::Integer?, ?result_per_page: ::Integer?) -> void
def []: (:query) -> ::String
| (:page_number) -> ::Integer
| (:result_per_page) -> ::Integer
| (::Symbol) -> untyped
def []=: (:query, ::String?) -> ::String?
| (:page_number, ::Integer?) -> ::Integer?
| (:result_per_page, ::Integer?) -> ::Integer?
| (::Symbol, untyped) -> untyped
end
end
endAnd you can type check your Ruby program using the classes with RBS above. 💪
Add this line to your application's Gemfile:
group :development do
gem 'rbs_protobuf', require: false
endAnd then execute:
$ bundle install
Or install it yourself as:
$ gem install rbs_protobuf
Run protoc with --rbs_out option.
$ RBS_PROTOBUF_BACKEND=protobuf protoc --rbs_out=sig/protos protos/a.proto
You may need bundle exec protoc ... to let bundler set up PATH.
RBS_PROTOBUF_BACKENDspecifies the Ruby code generator gem. Supported value isprotobuf. (We will addgoogle-protobufforgoogle-protobufgem.)PB_UPCASE_ENUMSis forprotobufgem support. Specify the environment variable to make enum value constants upper case.RBS_PROTOBUF_NO_NESTED_NAMESPACEis to make the RBS declarations flat.RBS_PROTOBUF_EXTENSIONspecifies what to do for extensions.RBS_PROTOBUF_ACCEPT_NIL_ATTR_WRITERis to allow passingnilto required fields.RBS_PROTOBUF_FILTERScontains filter Ruby script paths separated byFile::PATH_SEPARATORRBS_PROTOBUF_CONCAT_LEVELcontains the number of dir levels that groups generated RBS files to concat
To type check the output, make sure your type checker configuration loads type definitions of protobuf gem.
# Declare in Gemfile and load it with rbs-collection
gem 'protobuf'We assume that you don't type check the generated .pb.rb code.
If you want to type check them, you need the definition of Google::Protobuf, which can be generated from descriptor.proto.
| Protocol Buffer Feature | Support for protobuf gem |
|---|---|
| Messages | ✓ |
| Enums | ✓ |
| Packages | ✓ |
| Nested messages | ✓ |
| Maps | ✓ |
| Extensions | Read next section |
| Services | Only generates classes |
| Oneof | No support in protobuf gem |
Adding extensions may cause problems if the name of new attribute conflicts.
extend SearchRequest {
// This extension defines an attribute.
optional string option = 100;
}
extend SearchRequest {
// Another extension defines another attribute with same name.
optional string option = 101;
}In this case, defining two option attributes in RBS causes an error.
So, rbs_protobuf allows ignoring extensions for this case.
You can control the behavior with RBS_PROTOBUF_EXTENSION environment variable.
false: Ignores extensions.print: Prints RBS for extensions instead of writing them to files. You can copy or modify the printed RBS, and put them in some RBS files.- Any value else: Generates RBS for extensions.
- undefined: Ignores extensions but print messages to ask you to specify a value.
You can apply filters that modifies generated RBS files.
A filter is a proc object with type of ^(String rbs_name, String rbs_content, untyped proto_file) -> [String, String]:
It receives the file name of RBS file, the content of RBS file, the source protobuf object, and returns a pair of RBS file name and content.
# example_fitler.rb: It adds a warning comment at the top of the RBS content.
->(rbs_name, rbs_content, _proto_file) {
[
rbs_name,
"# Don't modify this file. This is generated with rbs_protobuf.\n\n" + rbs_content
]
}You can apply filters by setting RBS_PROTOBUF_FILTERS environment variable.
$ RBS_PROTOBUF_BACKEND=protobuf RBS_PROTOBUF_FILTERS=example_filter.rb protoc ...
After checking out the repo, run bin/setup to install dependencies. Then, run rake test example:typecheck to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
The gem works as a plugin of protoc command, so protoc command should be available for development.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/square/rbs_protobuf.
The gem is available as open source under the terms of the MIT License.