ruby_protobuf 0.2.0 → 0.3.0

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.
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
+ }