ruby_protobuf 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/History.txt +6 -1
  2. data/Manifest.txt +23 -2
  3. data/bin/rprotoc +5 -5
  4. data/examples/addressbook.proto +24 -0
  5. data/examples/addressbook.rb +30 -0
  6. data/examples/reading_a_message.rb +32 -0
  7. data/examples/writing_a_message.rb +46 -0
  8. data/lib/protobuf/compiler/compiler.rb +38 -74
  9. data/lib/protobuf/compiler/compiler_old.rb +123 -0
  10. data/lib/protobuf/compiler/nodes.rb +208 -0
  11. data/lib/protobuf/compiler/proto.y +198 -0
  12. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  13. data/lib/protobuf/compiler/proto_parser.rb +1259 -0
  14. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  15. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  16. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  17. data/lib/protobuf/compiler/visitors.rb +132 -0
  18. data/lib/protobuf/message/decoder.rb +8 -3
  19. data/lib/protobuf/message/enum.rb +4 -0
  20. data/lib/protobuf/message/field.rb +31 -5
  21. data/lib/protobuf/message/message.rb +130 -14
  22. data/lib/protobuf/rpc/client.rb +19 -0
  23. data/lib/protobuf/rpc/handler.rb +17 -0
  24. data/lib/protobuf/rpc/server.rb +39 -0
  25. data/lib/ruby_protobuf.rb +1 -20
  26. data/test/addressbook.proto +6 -0
  27. data/test/addressbook.rb +17 -14
  28. data/test/addressbook_base.proto +26 -0
  29. data/test/addressbook_base.rb +62 -0
  30. data/test/addressbook_ext.proto +6 -0
  31. data/test/addressbook_ext.rb +12 -0
  32. data/test/rpc.proto +6 -0
  33. data/test/test_addressbook.rb +3 -2
  34. data/test/test_compiler.rb +98 -3
  35. data/test/test_extension.rb +40 -0
  36. data/test/test_standard_message.rb +83 -0
  37. data/test/test_types.rb +3 -3
  38. metadata +31 -8
  39. data/lib/protobuf/compiler/parser.y +0 -138
  40. data/test/data/data2.bin +0 -3
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/ruby
2
+ require '<%= underscored_name %>'
3
+
4
+ <%= module_name %>::<%= service_name %>.new(:port => <%= default_port %>).start
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/ruby
2
+ require 'protobuf/rpc/client'
3
+ require '<%= required_file %>'
4
+
5
+ # build request
6
+ request = <%= message_module %>::<%= request %>.new
7
+ # TODO: setup a request
8
+ raise StandardError.new('setup a request')
9
+
10
+ # create blunk response
11
+ response = <%= message_module %>::<%= response %>.new
12
+
13
+ # execute rpc
14
+ Protobuf::Rpc::Client.new('localhost', <%= default_port %>).call :<%= underscore name %>, request, response
15
+
16
+ # show response
17
+ puts response
18
+
@@ -0,0 +1,25 @@
1
+ require 'protobuf/rpc/server'
2
+ require 'protobuf/rpc/handler'
3
+ require '<%= required_file %>'
4
+
5
+ <%- rpcs.each do |name, request, response| -%>
6
+ class <%= module_name %>::<%= name %>Handler < Protobuf::Rpc::Handler
7
+ request <%= module_name %>::<%= request %>
8
+ response <%= module_name %>::<%= response %>
9
+
10
+ def self.process_request(request, response)
11
+ # TODO: edit this method
12
+ end
13
+ end
14
+
15
+ <%- end -%>
16
+ class <%= module_name %>::<%= service_name %> < Protobuf::Rpc::Server
17
+ def setup_handlers
18
+ @handlers = {
19
+ <%- rpcs.each do |name, | -%>
20
+ :<%= underscore name %> => <%= module_name %>::<%= name %>Handler,
21
+ <%- end -%>
22
+ }
23
+ end
24
+ end
25
+
@@ -0,0 +1,132 @@
1
+ require 'erb'
2
+
3
+ module Protobuf
4
+ module Visitor
5
+ class CreateMessageVisitor
6
+ attr_accessor :indent, :context
7
+
8
+ def initialize(proto_dir='.', out_dir='.')
9
+ @proto_dir, @out_dir = proto_dir, out_dir
10
+ @indent = 0
11
+ @context = []
12
+ end
13
+
14
+ def write(str)
15
+ ruby << "#{' ' * @indent}#{str}"
16
+ end
17
+
18
+ def increment
19
+ @indent += 1
20
+ end
21
+
22
+ def decrement
23
+ @indent -= 1
24
+ end
25
+
26
+ def close_ruby
27
+ while 0 < indent
28
+ decrement
29
+ write 'end'
30
+ end
31
+ end
32
+
33
+ def ruby
34
+ @ruby ||= []
35
+ end
36
+
37
+ def to_s
38
+ @ruby.join "\n"
39
+ end
40
+
41
+ def in_context(klass, &block)
42
+ increment
43
+ context.push klass
44
+ block.call
45
+ context.pop
46
+ decrement
47
+ end
48
+
49
+ def visit(node)
50
+ node.accept_message_creator self
51
+ self
52
+ end
53
+
54
+ def required_message_from_proto(proto_file)
55
+ rb_path = proto_file.sub(/\.proto$/, '.rb')
56
+ unless File.exist?("#{@out_dir}/#{rb_path}")
57
+ Compiler.compile proto_file, @proto_dir, @out_dir
58
+ end
59
+ rb_path.sub /\.rb$/, ''
60
+ end
61
+ end
62
+
63
+ class CreateRpcVisitor
64
+ attr_accessor :package, :services, :current_service, :file_contents
65
+
66
+ def initialize
67
+ @services = {}
68
+ @create_file = true
69
+ @file_contents = {}
70
+ end
71
+
72
+ def visit(node)
73
+ node.accept_rpc_creator self
74
+ self
75
+ end
76
+
77
+ def add_rpc(name, request, response)
78
+ (@services[@current_service] ||= []) << [name, request.first, response.first]
79
+ end
80
+
81
+ #def create_files(message_file, out_dir, create_file=true)
82
+ def create_files(message_file, out_dir, create_file=false)
83
+ @create_file = create_file
84
+ default_port = 9999
85
+ @services.each do |service_name, rpcs|
86
+ underscored_name = underscore service_name.to_s
87
+ message_module = package.map{|p| p.to_s.capitalize}.join('::')
88
+ required_file = message_file.sub(/^\.\//, '').sub(/\.rb$/, '')
89
+
90
+ create_bin out_dir, underscored_name, message_module, service_name, default_port
91
+ create_service message_file, out_dir, underscored_name, message_module,
92
+ service_name, default_port, rpcs, required_file
93
+ rpcs.each do |name, request, response|
94
+ create_client out_dir, underscored_name, default_port, name, request, response, message_module, required_file
95
+ end
96
+ end
97
+ @file_contents
98
+ end
99
+
100
+ def create_bin(out_dir, underscored_name, module_name, service_name, default_port)
101
+ bin_filename = "#{out_dir}/start_#{underscored_name}"
102
+ bin_contents = template_erb('rpc_bin').result binding
103
+ File.open(bin_filename, 'w') {|f| f.write bin_contents} if @create_file
104
+ @file_contents[bin_filename] = bin_contents
105
+ end
106
+
107
+ def create_service(message_file, out_dir, underscored_name, module_name, service_name, default_port, rpcs, required_file)
108
+ service_filename = "#{out_dir}/#{underscored_name}.rb"
109
+ service_contents = template_erb('rpc_service').result binding
110
+ File.open(service_filename, 'w') {|f| f.write service_contents} if @create_file
111
+ @file_contents[service_filename] = service_contents
112
+ end
113
+
114
+ def create_client(out_dir, underscored_name, default_port, name, request, response, message_module, required_file)
115
+ client_filename = "#{out_dir}/client_#{underscore name}.rb"
116
+ client_contents = template_erb('rpc_client').result binding
117
+ File.open(client_filename, 'w') {|f| f.write client_contents} if @create_file
118
+ @file_contents[client_filename] = client_contents
119
+ end
120
+
121
+ private
122
+
123
+ def underscore(str)
124
+ str.to_s.gsub(/\B[A-Z]/, '_\&').downcase
125
+ end
126
+
127
+ def template_erb(template)
128
+ ERB.new File.read("#{File.dirname(__FILE__)}/template/#{template}.erb"), nil, '-'
129
+ end
130
+ end
131
+ end
132
+ end
@@ -42,10 +42,15 @@ module Protobuf
42
42
  protected
43
43
 
44
44
  def read_key(stream)
45
+ # TODO is there more clear way to do this?
46
+ bits = 0
45
47
  bytes = read_varint stream
46
- #TODO 1 < bytes.size のときエラー
47
- wire_type = bytes[0] & 0b00000111
48
- tag = bytes[0] >> 3 # TODO
48
+ bytes.each_with_index do |byte, index|
49
+ byte &= 0b01111111
50
+ bits |= byte << (7 * index)
51
+ end
52
+ wire_type = bits & 0b00000111
53
+ tag = bits >> 3
49
54
  [tag, wire_type]
50
55
  end
51
56
 
@@ -13,6 +13,10 @@ module Protobuf
13
13
  not get_name_by_tag(tag).nil?
14
14
  end
15
15
 
16
+ def name_by_value(value)
17
+ constants.find {|c| const_get(c) == value}
18
+ end
19
+
16
20
  def descriptor
17
21
  @descriptor ||= Protobuf::Descriptor::EnumDescriptor.new(self)
18
22
  end
@@ -31,18 +31,41 @@ module Protobuf
31
31
  end
32
32
 
33
33
  def initialize(message_class, rule, type, name, tag, opts={})
34
- @message_class, @rule, @type, @name, @tag, @default =
35
- message_class, rule, type, name, tag, opts[:default]
34
+ @message_class, @rule, @type, @name, @tag, @default, @extension =
35
+ message_class, rule, type, name, tag, opts[:default], opts[:extension]
36
36
  @error_message = 'Type invalid'
37
37
  end
38
38
 
39
39
  def ready?; true end
40
40
 
41
+ def initialized?(message)
42
+ case rule
43
+ when :required
44
+ return false if message[name].nil?
45
+ return false if is_a?(Protobuf::Field::MessageField) and not message[name].initialized?
46
+ when :repeated
47
+ return message[name].inject(true) do |result, msg|
48
+ result and msg.initialized?
49
+ end
50
+ end
51
+ true
52
+ end
53
+
54
+ def clear(message)
55
+ if repeated?
56
+ message[name].clear
57
+ else
58
+ message[name] = default_value
59
+ end
60
+ end
61
+
41
62
  def default_value
42
63
  case rule
43
64
  when :repeated
44
65
  FieldArray.new self
45
- when :required, :optional
66
+ when :required
67
+ nil
68
+ when :optional
46
69
  typed_default_value default
47
70
  else
48
71
  raise InvalidRuleError
@@ -68,10 +91,13 @@ module Protobuf
68
91
  end
69
92
 
70
93
  def define_setter(message_instance)
94
+ extension = @extension
71
95
  message_instance.instance_eval %Q{
72
96
  def #{name}=(val)
73
- field = get_field_by_name #{name.inspect}
74
- if field.acceptable? val
97
+ field = get_#{extension ? 'ext_' : ''}field_by_name #{name.inspect}
98
+ if val.nil?
99
+ @#{name} = field.default_value
100
+ elsif field.acceptable? val
75
101
  @#{name} = val
76
102
  end
77
103
  end
@@ -1,3 +1,4 @@
1
+ require 'pp'
1
2
  require 'stringio'
2
3
  require 'protobuf/message/decoder'
3
4
  require 'protobuf/message/encoder'
@@ -7,12 +8,28 @@ require 'protobuf/descriptor/descriptor'
7
8
  module Protobuf
8
9
  OPTIONS = {}
9
10
 
11
+
10
12
  class Message
13
+ class ExtensionFields < Hash
14
+ def initialize(key_range=0..-1)
15
+ @key_range = key_range
16
+ end
17
+
18
+ def []=(key, value)
19
+ raise RangeError.new("#{key} is not in #{@key_range}") unless @key_range.include? key
20
+ super
21
+ end
22
+
23
+ def include_tag?(tag)
24
+ @key_range.include? tag
25
+ end
26
+ end
27
+
11
28
  class <<self
12
29
  attr_reader :fields
13
30
 
14
31
  def extensions(range)
15
- raise NotImplementedError('TODO')
32
+ @extension_fields = ExtensionFields.new range
16
33
  end
17
34
 
18
35
  def required(type, name, tag, opts={})
@@ -28,25 +45,51 @@ module Protobuf
28
45
  end
29
46
 
30
47
  def define_field(rule, type, name, tag, opts={})
31
- (@fields ||= {})[tag] = Protobuf::Field.build self, rule, type, name, tag, opts
48
+ field_hash = opts[:extension] ? extension_fields : (@fields ||= {})
49
+ field_hash[tag] = Protobuf::Field.build self, rule, type, name, tag, opts
50
+ #(@fields ||= {})[tag] = Protobuf::Field.build self, rule, type, name, tag, opts
51
+ end
52
+
53
+ def extension_tag?(tag)
54
+ extension_fields.include_tag? tag
55
+ end
56
+
57
+ def extension_fields
58
+ @extension_fields ||= ExtensionFields.new
32
59
  end
33
60
 
34
61
  def get_field_by_name(name)
35
- @fields.values.find {|field| field.name == name.to_sym}
62
+ fields.values.find {|field| field.name == name.to_sym}
36
63
  end
37
64
 
38
65
  def get_field_by_tag(tag)
39
- @fields[tag]
66
+ fields[tag]
40
67
  end
41
68
 
42
69
  def get_field(tag_or_name)
43
70
  case tag_or_name
44
- when Integer
45
- get_field_by_tag tag_or_name
46
- when String, Symbol
47
- get_field_by_name tag_or_name
48
- else
49
- raise TypeError
71
+ when Integer; get_field_by_tag tag_or_name
72
+ when String, Symbol; get_field_by_name tag_or_name
73
+ else; raise TypeError
74
+ end
75
+ end
76
+
77
+ #TODO merge to get_field_by_name
78
+ def get_ext_field_by_name(name)
79
+ extension_fields.values.find {|field| field.name == name.to_sym}
80
+ end
81
+
82
+ #TODO merge to get_field_by_tag
83
+ def get_ext_field_by_tag(tag)
84
+ extension_fields[tag]
85
+ end
86
+
87
+ #TODO merge to get_field
88
+ def get_ext_field(tag_or_name)
89
+ case tag_or_name
90
+ when Integer; get_ext_field_by_tag tag_or_name
91
+ when String, Symbol; get_ext_field_by_name tag_or_name
92
+ else; raise TypeError
50
93
  end
51
94
  end
52
95
 
@@ -63,6 +106,69 @@ module Protobuf
63
106
  end
64
107
  field.define_accessor self
65
108
  end
109
+
110
+ # TODO
111
+ self.class.extension_fields.each do |tag, field|
112
+ unless field.ready?
113
+ field = field.setup
114
+ self.class.class_eval {@extension_fields[tag] = field}
115
+ end
116
+ field.define_accessor self
117
+ end
118
+ end
119
+
120
+ def initialized?
121
+ fields.to_a.inject(true) do |result, (tag, field)|
122
+ result and field.initialized?(self)
123
+ end and
124
+ extension_fields.to_a.inject(true) do |result, (tag, field)|
125
+ result and field.initialized?(self)
126
+ end
127
+ end
128
+
129
+ def clear!
130
+ each_field do |field, value|
131
+ field.clear self
132
+ end
133
+ end
134
+
135
+ def dup
136
+ ret = self.class.new
137
+ each_field do |field, value|
138
+ if field.repeated?
139
+ value.each do |v|
140
+ ret[field.name] << (v.is_a?(Numeric) ? v : v.dup)
141
+ end
142
+ else
143
+ ret[field.name] = value.is_a?(Numeric) ? value : value.dup
144
+ end
145
+ end
146
+ ret
147
+ end
148
+
149
+ def to_s(indent=0)
150
+ ret = ''
151
+ i = ' ' * indent
152
+ field_value_to_string = lambda do |field, value|
153
+ ret +=
154
+ if field.is_a? Protobuf::Field::MessageField
155
+ "#{i}#{field.name} {\n#{value.to_s(indent + 1)}#{i}}\n"
156
+ elsif field.is_a? Protobuf::Field::EnumField
157
+ "#{i}#{field.name}: #{field.type.name_by_value(value)}\n"
158
+ else
159
+ "#{i}#{field.name}: #{value.inspect}\n"
160
+ end
161
+ end
162
+ each_field do |field, value|
163
+ if field.repeated?
164
+ value.each do |v|
165
+ field_value_to_string.call field, v
166
+ end
167
+ else
168
+ field_value_to_string.call field, value
169
+ end
170
+ end
171
+ ret
66
172
  end
67
173
 
68
174
  def parse_from_string(string)
@@ -104,7 +210,8 @@ module Protobuf
104
210
  end
105
211
 
106
212
  def set_field(tag, bytes)
107
- get_field_by_tag(tag).set self, bytes
213
+ #get_field_by_tag(tag).set self, bytes
214
+ (get_field_by_tag(tag) or get_ext_field_by_tag(tag)).set self, bytes
108
215
  end
109
216
 
110
217
  def merge_field(tag, value)
@@ -115,16 +222,20 @@ module Protobuf
115
222
  def [](tag_or_name)
116
223
  if field = get_field(tag_or_name)
117
224
  send field.name
225
+ elsif field = get_ext_field(tag_or_name)
226
+ send field.name
118
227
  else
119
- raise NoMethodError.new("No such method: #{tag_or_name}")
228
+ raise NoMethodError.new("No such method: #{tag_or_name.inspect}")
120
229
  end
121
230
  end
122
231
 
123
232
  def []=(tag_or_name, value)
124
233
  if field = get_field(tag_or_name) and not field.repeated?
125
234
  send "#{field.name}=", value
235
+ elsif field = get_ext_field(tag_or_name) and not field.repeated?
236
+ send "#{field.name}=", value
126
237
  else
127
- raise NoMethodError.new("No such method: #{tag_or_name}=")
238
+ raise NoMethodError.new("No such method: #{tag_or_name.inspect}")
128
239
  end
129
240
  end
130
241
 
@@ -133,8 +244,13 @@ module Protobuf
133
244
  def get_field_by_tag(tag); self.class.get_field_by_tag(tag) end
134
245
  def get_field(tag_or_name); self.class.get_field(tag_or_name) end
135
246
 
247
+ def extension_fields; self.class.extension_fields end
248
+ def get_ext_field_by_name(name); self.class.get_ext_field_by_name(name) end
249
+ def get_ext_field_by_tag(tag); self.class.get_ext_field_by_tag(tag) end
250
+ def get_ext_field(tag_or_name); self.class.get_ext_field(tag_or_name) end
251
+
136
252
  def each_field(&block)
137
- fields.to_a.sort{|(t1, f1), (t2, f2)| t1 <=> t2}.each do |tag, field|
253
+ (fields.merge extension_fields).to_a.sort{|(t1, f1), (t2, f2)| t1 <=> t2}.each do |tag, field|
138
254
  block.call field, self[tag]
139
255
  end
140
256
  end
@@ -0,0 +1,19 @@
1
+ require 'socket'
2
+
3
+ module Protobuf
4
+ module Rpc
5
+ class Client
6
+ def initialize(host, port)
7
+ @host, @port = host, port
8
+ end
9
+
10
+ def call(name, request, response)
11
+ socket = TCPSocket.open @host, @port
12
+ socket.write "#{name}\n"
13
+ request.serialize_to socket
14
+ socket.close_write
15
+ response.parse_from socket
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Protobuf
2
+ module Rpc
3
+ class Handler
4
+ class <<self
5
+ attr_reader :request_class, :response_class
6
+
7
+ def request(request_class)
8
+ @request_class = request_class
9
+ end
10
+
11
+ def response(response_class)
12
+ @response_class = response_class
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ require 'webrick/config'
2
+ require 'webrick/server'
3
+
4
+ module Protobuf
5
+ module Rpc
6
+ class Server < WEBrick::GenericServer
7
+ def initialize(config={:Port => 9999}, default=WEBrick::Config::General)
8
+ super config, default
9
+ setup_handlers
10
+ end
11
+
12
+ def setup_handlers
13
+ @handlers = {}
14
+ end
15
+
16
+ def get_handler(socket)
17
+ @handlers[socket.readline.strip.to_sym]
18
+ end
19
+
20
+ def run(socket)
21
+ handler = get_handler socket
22
+ request = handler.request_class.new
23
+ request.parse_from socket
24
+ response = handler.response_class.new
25
+ begin
26
+ handler.process_request request, response
27
+ rescue StandardError => ex
28
+ @logger.error ex
29
+ ensure
30
+ begin
31
+ response.serialize_to socket
32
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
33
+ @logger.error ex
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
data/lib/ruby_protobuf.rb CHANGED
@@ -1,22 +1,3 @@
1
- require 'protobuf/compiler'
2
-
3
1
  class RubyProtobuf
4
- VERSION = '0.2.0'
5
-
6
- def start(proto_file, options)
7
- unless File.exist?(proto_file)
8
- if File.exist? "#{proto_file}.proto"
9
- proto_file = "#{proto_file}.proto"
10
- else
11
- raise ArgumentError.new("#{proto_file} does not exist.")
12
- end
13
- end
14
- rb_filename = File.basename proto_file.dup
15
- rb_filename += '.rb' unless rb_filename.sub!(/.\w+$/, '.rb')
16
- rb_filepath = "#{options[:out] || '.'}/#{rb_filename}"
17
- puts "#{rb_filepath} writting..."
18
- File.open(rb_filepath, 'w') do |f|
19
- f.write Protobuf::Compiler.compile(proto_file)
20
- end
21
- end
2
+ VERSION = '0.3.0'
22
3
  end
@@ -17,6 +17,12 @@ message Person {
17
17
  }
18
18
 
19
19
  repeated PhoneNumber phone = 4;
20
+
21
+ extensions 100 to 200;
22
+ }
23
+
24
+ extend Person {
25
+ optional int32 age = 100;
20
26
  }
21
27
 
22
28
  message AddressBook {
data/test/addressbook.rb CHANGED
@@ -4,36 +4,39 @@ require 'protobuf/message/service'
4
4
  require 'protobuf/message/extend'
5
5
 
6
6
  module Tutorial
7
- class Person < Protobuf::Message
7
+
8
+ class Person < ::Protobuf::Message
8
9
  required :string, :name, 1
9
10
  required :int32, :id, 2
10
11
  optional :string, :email, 3
11
-
12
- class PhoneType < Protobuf::Enum
12
+
13
+ class PhoneType < ::Protobuf::Enum
13
14
  MOBILE = 0
14
15
  HOME = 1
15
16
  WORK = 2
16
17
  end
17
-
18
- class PhoneNumber < Protobuf::Message
18
+
19
+ class PhoneNumber < ::Protobuf::Message
19
20
  required :string, :number, 1
20
21
  optional :PhoneType, :type, 2, {:default => :HOME}
21
22
  end
22
-
23
+
23
24
  repeated :PhoneNumber, :phone, 4
24
-
25
- #extensions 100..199
26
- #class Foo < Protobuf::Extend
27
- # optional :int32, :bar, 126
28
- #end
25
+
26
+ #extensions 100..200
29
27
  end
30
-
31
- class AddressBook < Protobuf::Message
28
+
29
+ # see: addressbool_ext.rb
30
+ #class Person < ::Protobuf::Message
31
+ # optional :int32, :age, 100, :extension => true
32
+ #end
33
+
34
+ class AddressBook < ::Protobuf::Message
32
35
  repeated :Person, :person, 1
33
36
  end
34
37
 
35
38
  #class SearchService < Protobuf::Service
36
- # rpc :Search => :SearchRequest, :returns => :SearchResponse
39
+ # rpc :Search, :request => :SearchRequest, :response => :SearchResponse
37
40
  #end
38
41
 
39
42
  #Protobuf::OPTIONS[:optimize_for] = :SPEED
@@ -0,0 +1,26 @@
1
+ package tutorial;
2
+
3
+ message Person {
4
+ required string name = 1;
5
+ required int32 id = 2;
6
+ optional string email = 3;
7
+
8
+ enum PhoneType {
9
+ MOBILE = 0;
10
+ HOME = 1;
11
+ WORK = 2;
12
+ }
13
+
14
+ message PhoneNumber {
15
+ required string number = 1;
16
+ optional PhoneType type = 2 [default = HOME];
17
+ }
18
+
19
+ repeated PhoneNumber phone = 4;
20
+
21
+ extensions 100 to 200;
22
+ }
23
+
24
+ message AddressBook {
25
+ repeated Person person = 1;
26
+ }