twirp 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|