protobuf 1.0.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 (113) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.md +216 -0
  5. data/Rakefile +1 -0
  6. data/bin/rpc_server +117 -0
  7. data/bin/rprotoc +46 -0
  8. data/examples/addressbook.pb.rb +55 -0
  9. data/examples/addressbook.proto +24 -0
  10. data/examples/reading_a_message.rb +32 -0
  11. data/examples/writing_a_message.rb +46 -0
  12. data/lib/protobuf.rb +6 -0
  13. data/lib/protobuf/common/exceptions.rb +11 -0
  14. data/lib/protobuf/common/logger.rb +64 -0
  15. data/lib/protobuf/common/util.rb +59 -0
  16. data/lib/protobuf/common/wire_type.rb +10 -0
  17. data/lib/protobuf/compiler/compiler.rb +52 -0
  18. data/lib/protobuf/compiler/nodes.rb +323 -0
  19. data/lib/protobuf/compiler/proto.y +216 -0
  20. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  21. data/lib/protobuf/compiler/proto_parser.rb +1425 -0
  22. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  23. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  24. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  25. data/lib/protobuf/compiler/template/rpc_service_implementation.erb +42 -0
  26. data/lib/protobuf/compiler/visitors.rb +302 -0
  27. data/lib/protobuf/descriptor/descriptor.proto +286 -0
  28. data/lib/protobuf/descriptor/descriptor.rb +55 -0
  29. data/lib/protobuf/descriptor/descriptor_builder.rb +143 -0
  30. data/lib/protobuf/descriptor/descriptor_proto.rb +138 -0
  31. data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
  32. data/lib/protobuf/descriptor/field_descriptor.rb +49 -0
  33. data/lib/protobuf/descriptor/file_descriptor.rb +37 -0
  34. data/lib/protobuf/message/decoder.rb +83 -0
  35. data/lib/protobuf/message/encoder.rb +46 -0
  36. data/lib/protobuf/message/enum.rb +62 -0
  37. data/lib/protobuf/message/extend.rb +8 -0
  38. data/lib/protobuf/message/field.rb +701 -0
  39. data/lib/protobuf/message/message.rb +402 -0
  40. data/lib/protobuf/message/protoable.rb +38 -0
  41. data/lib/protobuf/rpc/buffer.rb +74 -0
  42. data/lib/protobuf/rpc/client.rb +268 -0
  43. data/lib/protobuf/rpc/client_connection.rb +225 -0
  44. data/lib/protobuf/rpc/error.rb +34 -0
  45. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  46. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  47. data/lib/protobuf/rpc/rpc.pb.rb +107 -0
  48. data/lib/protobuf/rpc/server.rb +183 -0
  49. data/lib/protobuf/rpc/service.rb +244 -0
  50. data/lib/protobuf/rpc/stat.rb +70 -0
  51. data/lib/protobuf/version.rb +3 -0
  52. data/proto/rpc.proto +73 -0
  53. data/protobuf.gemspec +25 -0
  54. data/script/mk_parser +2 -0
  55. data/spec/functional/embedded_service_spec.rb +7 -0
  56. data/spec/proto/test.pb.rb +31 -0
  57. data/spec/proto/test.proto +31 -0
  58. data/spec/proto/test_service.rb +30 -0
  59. data/spec/proto/test_service_impl.rb +17 -0
  60. data/spec/spec_helper.rb +26 -0
  61. data/spec/unit/client_spec.rb +128 -0
  62. data/spec/unit/common/logger_spec.rb +121 -0
  63. data/spec/unit/enum_spec.rb +13 -0
  64. data/spec/unit/message_spec.rb +67 -0
  65. data/spec/unit/server_spec.rb +27 -0
  66. data/spec/unit/service_spec.rb +75 -0
  67. data/test/check_unbuild.rb +30 -0
  68. data/test/data/data.bin +3 -0
  69. data/test/data/data_source.py +14 -0
  70. data/test/data/types.bin +0 -0
  71. data/test/data/types_source.py +22 -0
  72. data/test/data/unk.png +0 -0
  73. data/test/proto/addressbook.pb.rb +66 -0
  74. data/test/proto/addressbook.proto +33 -0
  75. data/test/proto/addressbook_base.pb.rb +58 -0
  76. data/test/proto/addressbook_base.proto +26 -0
  77. data/test/proto/addressbook_ext.pb.rb +20 -0
  78. data/test/proto/addressbook_ext.proto +6 -0
  79. data/test/proto/collision.pb.rb +17 -0
  80. data/test/proto/collision.proto +5 -0
  81. data/test/proto/ext_collision.pb.rb +24 -0
  82. data/test/proto/ext_collision.proto +8 -0
  83. data/test/proto/ext_range.pb.rb +22 -0
  84. data/test/proto/ext_range.proto +7 -0
  85. data/test/proto/float_default.proto +10 -0
  86. data/test/proto/lowercase.pb.rb +30 -0
  87. data/test/proto/lowercase.proto +9 -0
  88. data/test/proto/merge.pb.rb +39 -0
  89. data/test/proto/merge.proto +15 -0
  90. data/test/proto/nested.pb.rb +30 -0
  91. data/test/proto/nested.proto +9 -0
  92. data/test/proto/optional_field.pb.rb +35 -0
  93. data/test/proto/optional_field.proto +12 -0
  94. data/test/proto/packed.pb.rb +22 -0
  95. data/test/proto/packed.proto +6 -0
  96. data/test/proto/rpc.proto +6 -0
  97. data/test/proto/types.pb.rb +84 -0
  98. data/test/proto/types.proto +37 -0
  99. data/test/test_addressbook.rb +56 -0
  100. data/test/test_compiler.rb +325 -0
  101. data/test/test_descriptor.rb +122 -0
  102. data/test/test_enum_value.rb +41 -0
  103. data/test/test_extension.rb +36 -0
  104. data/test/test_lowercase.rb +11 -0
  105. data/test/test_message.rb +128 -0
  106. data/test/test_optional_field.rb +103 -0
  107. data/test/test_packed_field.rb +40 -0
  108. data/test/test_parse.rb +15 -0
  109. data/test/test_repeated_types.rb +132 -0
  110. data/test/test_serialize.rb +61 -0
  111. data/test/test_standard_message.rb +96 -0
  112. data/test/test_types.rb +226 -0
  113. metadata +261 -0
