Skip to content

Commit

Permalink
Remove parser support from executable and update README
Browse files Browse the repository at this point in the history
  • Loading branch information
davebenvenuti committed Jan 13, 2025
1 parent ee3afc3 commit 246d26b
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 104 deletions.
94 changes: 5 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,106 +24,22 @@ $ bundle

## Compiling .proto Files

You can compile your own `.proto` files to generate importable `.rb` files:
`protoboeuf` expects a compiled binary protobuf file. These can be generated with the `protoc` command line tool that
comes with the protobuf library.

```
$ protoc -I examples test.proto -o test.bproto
$ protoboeuf -b test.bproto > test.rb
$ protoboeuf test.bproto > test.rb
```

The above will produce a `.rb` file with Ruby classes representing each message type.

You can also generate Ruby that contains Sorbet types by using the `-t` flag like this:

```
$ protoboeuf -t -b test.bproto > test.rb
$ protoboeuf -t test.bproto > test.rb
```

## Generating Code with ProtoBoeuf AST

The `protoc` command line tool can parse a `.proto` file and output a binary representation of the `.proto` file's abstract syntax tree.
ProtoBoeuf ships with classes built for loading these binary files.
This means you can combine `protoc` and ProtoBoeuf to write any kind of code generator you'd like based on what's inside the `.proto` file.

Given an input `.proto` file like this:

```
syntax = "proto3";
package test_app;
message HelloReq {
string name = 1;
}
message HelloResp {
string name = 1;
}
service HelloWorld {
rpc Hello(HelloReq) returns (HelloResp);
}
```

We can write code to process the AST and produce a very basic client:

```ruby
require "protoboeuf/protobuf/descriptor"

fds = ProtoBoeuf::Protobuf::FileDescriptorSet.decode(File.binread(ARGV[0]))
fds.file.each do |file|
file.service.each do |service|
puts "class #{service.name}"
service.method.each do |method|
input_klass = method.input_type.split(".").last
output_klass = method.output_type.split(".").last

puts <<-EORB
# expects #{input_klass}
def #{method.name.downcase}(input)
http = Net::HTTP.new(\"example.com\")
request = Net::HTTP::Post.new(\"/#{service.name}\")
req.content_type = 'application/protobuf'
data = #{input_klass}.encode(input)
request.body = data
response = http.request(request)
#{output_klass}.decode response.body
end
EORB

end
puts "end"
puts
end
end
```

First we convert the `.proto` file to a binary version like this (assuming the file name is `server.proto`):

```
$ protoc -I . server.proto -o server.bproto
```

Then we can use our program to load the binary version of the proto file and produce a simple client:

```
$ ruby -I lib test.rb server.bproto
class HelloWorld
# expects HelloReq
def hello(input)
http = Net::HTTP.new("example.com")
request = Net::HTTP::Post.new("/HelloWorld")
req.content_type = 'application/protobuf'
data = HelloReq.encode(input)
request.body = data
response = http.request(request)
HelloResp.decode response.body
end
end
```

The code you can generate is only limited by your imagination!

## Running tests

Run all tests like this:
Expand All @@ -146,7 +62,7 @@ bundle exec ruby -I lib:test test/message_test.rb -n test_decoding

## Generated files

Ruby files under `lib/protoboeuf/protobuf/` are generated from the `.proto` files in the same directory.
Ruby files under `lib/protoboeuf/protobuf/google` are generated from the `.proto` files in any subdirectories.
The same is true for `test/fixtures`.

For example, currently `test/fixtures/test_pb.rb`
Expand Down
19 changes: 4 additions & 15 deletions exe/protoboeuf
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,23 @@
# frozen_string_literal: true

require "protoboeuf/codegen"
require "protoboeuf/parser"
require "optparse"

generate_types = false
binary = false

op = OptionParser.new do |opts|
opts.banner = "Usage: protoboeuf file.proto"
opts.banner = "Usage: protoboeuf file.bproto"
opts.on("-t", "--typed", "Generate sorbet types") { generate_types = true }
opts.on("-b", "--bin", "Read binary proto file") { binary = true }
end
op.parse!

def parse_binary(file)
require "protoboeuf/protobuf/descriptor"
ProtoBoeuf::Protobuf::FileDescriptorSet.decode(File.binread(file))
end

def parse_file(file)
ProtoBoeuf.parse_file(file)
require "protoboeuf/google/protobuf/descriptor"
ProtoBoeuf::Google::Protobuf::FileDescriptorSet.decode(File.binread(file))
end

if ARGV[0] && File.exist?(ARGV[0])
unit = if binary
parse_binary(ARGV[0])
else
parse_file(ARGV[0])
end
unit = parse_binary(ARGV[0])

gen = ProtoBoeuf::CodeGen.new(unit, generate_types:)
puts gen.to_ruby
Expand Down

0 comments on commit 246d26b

Please sign in to comment.