ruby-protocol-buffers 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,8 @@ require 'stringio'
2
2
  require 'protocol_buffers/runtime/field'
3
3
  require 'protocol_buffers/runtime/encoder'
4
4
  require 'protocol_buffers/runtime/decoder'
5
+ require 'protocol_buffers/runtime/text_formatter'
6
+ require 'protocol_buffers/runtime/text_parser'
5
7
 
6
8
  module ProtocolBuffers
7
9
 
@@ -257,6 +259,19 @@ module ProtocolBuffers
257
259
  end
258
260
  alias_method :to_s, :serialize_to_string
259
261
 
262
+ # Format this message into the given IO stream using the text format of Protocol Buffers.
263
+ def text_format(io, options = nil)
264
+ formatter = TextFormatter.new(options)
265
+ formatter.format(io, self)
266
+ end
267
+
268
+ # Format this message into a text and return it.
269
+ def text_format_to_string(options = nil)
270
+ sio = ProtocolBuffers.utf8_sio
271
+ text_format(sio, options)
272
+ return sio.string
273
+ end
274
+
260
275
  def to_hash
261
276
  self.class.to_hash(self)
262
277
  end
@@ -296,6 +311,19 @@ module ProtocolBuffers
296
311
  self.new.parse(io)
297
312
  end
298
313
 
314
+ # Parse the text as a text representation of this class, and merge the parsed fields
315
+ # into the current message.
316
+ def parse_from_text(text)
317
+ parser = TextParser.new
318
+ parser.parse_text(text, self)
319
+ return self
320
+ end
321
+
322
+ # Shortcut, simply calls self.new.parse_from_text(text)
323
+ def self.parse_from_text(text)
324
+ self.new.parse_from_text(text)
325
+ end
326
+
299
327
  # Merge the attribute values from +obj+ into this Message, which must be of
300
328
  # the same class.
301
329
  #
@@ -426,6 +454,41 @@ module ProtocolBuffers
426
454
  @set_fields[tag] || false
427
455
  end
428
456
 
457
+ # Gets the field, returning nil if not set
458
+ # If a block is given, this block is called and it's
459
+ # return value returned if the value is not set
460
+ def get(*nested_field_names, &b)
461
+ if nested_field_names.size == 1
462
+ field_name = nested_field_names.first
463
+ field = self.class.field_for_name(field_name)
464
+ raise ArgumentError.new unless field
465
+ unless self.value_for_tag?(field.tag)
466
+ return b ? b.call : nil
467
+ end
468
+ return self.value_for_tag(field.tag)
469
+ end
470
+ last_proto = nested_field_names[0..-2].inject(self) do |sub_proto, ifield_name|
471
+ sub_field = sub_proto.class.field_for_name(ifield_name)
472
+ raise ArgumentError.new unless sub_field
473
+ raise ArgumentError.new unless sub_field.is_a?(ProtocolBuffers::Field::MessageField)
474
+ unless sub_proto.value_for_tag?(sub_field.tag)
475
+ return b ? b.call : nil
476
+ end
477
+ sub_proto.value_for_tag(sub_field.tag)
478
+ end
479
+ last_field_name = nested_field_names.last
480
+ last_field = last_proto.class.field_for_name(last_field_name)
481
+ unless last_proto.value_for_tag?(last_field.tag)
482
+ return b ? b.call : nil
483
+ end
484
+ last_proto.value_for_tag(last_field.tag)
485
+ end
486
+
487
+ # Gets the field, throwing ArgumentError if not set
488
+ def get!(*nested_field_names)
489
+ get(*nested_field_names) { raise ArgumentError.new("#{nested_field_names} is not set") }
490
+ end
491
+
429
492
  def inspect
430
493
  ret = ProtocolBuffers.bin_sio
431
494
  ret << "#<#{self.class.name}"
