twirp 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +78 -0
- data/examples/Gemfile +7 -0
- data/examples/Gemfile.lock +17 -0
- data/examples/config.ru +12 -0
- data/examples/gen/haberdasher_pb.rb +18 -0
- data/examples/gen/haberdasher_twirp.rb +7 -0
- data/examples/haberdasher.proto +14 -0
- data/lib/twirp.rb +1 -0
- data/lib/twirp/{server.rb → service.rb} +3 -3
- data/lib/twirp/version.rb +1 -1
- data/protoc-gen-twirp_ruby/main.go +62 -3
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2de989bef622a106065d7cad486cd575cc10d783
|
4
|
+
data.tar.gz: 5e01d8fae314ab0f4c53e64f9ede154aef54eafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 609f958dec7ba47552c36bd54a96aa7404e602f56ed669e22401deccf9e4568852beac96d09fe40cf81bf42bc601c811d6165fc1a2b1d67c6b92fa1412c26307
|
7
|
+
data.tar.gz: a7414cf878277e142d4b52a1492f03f8e37bfe71fe82ff1046c29199cd9405a5e8e59f4be13b26ecb69ed0d9d689eef98d5378da93ea6fbfa6e365ada542709f
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/README.md
CHANGED
@@ -1,3 +1,81 @@
|
|
1
1
|
# Ruby Twirp
|
2
2
|
|
3
3
|
Twirp services and clients in Ruby.
|
4
|
+
|
5
|
+
### Installation
|
6
|
+
Use `go get` to install the ruby_twirp protoc plugin:
|
7
|
+
```
|
8
|
+
➜ go get github.com/cyrusaf/ruby-twirp/protoc-gen-twirp_ruby
|
9
|
+
```
|
10
|
+
|
11
|
+
You will also need:
|
12
|
+
- [protoc](https://github.com/golang/protobuf), the protobuf compiler. You need
|
13
|
+
version 3+.
|
14
|
+
|
15
|
+
### Haberdasher Example
|
16
|
+
See the `examples/` folder for the final product.
|
17
|
+
|
18
|
+
First create a basic `.proto` file:
|
19
|
+
```
|
20
|
+
// haberdasher.proto
|
21
|
+
syntax = "proto3";
|
22
|
+
package examples;
|
23
|
+
|
24
|
+
service Haberdasher {
|
25
|
+
rpc HelloWorld(HelloWorldRequest) returns (HelloWorldResponse);
|
26
|
+
}
|
27
|
+
|
28
|
+
message HelloWorldRequest {
|
29
|
+
string name = 1;
|
30
|
+
}
|
31
|
+
|
32
|
+
message HelloWorldResponse {
|
33
|
+
string message = 1;
|
34
|
+
}
|
35
|
+
|
36
|
+
```
|
37
|
+
|
38
|
+
Run the `protoc` binary to generate `gen/haberdasher_pb.rb` and `gen/haberdasher_twirp.rb`.
|
39
|
+
```
|
40
|
+
➜ protoc --proto_path=. ./haberdasher.proto --ruby_out=gen --twirp_ruby_out=gen
|
41
|
+
```
|
42
|
+
|
43
|
+
Write an implementation of our haberdasher service and attach to a rack server:
|
44
|
+
```
|
45
|
+
# config.ru
|
46
|
+
require 'rack'
|
47
|
+
require_relative 'gen/haberdasher_pb.rb'
|
48
|
+
require_relative 'gen/haberdasher_twirp.rb'
|
49
|
+
|
50
|
+
class HaberdasherImplementation
|
51
|
+
def HelloWorld(req)
|
52
|
+
return Examples::HelloWorldResponse.new(message: "Hello #{req.name}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
svc = HaberdasherImplementation.new()
|
57
|
+
run Rack::URLMap.new HaberdasherService::PATH_PREFIX => HaberdasherService.new(svc).handler
|
58
|
+
```
|
59
|
+
|
60
|
+
You can also mount onto a rails service:
|
61
|
+
```
|
62
|
+
App::Application.routes.draw do
|
63
|
+
svc = HaberdasherImplementation.new()
|
64
|
+
mount HaberdasherServer.new(svc).handler, at: HaberdasherServer::PATH_PREFIX
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
Run `config.ru` to start the server on port 8080:
|
69
|
+
```
|
70
|
+
➜ rackup
|
71
|
+
```
|
72
|
+
|
73
|
+
`curl` your server to get a response:
|
74
|
+
```
|
75
|
+
➜ curl --request POST \
|
76
|
+
--url http://localhost:8080/twirp/examples.Haberdasher/HelloWorld \
|
77
|
+
--header 'content-type: application/json' \
|
78
|
+
--data '{
|
79
|
+
"name": "World"
|
80
|
+
}'
|
81
|
+
```
|
data/examples/Gemfile
ADDED
data/examples/config.ru
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require_relative 'gen/haberdasher_pb.rb'
|
3
|
+
require_relative 'gen/haberdasher_twirp.rb'
|
4
|
+
|
5
|
+
class HaberdasherImplementation
|
6
|
+
def HelloWorld(req)
|
7
|
+
return Examples::HelloWorldResponse.new(message: "Hello #{req.name}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
svc = HaberdasherImplementation.new()
|
12
|
+
run Rack::URLMap.new HaberdasherService::PATH_PREFIX => HaberdasherService.new(svc).handler
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# source: haberdasher.proto
|
3
|
+
|
4
|
+
require 'google/protobuf'
|
5
|
+
|
6
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
+
add_message "examples.HelloWorldRequest" do
|
8
|
+
optional :name, :string, 1
|
9
|
+
end
|
10
|
+
add_message "examples.HelloWorldResponse" do
|
11
|
+
optional :message, :string, 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Examples
|
16
|
+
HelloWorldRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.HelloWorldRequest").msgclass
|
17
|
+
HelloWorldResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("examples.HelloWorldResponse").msgclass
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
syntax = "proto3";
|
2
|
+
package examples;
|
3
|
+
|
4
|
+
service Haberdasher {
|
5
|
+
rpc HelloWorld(HelloWorldRequest) returns (HelloWorldResponse);
|
6
|
+
}
|
7
|
+
|
8
|
+
message HelloWorldRequest {
|
9
|
+
string name = 1;
|
10
|
+
}
|
11
|
+
|
12
|
+
message HelloWorldResponse {
|
13
|
+
string message = 1;
|
14
|
+
}
|
data/lib/twirp.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Twirp
|
2
|
-
class
|
2
|
+
class Service
|
3
3
|
@@rpcs = {}
|
4
4
|
|
5
5
|
def initialize(svc)
|
@@ -34,13 +34,13 @@ module Twirp
|
|
34
34
|
|
35
35
|
def serve_json(req, method_name, request_class, response_class)
|
36
36
|
params = request_class.decode_json(req.body.read)
|
37
|
-
resp = @svc.send(method_name
|
37
|
+
resp = @svc.send(method_name, params)
|
38
38
|
self.serve_success_json(response_class.encode_json(resp))
|
39
39
|
end
|
40
40
|
|
41
41
|
def serve_proto(req, method_name, request_class, response_class)
|
42
42
|
params = request_type.decode(req.body.read)
|
43
|
-
resp = @svc.send(method_name
|
43
|
+
resp = @svc.send(method_name, params)
|
44
44
|
self.serve_success_proto(response_class.encode(resp))
|
45
45
|
end
|
46
46
|
|
data/lib/twirp/version.rb
CHANGED
@@ -72,14 +72,15 @@ func (g *generator) generateFile(file *descriptor.FileDescriptorProto) *plugin.C
|
|
72
72
|
g.P(`# Code generated by protoc-gen-twirp_ruby, DO NOT EDIT.`)
|
73
73
|
for _, service := range file.Service {
|
74
74
|
serviceName := serviceName(service)
|
75
|
+
g.P(`require 'twirp'`)
|
75
76
|
g.P(``)
|
76
|
-
g.P(fmt.Sprintf("class %
|
77
|
-
g.P(fmt.Sprintf(`PATH_PREFIX = "/twirp/%s.%s"`, pkgName, serviceName))
|
77
|
+
g.P(fmt.Sprintf("class %sService < Twirp::Service", serviceName))
|
78
|
+
g.P(fmt.Sprintf(` PATH_PREFIX = "/twirp/%s.%s"`, pkgName, serviceName))
|
78
79
|
for _, method := range service.GetMethod() {
|
79
80
|
methName := methodName(method)
|
80
81
|
inputName := methodInputName(method)
|
81
82
|
outputName := methodOutputName(method)
|
82
|
-
g.P(fmt.Sprintf(" rpc :%s, %s, %s", methName, inputName, outputName))
|
83
|
+
g.P(fmt.Sprintf(" rpc :%s, %s::%s, %s::%s", methName, CamelCase(pkgName), inputName, CamelCase(pkgName), outputName))
|
83
84
|
}
|
84
85
|
g.P(`end`)
|
85
86
|
}
|
@@ -183,3 +184,61 @@ func writeResponse(w io.Writer, resp *plugin.CodeGeneratorResponse) {
|
|
183
184
|
Fail(err.Error(), "writing response")
|
184
185
|
}
|
185
186
|
}
|
187
|
+
|
188
|
+
// CamelCase converts a string from snake_case to CamelCased.
|
189
|
+
//
|
190
|
+
// If there is an interior underscore followed by a lower case letter, drop the
|
191
|
+
// underscore and convert the letter to upper case. There is a remote
|
192
|
+
// possibility of this rewrite causing a name collision, but it's so remote
|
193
|
+
// we're prepared to pretend it's nonexistent - since the C++ generator
|
194
|
+
// lowercases names, it's extremely unlikely to have two fields with different
|
195
|
+
// capitalizations. In short, _my_field_name_2 becomes XMyFieldName_2.
|
196
|
+
func CamelCase(s string) string {
|
197
|
+
if s == "" {
|
198
|
+
return ""
|
199
|
+
}
|
200
|
+
t := make([]byte, 0, 32)
|
201
|
+
i := 0
|
202
|
+
if s[0] == '_' {
|
203
|
+
// Need a capital letter; drop the '_'.
|
204
|
+
t = append(t, 'X')
|
205
|
+
i++
|
206
|
+
}
|
207
|
+
// Invariant: if the next letter is lower case, it must be converted
|
208
|
+
// to upper case.
|
209
|
+
//
|
210
|
+
// That is, we process a word at a time, where words are marked by _ or upper
|
211
|
+
// case letter. Digits are treated as words.
|
212
|
+
for ; i < len(s); i++ {
|
213
|
+
c := s[i]
|
214
|
+
if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
|
215
|
+
continue // Skip the underscore in s.
|
216
|
+
}
|
217
|
+
if isASCIIDigit(c) {
|
218
|
+
t = append(t, c)
|
219
|
+
continue
|
220
|
+
}
|
221
|
+
// Assume we have a letter now - if not, it's a bogus identifier. The next
|
222
|
+
// word is a sequence of characters that must start upper case.
|
223
|
+
if isASCIILower(c) {
|
224
|
+
c ^= ' ' // Make it a capital letter.
|
225
|
+
}
|
226
|
+
t = append(t, c) // Guaranteed not lower case.
|
227
|
+
// Accept lower case sequence that follows.
|
228
|
+
for i+1 < len(s) && isASCIILower(s[i+1]) {
|
229
|
+
i++
|
230
|
+
t = append(t, s[i])
|
231
|
+
}
|
232
|
+
}
|
233
|
+
return string(t)
|
234
|
+
}
|
235
|
+
|
236
|
+
// Is c an ASCII lower-case letter?
|
237
|
+
func isASCIILower(c byte) bool {
|
238
|
+
return 'a' <= c && c <= 'z'
|
239
|
+
}
|
240
|
+
|
241
|
+
// Is c an ASCII digit?
|
242
|
+
func isASCIIDigit(c byte) bool {
|
243
|
+
return '0' <= c && c <= '9'
|
244
|
+
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyrus A. Forbes
|
@@ -48,12 +48,19 @@ executables: []
|
|
48
48
|
extensions: []
|
49
49
|
extra_rdoc_files: []
|
50
50
|
files:
|
51
|
+
- ".gitignore"
|
51
52
|
- Gemfile
|
52
53
|
- Gemfile.lock
|
53
54
|
- README.md
|
55
|
+
- examples/Gemfile
|
56
|
+
- examples/Gemfile.lock
|
57
|
+
- examples/config.ru
|
58
|
+
- examples/gen/haberdasher_pb.rb
|
59
|
+
- examples/gen/haberdasher_twirp.rb
|
60
|
+
- examples/haberdasher.proto
|
54
61
|
- lib/twirp.rb
|
55
62
|
- lib/twirp/error.rb
|
56
|
-
- lib/twirp/
|
63
|
+
- lib/twirp/service.rb
|
57
64
|
- lib/twirp/version.rb
|
58
65
|
- protoc-gen-twirp_ruby/main.go
|
59
66
|
- test/error_test.rb
|