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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +4 -8
- data/CHANGELOG.md +80 -0
- data/Changelog.md +73 -9
- data/README.md +31 -32
- data/Rakefile +8 -0
- data/bin/protoc-gen-ruby +2 -1
- data/lib/protocol_buffers.rb +14 -3
- data/lib/protocol_buffers/runtime/encoder.rb +1 -1
- data/lib/protocol_buffers/runtime/field.rb +44 -0
- data/lib/protocol_buffers/runtime/message.rb +63 -0
- data/lib/protocol_buffers/runtime/text_formatter.rb +116 -0
- data/lib/protocol_buffers/runtime/text_parser.ry +136 -0
- data/lib/protocol_buffers/runtime/text_scanner.rb +94 -0
- data/lib/protocol_buffers/version.rb +1 -1
- data/ruby-protocol-buffers.gemspec +2 -1
- data/spec/message_spec.rb +22 -0
- data/spec/negative_int32_spec.rb +10 -10
- data/spec/nil_bugs_spec.rb +1 -1
- data/spec/runtime_spec.rb +2 -2
- data/spec/text_format_spec.rb +679 -0
- data/spec/unicode_string_spec.rb +2 -2
- metadata +46 -48
@@ -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
|
@@ -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.
|
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
|