@@ -0,0 +1,402 @@
1
+ require 'pp'
2
+ require 'stringio'
3
+ require 'protobuf/descriptor/descriptor'
4
+ require 'protobuf/message/decoder'
5
+ require 'protobuf/message/encoder'
6
+ require 'protobuf/message/field'
7
+ require 'protobuf/message/protoable'
8
+ require 'json'
9
+
10
+ module Protobuf
11
+ OPTIONS = {}
12
+
13
+ class Message
14
+
15
+ class ExtensionFields < Hash
16
+ def initialize(key_range=0..-1)
17
+ @key_range = key_range
18
+ end
19
+
20
+ def []=(key, value)
21
+ raise RangeError, "#{key} is not in #{@key_range}" unless @key_range.include?(key)
22
+ super
23
+ end
24
+
25
+ def include_tag?(tag)
26
+ @key_range.include?(tag)
27
+ end
28
+ end
29
+
30
+ class <<self
31
+ include Protoable
32
+
33
+ # Reserve field numbers for extensions. Don't use this method directly.
34
+ def extensions(range)
35
+ @extension_fields = ExtensionFields.new(range)
36
+ end
37
+
38
+ # Define a required field. Don't use this method directly.
39
+ def required(type, name, tag, options={})
40
+ define_field(:required, type, name, tag, options)
41
+ end
42
+
43
+ # Define a optional field. Don't use this method directly.
44
+ def optional(type, name, tag, options={})
45
+ define_field(:optional, type, name, tag, options)
46
+ end
47
+
48
+ # Define a repeated field. Don't use this method directly.
49
+ def repeated(type, name, tag, options={})
50
+ define_field(:repeated, type, name, tag, options)
51
+ end
52
+
53
+ # Define a field. Don't use this method directly.
54
+ def define_field(rule, type, fname, tag, options)
55
+ field_hash = options[:extension] ? extension_fields : fields
56
+ if field_hash.keys.include?(tag)
57
+ raise TagCollisionError, %!{Field number #{tag} has already been used in "#{self.name}" by field "#{fname}".!
58
+ end
59
+ field_hash[tag] = Field.build(self, rule, type, fname, tag, options)
60
+ end
61
+
62
+ def extension_tag?(tag)
63
+ extension_fields.include_tag?(tag)
64
+ end
65
+
66
+ # A collection of field object.
67
+ def fields
68
+ @fields ||= {}
69
+ end
70
+
71
+ # An extension field object.
72
+ def extension_fields
73
+ @extension_fields ||= ExtensionFields.new
74
+ end
75
+
76
+ # Find a field object by +name+.
77
+ def get_field_by_name(name)
78
+ name = name.to_sym
79
+ fields.values.find {|field| field.name == name}
80
+ end
81
+
82
+ # Find a field object by +tag+ number.
83
+ def get_field_by_tag(tag)
84
+ fields[tag]
85
+ end
86
+
87
+ # Find a field object by +tag_or_name+.
88
+ def get_field(tag_or_name)
89
+ case tag_or_name
90
+ when Integer then get_field_by_tag(tag_or_name)
91
+ when String, Symbol then get_field_by_name(tag_or_name)
92
+ else raise TypeError, tag_or_name.class
93
+ end
94
+ end
95
+
96
+ #TODO merge to get_field_by_name
97
+ def get_ext_field_by_name(name)
98
+ name = name.to_sym
99
+ extension_fields.values.find {|field| field.name == name}
100
+ end
101
+
102
+ #TODO merge to get_field_by_tag
103
+ def get_ext_field_by_tag(tag)
104
+ extension_fields[tag]
105
+ end
106
+
107
+ #TODO merge to get_field
108
+ def get_ext_field(tag_or_name)
109
+ case tag_or_name
110
+ when Integer then get_ext_field_by_tag(tag_or_name)
111
+ when String, Symbol then get_ext_field_by_name(tag_or_name)
112
+ else raise TypeError, tag_or_name.class
113
+ end
114
+ end
115
+
116
+ def descriptor
117
+ @descriptor ||= Descriptor::Descriptor.new(self)
118
+ end
119
+ end
120
+
121
+ def initialize(values={})
122
+ @values = {}
123
+
124
+ self.class.fields.each do |tag, field|
125
+ unless field.ready?
126
+ field = field.setup
127
+ self.class.class_eval {@fields[tag] = field}
128
+ end
129
+ if field.repeated?
130
+ @values[field.name] = Field::FieldArray.new(field)
131
+ end
132
+ end
133
+
134
+ # TODO
135
+ self.class.extension_fields.each do |tag, field|
136
+ unless field.ready?
137
+ field = field.setup
138
+ self.class.class_eval {@extension_fields[tag] = field}
139
+ end
140
+ if field.repeated?
141
+ @values[field.name] = Field::FieldArray.new(field)
142
+ end
143
+ end
144
+
145
+ values.each {|tag, val| self[tag] = val}
146
+ end
147
+
148
+ def initialized?
149
+ fields.all? {|tag, field| field.initialized?(self) } && \
150
+ extension_fields.all? {|tag, field| field.initialized?(self) }
151
+ end
152
+
153
+ def has_field?(tag_or_name)
154
+ field = get_field(tag_or_name) || get_ext_field(tag_or_name)
155
+ raise ArgumentError, "unknown field: #{tag_or_name.inspect}" unless field
156
+ @values.has_key?(field.name)
157
+ end
158
+
159
+ def ==(obj)
160
+ return false unless obj.is_a?(self.class)
161
+ each_field do |field, value|
162
+ return false unless value == obj.__send__(field.name)
163
+ end
164
+ true
165
+ end
166
+
167
+ def clear!
168
+ @values.delete_if do |_, value|
169
+ if value.is_a?(Field::FieldArray)
170
+ value.clear
171
+ false
172
+ else
173
+ true
174
+ end
175
+ end
176
+ self
177
+ end
178
+
179
+ def dup
180
+ copy_to(super, :dup)
181
+ end
182
+
183
+ def clone
184
+ copy_to(super, :clone)
185
+ end
186
+
187
+ def copy_to(object, method)
188
+ duplicate = proc {|obj|
189
+ case obj
190
+ when Message, String then obj.__send__(method)
191
+ else obj
192
+ end
193
+ }
194
+
195
+ object.__send__(:initialize)
196
+ @values.each do |name, value|
197
+ if value.is_a?(Field::FieldArray)
198
+ object.__send__(name).replace(value.map {|v| duplicate.call(v)})
199
+ else
200
+ object.__send__("#{name}=", duplicate.call(value))
201
+ end
202
+ end
203
+ object
204
+ end
205
+ private :copy_to
206
+
207
+ def inspect(indent=0)
208
+ result = []
209
+ i = ' ' * indent
210
+ field_value_to_string = lambda {|field, value|
211
+ result << \
212
+ if field.optional? && ! has_field?(field.name)
213
+ ''
214
+ else
215
+ case field
216
+ when Field::MessageField
217
+ if value.nil?
218
+ "#{i}#{field.name} {}\n"
219
+ else
220
+ "#{i}#{field.name} {\n#{value.inspect(indent + 1)}#{i}}\n"
221
+ end
222
+ when Field::EnumField
223
+ if value.is_a?(EnumValue)
224
+ "#{i}#{field.name}: #{value.name}\n"
225
+ else
226
+ "#{i}#{field.name}: #{field.type.name_by_value(value)}\n"
227
+ end
228
+ else
229
+ "#{i}#{field.name}: #{value.inspect}\n"
230
+ end
231
+ end
232
+ }
233
+ each_field do |field, value|
234
+ if field.repeated?
235
+ value.each do |v|
236
+ field_value_to_string.call(field, v)
237
+ end
238
+ else
239
+ field_value_to_string.call(field, value)
240
+ end
241
+ end
242
+ result.join
243
+ end
244
+
245
+ def parse_from_string(string)
246
+ parse_from(StringIO.new(string))
247
+ end
248
+
249
+ def parse_from_file(filename)
250
+ if filename.is_a?(File)
251
+ parse_from(filename)
252
+ else
253
+ File.open(filename, 'rb') do |f|
254
+ parse_from(f)
255
+ end
256
+ end
257
+ end
258
+
259
+ def parse_from(stream)
260
+ Decoder.decode(stream, self)
261
+ end
262
+
263
+ def serialize_to_string(string='')
264
+ io = StringIO.new(string)
265
+ serialize_to(io)
266
+ result = io.string
267
+ result.force_encoding('ASCII-8BIT') if result.respond_to?(:force_encoding)
268
+ result
269
+ end
270
+ alias to_s serialize_to_string
271
+
272
+ def serialize_to_file(filename)
273
+ if filename.is_a?(File)
274
+ serialize_to(filename)
275
+ else
276
+ File.open(filename, 'wb') do |f|
277
+ serialize_to(f)
278
+ end
279
+ end
280
+ end
281
+
282
+ def serialize_to(stream)
283
+ Encoder.encode(stream, self)
284
+ end
285
+
286
+ def merge_from(message)
287
+ # TODO
288
+ fields.each {|tag, field| merge_field(tag, message.__send__(field.name))}
289
+ extension_fields.each {|tag, field| merge_field(tag, message.__send__(field.name))}
290
+ end
291
+
292
+ def set_field(tag, bytes)
293
+ field = (get_field_by_tag(tag) || get_ext_field_by_tag(tag))
294
+ field.set(self, bytes) if field
295
+ end
296
+
297
+ def merge_field(tag, value)
298
+ #get_field_by_tag(tag).merge self, bytes #TODO
299
+ (get_field_by_tag(tag) || get_ext_field_by_tag(tag)).merge(self, value)
300
+ end
301
+
302
+ def [](tag_or_name)
303
+ if field = get_field(tag_or_name) || get_ext_field(tag_or_name)
304
+ __send__(field.name)
305
+ else
306
+ raise NoMethodError, "No such field: #{tag_or_name.inspect}"
307
+ end
308
+ end
309
+
310
+ def []=(tag_or_name, value)
311
+ if field = get_field(tag_or_name) || get_ext_field(tag_or_name)
312
+ __send__("#{field.name}=", value)
313
+ else
314
+ raise NoMethodError, "No such field: #{tag_or_name.inspect}"
315
+ end
316
+ end
317
+
318
+ # Returns a hash; which key is a tag number, and value is a field object.
319
+ def fields
320
+ self.class.fields
321
+ end
322
+
323
+ # Returns field object or +nil+.
324
+ def get_field_by_name(name)
325
+ self.class.get_field_by_name(name)
326
+ end
327
+
328
+ # Returns field object or +nil+.
329
+ def get_field_by_tag(tag)
330
+ self.class.get_field_by_tag(tag)
331
+ end
332
+
333
+ # Returns field object or +nil+.
334
+ def get_field(tag_or_name)
335
+ self.class.get_field(tag_or_name)
336
+ end
337
+
338
+ # Returns extension fields. See Message#fields method.
339
+ def extension_fields
340
+ self.class.extension_fields
341
+ end
342
+
343
+ def get_ext_field_by_name(name) # :nodoc:
344
+ self.class.get_ext_field_by_name(name)
345
+ end
346
+
347
+ def get_ext_field_by_tag(tag) # :nodoc:
348
+ self.class.get_ext_field_by_tag(tag)
349
+ end
350
+
351
+ def get_ext_field(tag_or_name) # :nodoc:
352
+ self.class.get_ext_field(tag_or_name)
353
+ end
354
+
355
+ # Iterate over a field collection.
356
+ # message.each_field do |field_object, value|
357
+ # # do something
358
+ # end
359
+ def each_field
360
+ fields.merge(extension_fields).sort_by {|tag, _| tag}.each do |_, field|
361
+ value = __send__(field.name)
362
+ yield(field, value)
363
+ end
364
+ end
365
+
366
+ def to_hash
367
+ result = {}
368
+ build_value = lambda {|field, value|
369
+ if !field.optional? || (field.optional? && has_field?(field.name))
370
+ case field
371
+ when Field::MessageField
372
+ value.to_hash
373
+ when Field::EnumField
374
+ if value.is_a?(EnumValue)
375
+ value.to_i
376
+ elsif value.is_a?(Symbol)
377
+ field.type[value].to_i
378
+ else
379
+ value
380
+ end
381
+ else
382
+ value
383
+ end
384
+ end
385
+ }
386
+ each_field do |field, value|
387
+ if field.repeated?
388
+ result[field.name] = value.map do |v|
389
+ build_value.call(field, v)
390
+ end
391
+ else
392
+ result[field.name] = build_value.call(field, value)
393
+ end
394
+ end
395
+ result
396
+ end
397
+
398
+ def to_json
399
+ to_hash.to_json
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,38 @@
1
+ module Protobuf
2
+ module Protoable
3
+ def defined_filenames
4
+ @defined_filenames ||= []
5
+ end
6
+
7
+ def defined_in(filename)
8
+ path = File.expand_path(filename)
9
+ defined_filenames << path unless defined_filenames.include?(path)
10
+ end
11
+
12
+ def proto_filenames
13
+ defined_filenames.map do |filename|
14
+ retrieve_header(File.read(filename)).first
15
+ end
16
+ end
17
+
18
+ def proto_contents
19
+ #TODO: temporary implementation because the result includes not only this message but also all messages
20
+ ret = {}
21
+ defined_filenames.each do |filename|
22
+ header = retrieve_header(File.read(filename))
23
+ ret[header.first] = header.last
24
+ end
25
+ ret
26
+ end
27
+
28
+ def retrieve_header(contents)
29
+ if contents =~ /### Generated by rprotoc\. DO NOT EDIT!\n### <proto file: (.*)>\n((# .*\n)+)/
30
+ proto_filename = $1
31
+ proto_contents = $2.gsub(/^# /, '')
32
+ [proto_filename, proto_contents]
33
+ else
34
+ [nil, nil]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,74 @@
1
+ module Protobuf
2
+ module Rpc
3
+ class Buffer
4
+
5
+ attr_accessor :mode
6
+ attr_reader :data, :size
7
+
8
+ MODES = [:read, :write]
9
+
10
+ def initialize mode=:read, data=''
11
+ @data = data.is_a?(Protobuf::Message) ? data.serialize_to_string : data.to_s
12
+ @flush = false
13
+ self.mode = mode
14
+ end
15
+
16
+ def mode= mode
17
+ if MODES.include? mode
18
+ @mode = mode
19
+ else
20
+ @mode = :read
21
+ end
22
+ end
23
+
24
+ def write force_mode=true
25
+ if force_mode and reading?
26
+ mode = :write
27
+ elsif not force_mode and reading?
28
+ raise = 'You chose to write the buffer when in read mode'
29
+ end
30
+
31
+ @size = @data.length
32
+ '%d-%s' % [@size, @data]
33
+ end
34
+
35
+ def << data
36
+ @data << data
37
+ if reading?
38
+ get_data_size
39
+ check_for_flush
40
+ end
41
+ end
42
+
43
+ def reading?
44
+ mode == :read
45
+ end
46
+
47
+ def writing?
48
+ mode == :write
49
+ end
50
+
51
+ def flushed?
52
+ @flush
53
+ end
54
+
55
+ private
56
+
57
+ def get_data_size
58
+ if @size.nil?
59
+ sliced_size = @data.slice! /^\d+-/
60
+ unless sliced_size.nil?
61
+ @size = sliced_size.gsub(/-/, '').to_i
62
+ end
63
+ end
64
+ end
65
+
66
+ def check_for_flush
67
+ if not @size.nil? and @data.length == @size
68
+ @flush = true
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end