@@ -0,0 +1,116 @@
1
+ module ProtocolBuffers
2
+ class TextFormatter
3
+ def initialize(options = nil)
4
+ @options = options || {}
5
+ end
6
+
7
+ def format(io, message, options = nil)
8
+ message.validate!
9
+ options ||= {}
10
+ options = options.merge(@options)
11
+ options[:nest] ||= 0
12
+
13
+ if options[:short]
14
+ indent = ""
15
+ newline = " "
16
+ else
17
+ indent = " " * options[:nest]
18
+ newline = "\n"
19
+ end
20
+
21
+ sep = ""
22
+ message.fields.each do |tag, field|
23
+ next unless message.value_for_tag?(tag)
24
+ value = message.value_for_tag(tag)
25
+ if field.repeated?
26
+ next if value.size == 0
27
+ value.each do |v|
28
+ io.write sep; sep = newline
29
+
30
+ format_field(io, field, v, indent, newline, options)
31
+ end
32
+ else
33
+ io.write sep; sep = newline
34
+
35
+ format_field(io, field, value, indent, newline, options)
36
+ end
37
+ end
38
+
39
+ message.each_unknown_field do |tag_int, value|
40
+ io.write sep; sep = newline
41
+
42
+ wire_type = tag_int & 0x7
43
+ id = tag_int >> 3
44
+ format_unknown_field(io, wire_type, id, value, options)
45
+ end
46
+
47
+ io.write sep if !options[:short]
48
+
49
+ io
50
+ end
51
+
52
+ def format_field(io, field, value, indent, newline, options)
53
+ if field.kind_of? Field::GroupField
54
+ name = value.class.name.sub(/\A.*::/, '')
55
+ else
56
+ name = field.name
57
+ end
58
+
59
+ io.write "#{indent}#{name}"
60
+ if field.kind_of? Field::AggregateField
61
+ io.write " "
62
+ else
63
+ io.write ": "
64
+ end
65
+ field.text_format(io, value, options)
66
+ end
67
+
68
+ def format_unknown_field(io, wire_type, id, value, options)
69
+ options = options.dup
70
+ options[:nest] ||= 0
71
+
72
+ if options[:short]
73
+ indent = ""
74
+ newline = " "
75
+ else
76
+ indent = " " * options[:nest]
77
+ newline = "\n"
78
+ end
79
+
80
+ if wire_type == 3
81
+ options[:nest] += 1
82
+
83
+ io.write "#{indent}#{id} {#{newline}"
84
+ else
85
+ io.write "#{indent}#{id}: "
86
+ end
87
+
88
+ case wire_type
89
+ when 0 # VARINT
90
+ io.write "#{value}"
91
+
92
+ when 1 # FIXED64
93
+ lo, hi = value.unpack("V2")
94
+ io.write "0x%016x" % (hi << 32 | lo)
95
+
96
+ when 5 # FIXED32
97
+ io.write "0x%08x" % value.unpack("V")
98
+
99
+ when 2 # LENGTH_DELIMITED
100
+ value = value.unpack("C*").map { |b| "\\x%02x" % b }.join(nil)
101
+ io.write "\"#{value}\""
102
+
103
+ when 3 # START_GROUP
104
+ format(io, value, options)
105
+
106
+ when 4 # END_GROUP: never appear
107
+ raise(EncodeError, "Unexpected wire type END_GROUP")
108
+ else
109
+ raise(EncodeError, "unknown wire type: #{wire_type}")
110
+ end
111
+ if wire_type == 3
112
+ io.write "#{indent}}"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,136 @@
1
+ # vim: set ft=racc fenc=us-ascii : -*- mode: racc coding: US-ASCII -*-
2
+ class ProtocolBuffers::TextParser
3
+ token identifier string integer bool float
4
+ rule
5
+ message : /* none */
6
+ {
7
+ result = current_message
8
+ }
9
+ | message field
10
+
11
+ field : field_name ':' primitive_value
12
+ {
13
+ set_field(val[0], val[2])
14
+ }
15
+ | field_name ':' identifier
16
+ {
17
+ field, enum_symbol = val[0], val[2]
18
+ unless field.kind_of?(ProtocolBuffers::Field::EnumField)
19
+ raise Racc::ParseError, "not a enum field: %s" % field.name
20
+ end
21
+ value = field.value_from_name(enum_symbol)
22
+ unless value
23
+ raise Racc::ParseError, "enum type %s has no value named %s" % [field.name, enum_symbol]
24
+ end
25
+ set_field(field, value)
26
+ }
27
+ | message_field_head '<'
28
+ {
29
+ field = _values[-2]
30
+ push_message(field.proxy_class.new)
31
+ }
32
+ message '>'
33
+ {
34
+ pop_message
35
+ set_field(val[0], val[3])
36
+ }
37
+ | message_field_head '{'
38
+ {
39
+ field = _values[-2]
40
+ push_message(field.proxy_class.new)
41
+ }
42
+ message '}'
43
+ {
44
+ pop_message
45
+ set_field(val[0], val[3])
46
+ }
47
+
48
+ message_field_head : field_name
49
+ | field_name ':'
50
+
51
+ field_name : identifier
52
+ {
53
+ field = current_message.class.field_for_name(val[0])
54
+ if field
55
+ return field
56
+ end
57
+
58
+ # fallback for case mismatch in group fields.
59
+ field = current_message.fields.find { |tag,field| field.name.to_s.downcase == val[0].downcase }
60
+ field &&= field.last
61
+ if field && field.kind_of?(ProtocolBuffers::Field::GroupField)
62
+ return field
63
+ end
64
+
65
+ raise Racc::ParseError, "no such field %s in %s" % [val[0], current_message.class]
66
+ }
67
+ | '[' qualified_name ']'
68
+ {
69
+ raise NotImplementedError, "extension is not yet supported"
70
+ }
71
+
72
+ qualified_name : identifier
73
+ {
74
+ result = [val[0]]
75
+ }
76
+ | qualified_name '.' identifier
77
+ {
78
+ result = (val[0] << val[2])
79
+ }
80
+
81
+ primitive_value : concat_string
82
+ | integer
83
+ | float
84
+ | bool
85
+ concat_string : string
86
+ | concat_string string
87
+ {
88
+ result = val[0] + val[1]
89
+ }
90
+ end
91
+
92
+ ---- header
93
+ require 'protocol_buffers/runtime/text_scanner'
94
+
95
+ ---- inner
96
+ def initialize
97
+ @msgstack = []
98
+ end
99
+
100
+ attr_accessor :yydebug
101
+
102
+ def parse_text(text, message)
103
+ scanner = ProtocolBuffers::TextScanner.new(text)
104
+ parse_from_scanner(scanner.enum_for(:scan), message)
105
+ end
106
+
107
+ def parse_from_scanner(scanner, message)
108
+ @msgstack.clear
109
+ push_message(message)
110
+ yyparse(scanner, :each)
111
+ pop_message
112
+ end
113
+
114
+ private :yyparse, :do_parse
115
+ private
116
+ def current_message
117
+ @msgstack.last
118
+ end
119
+
120
+ def push_message(message)
121
+ @msgstack.push(message)
122
+ end
123
+
124
+ def pop_message
125
+ @msgstack.pop
126
+ end
127
+
128
+ def set_field(field, value)
129
+ msg = current_message
130
+ if field.repeated?
131
+ msg.value_for_tag(field.tag) << value
132
+ else
133
+ msg.set_value_for_tag(field.tag, value)
134
+ end
135
+ msg
136
+ end
@@ -0,0 +1,94 @@
1
+ # -*- coding: UTF-8 -*-
2
+
3
+ require 'strscan'
4
+ require 'racc/parser'
5
+
6
+ module ProtocolBuffers; end
7
+
8
+ class ProtocolBuffers::TextScanner
9
+ def initialize(text)
10
+ @text = text.encode(Encoding::UTF_8)
11
+ @scanner = StringScanner.new(@text)
12
+ @lineno = 1
13
+ end
14
+
15
+ attr_reader :lineno
16
+
17
+ def scan
18
+ while @scanner.rest?
19
+ case
20
+ when @scanner.skip(/[ \t\v]+/)
21
+ next
22
+ when @scanner.skip(/#.*?$/)
23
+ next
24
+ when @scanner.skip(/\r?\n|\r/)
25
+ @lineno += 1
26
+ next
27
+ when @scanner.scan(/[.:<>{}\[\]]/)
28
+ c = @scanner[0]
29
+ yield [c, c]
30
+ when @scanner.scan(/true/)
31
+ yield [:bool, true]
32
+ when @scanner.scan(/false/)
33
+ yield [:bool, false]
34
+ when @scanner.scan(/["']/)
35
+ quote = @scanner[0]
36
+ line = lineno
37
+ if @scanner.scan(/(.*?)(?<!\\)#{quote}/)
38
+ str = @scanner[1]
39
+ yield [:string, unescape(str)]
40
+ else
41
+ raise Racc::ParseError, "unterminated string from line #{line}"
42
+ end
43
+ when @scanner.scan(/([+-])?[0-9]+\.[0-9]+([Ee][+-]?[0-9]+)?/)
44
+ yield [:float, Float(@scanner[0])]
45
+ when @scanner.scan(/([+-])?0[Bb]([01]+)/)
46
+ yield [:integer, Integer(@scanner[0], 2)]
47
+ when @scanner.scan(/([+-])?0[Xx]([[:xdigit:]]+)/)
48
+ yield [:integer, Integer(@scanner[0], 16)]
49
+ when @scanner.scan(/([+-])?0[Oo]?([0-7]+)/)
50
+ yield [:integer, Integer(@scanner[0], 8)]
51
+ when @scanner.scan(/([+-])?(?:0[Dd])?([0-9]+)/)
52
+ yield [:integer, Integer(@scanner[0], 10)]
53
+ when @scanner.scan(/[[:alpha:]_][[:alnum:]_]*/)
54
+ yield [:identifier, @scanner[0]]
55
+ else
56
+ line = lineno
57
+ raise Racc::ParseError, "unexpected character at: line #{line}: #{@scanner.rest.inspect}"
58
+ end
59
+ end
60
+ yield [false, nil]
61
+ end
62
+
63
+ private
64
+ ESCAPE_SEQUENCE = {
65
+ 'a' => "\a",
66
+ 'b' => "\b",
67
+ 'f' => "\f",
68
+ 'n' => "\n",
69
+ 'r' => "\r",
70
+ 't' => "\t",
71
+ 'v' => "\v",
72
+ '\\' => "\\",
73
+ '"' => '"',
74
+ "'" => "'",
75
+ }.freeze
76
+
77
+ def unescape(str)
78
+ str.gsub(%r!
79
+ \\ (?:
80
+ [Xx]([[:xdigit:]]{1,2}) |
81
+ ([0-7]{1,3}) |
82
+ ([abfnrtv\\'"])
83
+ )!x) do
84
+ case
85
+ when $1
86
+ Integer($1, 16).chr
87
+ when $2
88
+ Integer($2, 8).chr
89
+ when $3
90
+ ESCAPE_SEQUENCE[$3]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,3 @@
1
1
  module ProtocolBuffers
2
- VERSION = "1.5.1"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -25,6 +25,7 @@ Gem::Specification.new do |gem|
25
25
  gem.add_development_dependency "rake"
26
26
  gem.add_development_dependency "rake-compiler"
27
27
  gem.add_development_dependency "simplecov"
28
- gem.add_development_dependency "rspec", "~> 2.5"
28
+ gem.add_development_dependency "rspec", "~> 2.14"
29
29
  gem.add_development_dependency "yard"
30
+ gem.add_development_dependency "racc", "~> 1.4.12"
30
31
  end