beefcake 0.3.1 → 0.3.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.
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/README.md +1 -1
- data/Rakefile +2 -0
- data/beefcake.gemspec +21 -0
- data/bench/simple.rb +90 -0
- data/bin/protoc-gen-beefcake +1 -286
- data/dat/code_generator_request.dat +0 -0
- data/lib/beefcake/generator.rb +288 -0
- data/lib/beefcake/version.rb +3 -0
- data/test/generator_test.rb +51 -0
- metadata +26 -34
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -104,7 +104,6 @@ Currently Beefcake is tested and working on:
|
|
104
104
|
|
105
105
|
Nice to have:
|
106
106
|
|
107
|
-
* `.proto` -> Ruby generator
|
108
107
|
* Groups (would be nice for accessing older protos)
|
109
108
|
|
110
109
|
# Further Reading
|
@@ -113,4 +112,5 @@ http://code.google.com/apis/protocolbuffers/docs/encoding.html
|
|
113
112
|
|
114
113
|
# Thank You
|
115
114
|
|
115
|
+
Keith Rarick (kr) for help with encoding/decoding.
|
116
116
|
Aman Gupta (tmm1) for help with cross VM support and performance enhancements.
|
data/Rakefile
ADDED
data/beefcake.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "beefcake/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "beefcake"
|
7
|
+
s.version = Beefcake::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Blake Mizerany"]
|
10
|
+
s.email = ["blake.mizerany@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A sane protobuf library for Ruby}
|
13
|
+
s.description = %q{A sane protobuf library for Ruby}
|
14
|
+
|
15
|
+
s.rubyforge_project = "beefcake"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
data/bench/simple.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'beefcake'
|
3
|
+
|
4
|
+
class MyMessage
|
5
|
+
include Beefcake::Message
|
6
|
+
required :number, :int32, 1
|
7
|
+
required :chars, :string, 2
|
8
|
+
required :raw, :bytes, 3
|
9
|
+
required :bool, :bool, 4
|
10
|
+
required :float, :float, 5
|
11
|
+
end
|
12
|
+
|
13
|
+
if ARGV[0] == 'pprof'
|
14
|
+
# profile message creation/encoding/decoding w/ perftools.rb
|
15
|
+
# works on 1.8 and 1.9
|
16
|
+
# ruby bench/simple.rb pprof
|
17
|
+
# open bench/beefcake.prof.gif
|
18
|
+
|
19
|
+
ENV['CPUPROFILE_FREQUENCY'] = '4000'
|
20
|
+
require 'rubygems'
|
21
|
+
require 'perftools'
|
22
|
+
PerfTools::CpuProfiler.start(File.expand_path("../beefcake.prof", __FILE__)) do
|
23
|
+
100_000.times do
|
24
|
+
str = MyMessage.new(
|
25
|
+
:number => 12345,
|
26
|
+
:chars => 'hello',
|
27
|
+
:raw => 'world',
|
28
|
+
:bool => true,
|
29
|
+
:float => 1.2345
|
30
|
+
).encode
|
31
|
+
MyMessage.decode(str)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Dir.chdir(File.dirname(__FILE__)) do
|
35
|
+
`pprof.rb beefcake.prof --gif > beefcake.prof.gif`
|
36
|
+
end
|
37
|
+
|
38
|
+
else
|
39
|
+
# benchmark message creation/encoding/decoding
|
40
|
+
# rvm install 1.8.7 1.9.2 jruby rbx
|
41
|
+
# rvm 1.8.7,1.9.2,jruby,rbx ruby bench/simple.rb
|
42
|
+
|
43
|
+
require 'benchmark'
|
44
|
+
|
45
|
+
ITERS = 100_000
|
46
|
+
|
47
|
+
Benchmark.bmbm do |x|
|
48
|
+
x.report 'object creation' do
|
49
|
+
ITERS.times do
|
50
|
+
Object.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
x.report 'message creation' do
|
54
|
+
ITERS.times do
|
55
|
+
MyMessage.new(
|
56
|
+
:number => 12345,
|
57
|
+
:chars => 'hello',
|
58
|
+
:raw => 'world',
|
59
|
+
:bool => true,
|
60
|
+
:float => 1.2345
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
x.report 'message encoding' do
|
65
|
+
m = MyMessage.new(
|
66
|
+
:number => 12345,
|
67
|
+
:chars => 'hello',
|
68
|
+
:raw => 'world',
|
69
|
+
:bool => true,
|
70
|
+
:float => 1.2345
|
71
|
+
)
|
72
|
+
ITERS.times do
|
73
|
+
m.encode
|
74
|
+
end
|
75
|
+
end
|
76
|
+
x.report 'message decoding' do
|
77
|
+
str = MyMessage.new(
|
78
|
+
:number => 12345,
|
79
|
+
:chars => 'hello',
|
80
|
+
:raw => 'world',
|
81
|
+
:bool => true,
|
82
|
+
:float => 1.2345
|
83
|
+
).encode.to_s
|
84
|
+
ITERS.times do
|
85
|
+
MyMessage.decode(str.dup)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/bin/protoc-gen-beefcake
CHANGED
@@ -1,291 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
|
-
|
4
|
-
require 'beefcake'
|
5
|
-
require 'stringio'
|
6
|
-
|
7
|
-
|
8
|
-
class CodeGeneratorRequest
|
9
|
-
include Beefcake::Message
|
10
|
-
|
11
|
-
|
12
|
-
class FieldDescriptorProto
|
13
|
-
include Beefcake::Message
|
14
|
-
|
15
|
-
module Type
|
16
|
-
## 0 is reserved for errors.
|
17
|
-
## Order is weird for historical reasons.
|
18
|
-
TYPE_DOUBLE = 1
|
19
|
-
TYPE_FLOAT = 2
|
20
|
-
TYPE_INT64 = 3 ## Not ZigZag encoded. Negative numbers
|
21
|
-
## take 10 bytes. Use TYPE_SINT64 if negative
|
22
|
-
## values are likely.
|
23
|
-
TYPE_UINT64 = 4
|
24
|
-
TYPE_INT32 = 5 ## Not ZigZag encoded. Negative numbers
|
25
|
-
## take 10 bytes. Use TYPE_SINT32 if negative
|
26
|
-
## values are likely.
|
27
|
-
TYPE_FIXED64 = 6
|
28
|
-
TYPE_FIXED32 = 7
|
29
|
-
TYPE_BOOL = 8
|
30
|
-
TYPE_STRING = 9
|
31
|
-
TYPE_GROUP = 10 ## Tag-delimited aggregate.
|
32
|
-
TYPE_MESSAGE = 11 ## Length-delimited aggregate.
|
33
|
-
|
34
|
-
## New in version 2.
|
35
|
-
TYPE_BYTES = 12
|
36
|
-
TYPE_UINT32 = 13
|
37
|
-
TYPE_ENUM = 14
|
38
|
-
TYPE_SFIXED32 = 15
|
39
|
-
TYPE_SFIXED64 = 16
|
40
|
-
TYPE_SINT32 = 17 ## Uses ZigZag encoding.
|
41
|
-
TYPE_SINT64 = 18 ## Uses ZigZag encoding.
|
42
|
-
end
|
43
|
-
|
44
|
-
module Label
|
45
|
-
LABEL_OPTIONAL = 1
|
46
|
-
LABEL_REQUIRED = 2
|
47
|
-
LABEL_REPEATED = 3
|
48
|
-
end
|
49
|
-
|
50
|
-
optional :name, :string, 1
|
51
|
-
optional :number, :int32, 3
|
52
|
-
optional :label, Label, 4
|
53
|
-
|
54
|
-
## If type_name is set, this need not be set. If both this and type_name
|
55
|
-
## are set, this must be either TYPE_ENUM or TYPE_MESSAGE.
|
56
|
-
optional :type, Type, 5
|
57
|
-
|
58
|
-
## For message and enum types, this is the name of the type. If the name
|
59
|
-
## starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
|
60
|
-
## rules are used to find the type (i.e. first the nested types within this
|
61
|
-
## message are searched, then within the parent, on up to the root
|
62
|
-
## namespace).
|
63
|
-
optional :type_name, :string, 6
|
64
|
-
|
65
|
-
## For extensions, this is the name of the type being extended. It is
|
66
|
-
## resolved in the same manner as type_name.
|
67
|
-
optional :extended, :string, 2
|
68
|
-
|
69
|
-
## For numeric types, contains the original text representation of the value.
|
70
|
-
## For booleans, "true" or "false".
|
71
|
-
## For strings, contains the default text contents (not escaped in any way).
|
72
|
-
## For bytes, contains the C escaped value. All bytes >= 128 are escaped.
|
73
|
-
optional :default_value, :string, 7
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
class EnumValueDescriptorProto
|
78
|
-
include Beefcake::Message
|
79
|
-
|
80
|
-
optional :name, :string, 1
|
81
|
-
optional :number, :int32, 2
|
82
|
-
# optional EnumValueOptions options = 3;
|
83
|
-
end
|
84
|
-
|
85
|
-
class EnumDescriptorProto
|
86
|
-
include Beefcake::Message
|
87
|
-
|
88
|
-
optional :name, :string, 1
|
89
|
-
repeated :value, EnumValueDescriptorProto, 2
|
90
|
-
# optional :options, EnumOptions, 3
|
91
|
-
end
|
92
|
-
|
93
|
-
class DescriptorProto
|
94
|
-
include Beefcake::Message
|
95
|
-
|
96
|
-
optional :name, :string, 1
|
97
|
-
|
98
|
-
repeated :field, FieldDescriptorProto, 2
|
99
|
-
repeated :extended, FieldDescriptorProto, 6
|
100
|
-
repeated :nested_type, DescriptorProto, 3
|
101
|
-
repeated :enum_type, EnumDescriptorProto, 4
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
class FileDescriptorProto
|
106
|
-
include Beefcake::Message
|
107
|
-
|
108
|
-
optional :name, :string, 1 # file name, relative to root of source tree
|
109
|
-
optional :package, :string, 2 # e.g. "foo", "foo.bar", etc.
|
110
|
-
|
111
|
-
repeated :message_type, DescriptorProto, 4;
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
|
-
repeated :file_to_generate, :string, 1
|
116
|
-
optional :parameter, :string, 2
|
117
|
-
|
118
|
-
repeated :proto_file, FileDescriptorProto, 15
|
119
|
-
end
|
120
|
-
|
121
|
-
class CodeGeneratorResponse
|
122
|
-
include Beefcake::Message
|
123
|
-
|
124
|
-
class File
|
125
|
-
include Beefcake::Message
|
126
|
-
|
127
|
-
optional :name, :string, 1
|
128
|
-
optional :content, :string, 15
|
129
|
-
end
|
130
|
-
|
131
|
-
repeated :file, File, 15
|
132
|
-
end
|
133
|
-
|
134
|
-
|
135
|
-
module Beefcake
|
136
|
-
class Generator
|
137
|
-
|
138
|
-
L = CodeGeneratorRequest::FieldDescriptorProto::Label
|
139
|
-
T = CodeGeneratorRequest::FieldDescriptorProto::Type
|
140
|
-
|
141
|
-
|
142
|
-
def self.compile(ns, req)
|
143
|
-
file = req.proto_file.map do |file|
|
144
|
-
g = new(StringIO.new)
|
145
|
-
g.compile(ns, file)
|
146
|
-
|
147
|
-
g.c.rewind
|
148
|
-
CodeGeneratorResponse::File.new(
|
149
|
-
:name => File.basename(file.name, ".proto") + ".pb.rb",
|
150
|
-
:content => g.c.read
|
151
|
-
)
|
152
|
-
end
|
153
|
-
|
154
|
-
CodeGeneratorResponse.new(:file => file)
|
155
|
-
end
|
156
|
-
|
157
|
-
attr_reader :c
|
158
|
-
|
159
|
-
def initialize(c)
|
160
|
-
@c = c
|
161
|
-
@n = 0
|
162
|
-
end
|
163
|
-
|
164
|
-
def file!(file)
|
165
|
-
puts "## Generated from #{file.name} for #{file.package}"
|
166
|
-
|
167
|
-
file.message_type.each do |mt|
|
168
|
-
message!(mt)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def indent(&blk)
|
173
|
-
@n += 1
|
174
|
-
blk.call
|
175
|
-
@n -= 1
|
176
|
-
end
|
177
|
-
|
178
|
-
def indent!(n)
|
179
|
-
@n = n
|
180
|
-
end
|
181
|
-
|
182
|
-
def message!(pkg, mt)
|
183
|
-
puts
|
184
|
-
puts "class #{mt.name}"
|
185
|
-
|
186
|
-
indent do
|
187
|
-
puts "include Beefcake::Message"
|
188
|
-
puts
|
189
|
-
|
190
|
-
mt.enum_type.each do |et|
|
191
|
-
enum!(et)
|
192
|
-
end
|
193
|
-
|
194
|
-
## Generate Types
|
195
|
-
(mt.nested_type || []).each do |nt|
|
196
|
-
message!(nt)
|
197
|
-
end
|
198
|
-
puts
|
199
|
-
|
200
|
-
## Generate fields
|
201
|
-
(mt.field || []).each do |f|
|
202
|
-
field!(pkg, f)
|
203
|
-
end
|
204
|
-
puts
|
205
|
-
end
|
206
|
-
|
207
|
-
puts "end"
|
208
|
-
end
|
209
|
-
|
210
|
-
def enum!(et)
|
211
|
-
puts "module #{et.name}"
|
212
|
-
indent do
|
213
|
-
et.value.each do |v|
|
214
|
-
puts "%s = %d" % [v.name, v.number]
|
215
|
-
end
|
216
|
-
end
|
217
|
-
puts "end"
|
218
|
-
end
|
219
|
-
|
220
|
-
def field!(pkg, f)
|
221
|
-
# Turn the label into Ruby
|
222
|
-
label = name_for(f, L, f.label)
|
223
|
-
|
224
|
-
# Turn the name into a Ruby
|
225
|
-
name = ":#{f.name}"
|
226
|
-
|
227
|
-
# Determine the type-name and convert to Ruby
|
228
|
-
type = if f.type_name
|
229
|
-
# We have a type_name so we will use it after converting to a
|
230
|
-
# Ruby friendly version
|
231
|
-
t = f.type_name
|
232
|
-
t = t.gsub(pkg, "") # Remove the leading package name
|
233
|
-
t = t.gsub(/^\.*/, "") # Remove leading `.`s
|
234
|
-
|
235
|
-
t.gsub(".", "::") # Convert to Ruby namespacing syntax
|
236
|
-
else
|
237
|
-
":#{name_for(f, T, f.type)}"
|
238
|
-
end
|
239
|
-
|
240
|
-
# Finally, generate the declaration
|
241
|
-
out = "%s %s, %s, %d" % [label, name, type, f.number]
|
242
|
-
|
243
|
-
if f.default_value
|
244
|
-
out += ", :default => #{f.default_value}"
|
245
|
-
end
|
246
|
-
|
247
|
-
puts out
|
248
|
-
end
|
249
|
-
|
250
|
-
# Determines the name for a
|
251
|
-
def name_for(b, mod, val)
|
252
|
-
b.name_for(mod, val).gsub(/.*_/, "").downcase
|
253
|
-
end
|
254
|
-
|
255
|
-
def compile(ns, file)
|
256
|
-
puts "## Generated from #{file.name} for #{file.package}"
|
257
|
-
puts "require \"beefcake\""
|
258
|
-
puts
|
259
|
-
|
260
|
-
ns!(*ns) do
|
261
|
-
file.message_type.each do |mt|
|
262
|
-
message!(file.package, mt)
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def ns!(n, *tail, &blk)
|
268
|
-
puts "module #{n}"
|
269
|
-
indent do
|
270
|
-
if tail.empty?
|
271
|
-
blk.call
|
272
|
-
else
|
273
|
-
ns!(*tail, &blk)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
puts "end"
|
277
|
-
end
|
278
|
-
|
279
|
-
def puts(msg=nil)
|
280
|
-
if msg
|
281
|
-
c.puts((" " * @n) + msg)
|
282
|
-
else
|
283
|
-
c.puts
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
end
|
288
|
-
end
|
3
|
+
require 'beefcake/generator'
|
289
4
|
|
290
5
|
ns = (ENV["BEEFCAKE_NAMESPACE"] || "").split("::")
|
291
6
|
|
Binary file
|
@@ -0,0 +1,288 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# The above line allows concatenation of constant strings like ".pb.rb" to
|
3
|
+
# maintain the internal format of the buffers, rather than converting the
|
4
|
+
# buffer to US-ASCII
|
5
|
+
|
6
|
+
require 'beefcake'
|
7
|
+
require 'stringio'
|
8
|
+
|
9
|
+
class CodeGeneratorRequest
|
10
|
+
include Beefcake::Message
|
11
|
+
|
12
|
+
|
13
|
+
class FieldDescriptorProto
|
14
|
+
include Beefcake::Message
|
15
|
+
|
16
|
+
module Type
|
17
|
+
## 0 is reserved for errors.
|
18
|
+
## Order is weird for historical reasons.
|
19
|
+
TYPE_DOUBLE = 1
|
20
|
+
TYPE_FLOAT = 2
|
21
|
+
TYPE_INT64 = 3 ## Not ZigZag encoded. Negative numbers
|
22
|
+
## take 10 bytes. Use TYPE_SINT64 if negative
|
23
|
+
## values are likely.
|
24
|
+
TYPE_UINT64 = 4
|
25
|
+
TYPE_INT32 = 5 ## Not ZigZag encoded. Negative numbers
|
26
|
+
## take 10 bytes. Use TYPE_SINT32 if negative
|
27
|
+
## values are likely.
|
28
|
+
TYPE_FIXED64 = 6
|
29
|
+
TYPE_FIXED32 = 7
|
30
|
+
TYPE_BOOL = 8
|
31
|
+
TYPE_STRING = 9
|
32
|
+
TYPE_GROUP = 10 ## Tag-delimited aggregate.
|
33
|
+
TYPE_MESSAGE = 11 ## Length-delimited aggregate.
|
34
|
+
|
35
|
+
## New in version 2.
|
36
|
+
TYPE_BYTES = 12
|
37
|
+
TYPE_UINT32 = 13
|
38
|
+
TYPE_ENUM = 14
|
39
|
+
TYPE_SFIXED32 = 15
|
40
|
+
TYPE_SFIXED64 = 16
|
41
|
+
TYPE_SINT32 = 17 ## Uses ZigZag encoding.
|
42
|
+
TYPE_SINT64 = 18 ## Uses ZigZag encoding.
|
43
|
+
end
|
44
|
+
|
45
|
+
module Label
|
46
|
+
LABEL_OPTIONAL = 1
|
47
|
+
LABEL_REQUIRED = 2
|
48
|
+
LABEL_REPEATED = 3
|
49
|
+
end
|
50
|
+
|
51
|
+
optional :name, :string, 1
|
52
|
+
optional :number, :int32, 3
|
53
|
+
optional :label, Label, 4
|
54
|
+
|
55
|
+
## If type_name is set, this need not be set. If both this and type_name
|
56
|
+
## are set, this must be either TYPE_ENUM or TYPE_MESSAGE.
|
57
|
+
optional :type, Type, 5
|
58
|
+
|
59
|
+
## For message and enum types, this is the name of the type. If the name
|
60
|
+
## starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
|
61
|
+
## rules are used to find the type (i.e. first the nested types within this
|
62
|
+
## message are searched, then within the parent, on up to the root
|
63
|
+
## namespace).
|
64
|
+
optional :type_name, :string, 6
|
65
|
+
|
66
|
+
## For extensions, this is the name of the type being extended. It is
|
67
|
+
## resolved in the same manner as type_name.
|
68
|
+
optional :extended, :string, 2
|
69
|
+
|
70
|
+
## For numeric types, contains the original text representation of the value.
|
71
|
+
## For booleans, "true" or "false".
|
72
|
+
## For strings, contains the default text contents (not escaped in any way).
|
73
|
+
## For bytes, contains the C escaped value. All bytes >= 128 are escaped.
|
74
|
+
optional :default_value, :string, 7
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
class EnumValueDescriptorProto
|
79
|
+
include Beefcake::Message
|
80
|
+
|
81
|
+
optional :name, :string, 1
|
82
|
+
optional :number, :int32, 2
|
83
|
+
# optional EnumValueOptions options = 3;
|
84
|
+
end
|
85
|
+
|
86
|
+
class EnumDescriptorProto
|
87
|
+
include Beefcake::Message
|
88
|
+
|
89
|
+
optional :name, :string, 1
|
90
|
+
repeated :value, EnumValueDescriptorProto, 2
|
91
|
+
# optional :options, EnumOptions, 3
|
92
|
+
end
|
93
|
+
|
94
|
+
class DescriptorProto
|
95
|
+
include Beefcake::Message
|
96
|
+
|
97
|
+
optional :name, :string, 1
|
98
|
+
|
99
|
+
repeated :field, FieldDescriptorProto, 2
|
100
|
+
repeated :extended, FieldDescriptorProto, 6
|
101
|
+
repeated :nested_type, DescriptorProto, 3
|
102
|
+
repeated :enum_type, EnumDescriptorProto, 4
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
class FileDescriptorProto
|
107
|
+
include Beefcake::Message
|
108
|
+
|
109
|
+
optional :name, :string, 1 # file name, relative to root of source tree
|
110
|
+
optional :package, :string, 2 # e.g. "foo", "foo.bar", etc.
|
111
|
+
|
112
|
+
repeated :message_type, DescriptorProto, 4;
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
repeated :file_to_generate, :string, 1
|
117
|
+
optional :parameter, :string, 2
|
118
|
+
|
119
|
+
repeated :proto_file, FileDescriptorProto, 15
|
120
|
+
end
|
121
|
+
|
122
|
+
class CodeGeneratorResponse
|
123
|
+
include Beefcake::Message
|
124
|
+
|
125
|
+
class File
|
126
|
+
include Beefcake::Message
|
127
|
+
|
128
|
+
optional :name, :string, 1
|
129
|
+
optional :content, :string, 15
|
130
|
+
end
|
131
|
+
|
132
|
+
repeated :file, File, 15
|
133
|
+
end
|
134
|
+
|
135
|
+
module Beefcake
|
136
|
+
class Generator
|
137
|
+
|
138
|
+
L = CodeGeneratorRequest::FieldDescriptorProto::Label
|
139
|
+
T = CodeGeneratorRequest::FieldDescriptorProto::Type
|
140
|
+
|
141
|
+
|
142
|
+
def self.compile(ns, req)
|
143
|
+
file = req.proto_file.map do |file|
|
144
|
+
g = new(StringIO.new)
|
145
|
+
g.compile(ns, file)
|
146
|
+
|
147
|
+
g.c.rewind
|
148
|
+
CodeGeneratorResponse::File.new(
|
149
|
+
:name => File.basename(file.name, ".proto") + ".pb.rb",
|
150
|
+
:content => g.c.read
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
CodeGeneratorResponse.new(:file => file)
|
155
|
+
end
|
156
|
+
|
157
|
+
attr_reader :c
|
158
|
+
|
159
|
+
def initialize(c)
|
160
|
+
@c = c
|
161
|
+
@n = 0
|
162
|
+
end
|
163
|
+
|
164
|
+
def file!(file)
|
165
|
+
puts "## Generated from #{file.name} for #{file.package}"
|
166
|
+
|
167
|
+
file.message_type.each do |mt|
|
168
|
+
message!("", mt)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def indent(&blk)
|
173
|
+
@n += 1
|
174
|
+
blk.call
|
175
|
+
@n -= 1
|
176
|
+
end
|
177
|
+
|
178
|
+
def indent!(n)
|
179
|
+
@n = n
|
180
|
+
end
|
181
|
+
|
182
|
+
def message!(pkg, mt)
|
183
|
+
puts
|
184
|
+
puts "class #{mt.name}"
|
185
|
+
|
186
|
+
indent do
|
187
|
+
puts "include Beefcake::Message"
|
188
|
+
puts
|
189
|
+
|
190
|
+
Array(mt.enum_type).each do |et|
|
191
|
+
enum!(et)
|
192
|
+
end
|
193
|
+
|
194
|
+
## Generate Types
|
195
|
+
Array(mt.nested_type).each do |nt|
|
196
|
+
message!(pkg, nt)
|
197
|
+
end
|
198
|
+
puts
|
199
|
+
|
200
|
+
## Generate fields
|
201
|
+
Array(mt.field).each do |f|
|
202
|
+
field!(pkg, f)
|
203
|
+
end
|
204
|
+
puts
|
205
|
+
end
|
206
|
+
|
207
|
+
puts "end"
|
208
|
+
end
|
209
|
+
|
210
|
+
def enum!(et)
|
211
|
+
puts "module #{et.name}"
|
212
|
+
indent do
|
213
|
+
et.value.each do |v|
|
214
|
+
puts "%s = %d" % [v.name, v.number]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
puts "end"
|
218
|
+
end
|
219
|
+
|
220
|
+
def field!(pkg, f)
|
221
|
+
# Turn the label into Ruby
|
222
|
+
label = name_for(f, L, f.label)
|
223
|
+
|
224
|
+
# Turn the name into a Ruby
|
225
|
+
name = ":#{f.name}"
|
226
|
+
|
227
|
+
# Determine the type-name and convert to Ruby
|
228
|
+
type = if f.type_name
|
229
|
+
# We have a type_name so we will use it after converting to a
|
230
|
+
# Ruby friendly version
|
231
|
+
t = f.type_name
|
232
|
+
t = t.gsub(pkg, "") # Remove the leading package name
|
233
|
+
t = t.gsub(/^\.*/, "") # Remove leading `.`s
|
234
|
+
|
235
|
+
t.gsub(".", "::") # Convert to Ruby namespacing syntax
|
236
|
+
else
|
237
|
+
":#{name_for(f, T, f.type)}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# Finally, generate the declaration
|
241
|
+
out = "%s %s, %s, %d" % [label, name, type, f.number]
|
242
|
+
|
243
|
+
if f.default_value
|
244
|
+
out += ", :default => #{f.default_value}"
|
245
|
+
end
|
246
|
+
|
247
|
+
puts out
|
248
|
+
end
|
249
|
+
|
250
|
+
# Determines the name for a
|
251
|
+
def name_for(b, mod, val)
|
252
|
+
b.name_for(mod, val).to_s.gsub(/.*_/, "").downcase
|
253
|
+
end
|
254
|
+
|
255
|
+
def compile(ns, file)
|
256
|
+
puts "## Generated from #{file.name} for #{file.package}"
|
257
|
+
puts "require \"beefcake\""
|
258
|
+
puts
|
259
|
+
|
260
|
+
ns!(ns) do
|
261
|
+
file.message_type.each do |mt|
|
262
|
+
message!(file.package, mt)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def ns!(modules, &blk)
|
268
|
+
if modules.empty?
|
269
|
+
blk.call
|
270
|
+
else
|
271
|
+
puts "module #{modules.first}"
|
272
|
+
indent do
|
273
|
+
ns!(modules[1..-1], &blk)
|
274
|
+
end
|
275
|
+
puts "end"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def puts(msg=nil)
|
280
|
+
if msg
|
281
|
+
c.puts((" " * @n) + msg)
|
282
|
+
else
|
283
|
+
c.puts
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'beefcake/generator'
|
2
|
+
|
3
|
+
class GeneratorTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
# Load up the generator request for the addressbook.proto example
|
7
|
+
dat = File.dirname(__FILE__) + "/../dat/code_generator_request.dat"
|
8
|
+
mock_request = File.read(dat)
|
9
|
+
@req = CodeGeneratorRequest.decode(mock_request)
|
10
|
+
end
|
11
|
+
|
12
|
+
if "".respond_to?(:encoding)
|
13
|
+
def test_request_has_filenames_as_binary
|
14
|
+
@req.proto_file.each do |file|
|
15
|
+
assert_equal Encoding.find("ASCII-8BIT"), file.name.encoding
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_generate_empty_namespace
|
21
|
+
@res = Beefcake::Generator.compile([], @req)
|
22
|
+
assert_equal(CodeGeneratorResponse, @res.class)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_generate_top_namespace
|
26
|
+
@res = Beefcake::Generator.compile(["Top"], @req)
|
27
|
+
assert_equal(CodeGeneratorResponse, @res.class)
|
28
|
+
assert_match(/module Top/, @res.file.first.content)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_generate_two_level_namespace
|
32
|
+
@res = Beefcake::Generator.compile(["Top", "Bottom"], @req)
|
33
|
+
assert_equal(CodeGeneratorResponse, @res.class)
|
34
|
+
assert_match(/module Top\s*\n\s*module Bottom/m, @res.file.first.content)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Covers the regression of encoding a CodeGeneratorResponse under 1.9.2-p136 raising
|
38
|
+
# Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and US-ASCII
|
39
|
+
def test_encode_decode_generated_response
|
40
|
+
@res = Beefcake::Generator.compile([], @req)
|
41
|
+
assert_nothing_raised { @res.encode }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_encode_decode_generated_response
|
45
|
+
@res = Beefcake::Generator.compile([], @req)
|
46
|
+
assert_equal(CodeGeneratorResponse, @res.class)
|
47
|
+
|
48
|
+
round_trip = CodeGeneratorResponse.decode(@res.encode)
|
49
|
+
assert_equal round_trip, @res
|
50
|
+
end
|
51
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beefcake
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 2
|
10
|
+
version: 0.3.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Blake Mizerany
|
@@ -15,57 +15,48 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-03
|
18
|
+
date: 2011-06-03 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
22
|
-
name: turn
|
23
|
-
prerelease: false
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
33
|
-
type: :development
|
34
|
-
version_requirements: *id001
|
20
|
+
dependencies: []
|
21
|
+
|
35
22
|
description: A sane protobuf library for Ruby
|
36
23
|
email:
|
24
|
+
- blake.mizerany@gmail.com
|
37
25
|
executables:
|
38
26
|
- protoc-gen-beefcake
|
39
27
|
extensions: []
|
40
28
|
|
41
|
-
extra_rdoc_files:
|
42
|
-
|
43
|
-
- LICENSE
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
44
31
|
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
45
34
|
- LICENSE
|
46
35
|
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- beefcake.gemspec
|
38
|
+
- bench/simple.rb
|
39
|
+
- bin/protoc-gen-beefcake
|
40
|
+
- dat/code_generator_request.dat
|
41
|
+
- lib/beefcake.rb
|
42
|
+
- lib/beefcake/buffer.rb
|
47
43
|
- lib/beefcake/buffer/base.rb
|
48
44
|
- lib/beefcake/buffer/decode.rb
|
49
45
|
- lib/beefcake/buffer/encode.rb
|
50
|
-
- lib/beefcake/
|
51
|
-
- lib/beefcake.rb
|
46
|
+
- lib/beefcake/generator.rb
|
47
|
+
- lib/beefcake/version.rb
|
52
48
|
- test/buffer_decode_test.rb
|
53
49
|
- test/buffer_encode_test.rb
|
54
50
|
- test/buffer_test.rb
|
51
|
+
- test/generator_test.rb
|
55
52
|
- test/message_test.rb
|
56
|
-
- bin/protoc-gen-beefcake
|
57
53
|
has_rdoc: true
|
58
|
-
homepage:
|
54
|
+
homepage: ""
|
59
55
|
licenses: []
|
60
56
|
|
61
57
|
post_install_message:
|
62
|
-
rdoc_options:
|
63
|
-
|
64
|
-
- --inline-source
|
65
|
-
- --title
|
66
|
-
- Sinatra
|
67
|
-
- --main
|
68
|
-
- README.rdoc
|
58
|
+
rdoc_options: []
|
59
|
+
|
69
60
|
require_paths:
|
70
61
|
- lib
|
71
62
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -91,10 +82,11 @@ requirements: []
|
|
91
82
|
rubyforge_project: beefcake
|
92
83
|
rubygems_version: 1.5.2
|
93
84
|
signing_key:
|
94
|
-
specification_version:
|
85
|
+
specification_version: 3
|
95
86
|
summary: A sane protobuf library for Ruby
|
96
87
|
test_files:
|
97
88
|
- test/buffer_decode_test.rb
|
98
89
|
- test/buffer_encode_test.rb
|
99
90
|
- test/buffer_test.rb
|
91
|
+
- test/generator_test.rb
|
100
92
|
- test/message_test.rb
|