ruby-protocol-buffers 1.5.1 → 1.6.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.
- 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
|