protobuf 3.6.12 → 3.7.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +4 -4
- data/CHANGES.md +14 -3
- data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +39 -2
- data/lib/protobuf/field.rb +2 -2
- data/lib/protobuf/field/base_field.rb +27 -84
- data/lib/protobuf/field/bool_field.rb +3 -13
- data/lib/protobuf/field/bytes_field.rb +10 -26
- data/lib/protobuf/field/enum_field.rb +10 -20
- data/lib/protobuf/field/message_field.rb +13 -23
- data/lib/protobuf/generators/enum_generator.rb +1 -0
- data/lib/protobuf/generators/field_generator.rb +8 -2
- data/lib/protobuf/generators/file_generator.rb +8 -0
- data/lib/protobuf/generators/service_generator.rb +2 -2
- data/lib/protobuf/message.rb +47 -12
- data/lib/protobuf/message/fields.rb +80 -8
- data/lib/protobuf/rpc/connectors/common.rb +1 -1
- data/lib/protobuf/rpc/connectors/ping.rb +2 -2
- data/lib/protobuf/rpc/connectors/zmq.rb +1 -1
- data/lib/protobuf/rpc/middleware/exception_handler.rb +0 -4
- data/lib/protobuf/rpc/middleware/logger.rb +0 -4
- data/lib/protobuf/rpc/middleware/request_decoder.rb +16 -11
- data/lib/protobuf/rpc/middleware/response_encoder.rb +20 -15
- data/lib/protobuf/rpc/service.rb +10 -2
- data/lib/protobuf/rpc/service_directory.rb +0 -8
- data/lib/protobuf/rpc/service_dispatcher.rb +6 -5
- data/lib/protobuf/rpc/service_filters.rb +30 -8
- data/lib/protobuf/version.rb +1 -1
- data/proto/google/protobuf/descriptor.proto +190 -31
- data/spec/lib/protobuf/field/bool_field_spec.rb +33 -7
- data/spec/lib/protobuf/field/enum_field_spec.rb +26 -0
- data/spec/lib/protobuf/field/float_field_spec.rb +32 -1
- data/spec/lib/protobuf/field/int32_field_spec.rb +32 -1
- data/spec/lib/protobuf/field/message_field_spec.rb +79 -0
- data/spec/lib/protobuf/field/string_field_spec.rb +34 -0
- data/spec/lib/protobuf/field_spec.rb +1 -0
- data/spec/lib/protobuf/generators/enum_generator_spec.rb +9 -0
- data/spec/lib/protobuf/generators/service_generator_spec.rb +9 -0
- data/spec/lib/protobuf/message_spec.rb +328 -63
- data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +3 -3
- data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +18 -1
- data/spec/support/protos/enum.pb.rb +1 -1
- data/spec/support/protos/google_unittest.pb.rb +113 -111
- data/spec/support/protos/google_unittest.proto +7 -0
- data/spec/support/protos/multi_field_extensions.pb.rb +1 -1
- data/spec/support/protos/resource.pb.rb +9 -9
- metadata +79 -5
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'protobuf/field/
|
1
|
+
require 'protobuf/field/integer_field'
|
2
2
|
|
3
3
|
module Protobuf
|
4
4
|
module Field
|
5
|
-
class EnumField <
|
5
|
+
class EnumField < IntegerField
|
6
6
|
|
7
7
|
##
|
8
8
|
# Class Methods
|
@@ -25,36 +25,26 @@ module Protobuf
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def decode(value)
|
28
|
-
|
28
|
+
decoded = super(value)
|
29
|
+
decoded if acceptable?(decoded)
|
29
30
|
end
|
30
31
|
|
31
32
|
def enum?
|
32
33
|
true
|
33
34
|
end
|
34
35
|
|
36
|
+
def coerce!(value)
|
37
|
+
enum_value = type_class.fetch(value)
|
38
|
+
fail TypeError, "Invalid Enum value: #{value.inspect} for #{name}" unless enum_value
|
39
|
+
enum_value
|
40
|
+
end
|
41
|
+
|
35
42
|
private
|
36
43
|
|
37
44
|
##
|
38
45
|
# Private Instance Methods
|
39
46
|
#
|
40
47
|
|
41
|
-
def define_setter
|
42
|
-
field = self
|
43
|
-
message_class.class_eval do
|
44
|
-
define_method("#{field.name}=") do |value|
|
45
|
-
orig_value = value
|
46
|
-
if value.nil?
|
47
|
-
@values.delete(field.name)
|
48
|
-
else
|
49
|
-
value = field.type_class.fetch(value)
|
50
|
-
fail TypeError, "Invalid Enum value: #{orig_value.inspect} for #{field.name}" unless value
|
51
|
-
|
52
|
-
@values[field.name] = value
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
48
|
def typed_default_value
|
59
49
|
if default.is_a?(Symbol)
|
60
50
|
type_class.const_get(default)
|
@@ -9,7 +9,7 @@ module Protobuf
|
|
9
9
|
#
|
10
10
|
|
11
11
|
def acceptable?(val)
|
12
|
-
val.is_a?(type_class) || val.respond_to?(:to_hash)
|
12
|
+
val.is_a?(type_class) || val.respond_to?(:to_hash) || val.respond_to?(:to_proto)
|
13
13
|
end
|
14
14
|
|
15
15
|
def decode(bytes)
|
@@ -30,30 +30,20 @@ module Protobuf
|
|
30
30
|
::Protobuf::WireType::LENGTH_DELIMITED
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
def coerce!(value)
|
34
|
+
return nil if value.nil?
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
coerced_value = if value.respond_to?(:to_proto)
|
37
|
+
value.to_proto
|
38
|
+
elsif value.respond_to?(:to_hash)
|
39
|
+
type_class.new(value.to_hash)
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
|
44
|
+
return coerced_value if coerced_value.is_a?(type_class)
|
38
45
|
|
39
|
-
|
40
|
-
field = self
|
41
|
-
message_class.class_eval do
|
42
|
-
define_method("#{field.name}=") do |val|
|
43
|
-
case
|
44
|
-
when val.nil?
|
45
|
-
@values.delete(field.name)
|
46
|
-
when val.is_a?(field.type_class)
|
47
|
-
@values[field.name] = val
|
48
|
-
when val.respond_to?(:to_proto)
|
49
|
-
@values[field.name] = val.to_proto
|
50
|
-
when val.respond_to?(:to_hash)
|
51
|
-
@values[field.name] = field.type_class.new(val.to_hash)
|
52
|
-
else
|
53
|
-
fail TypeError, "Expected value of type '#{field.type_class}' for field #{field.name}, but got '#{val.class}'"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
46
|
+
fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{value.class}'"
|
57
47
|
end
|
58
48
|
|
59
49
|
end
|
@@ -20,7 +20,13 @@ module Protobuf
|
|
20
20
|
attr_reader :field_options
|
21
21
|
|
22
22
|
def applicable_options
|
23
|
-
|
23
|
+
# Note on the strange use of `#inspect`:
|
24
|
+
# :boom.inspect #=> ":boom"
|
25
|
+
# :".boom.foo".inspect #=> ":\".boom.foo\""
|
26
|
+
# An alternative to `#inspect` would be always adding double quotes,
|
27
|
+
# but the generatated code looks un-idiomatic:
|
28
|
+
# ":\"#{:boom}\"" #=> ":\"boom\"" <-- Note the unnecessary double quotes
|
29
|
+
@applicable_options ||= field_options.map { |k, v| "#{k.inspect} => #{v}" }
|
24
30
|
end
|
25
31
|
|
26
32
|
def default_value
|
@@ -64,7 +70,7 @@ module Protobuf
|
|
64
70
|
end
|
65
71
|
|
66
72
|
def name
|
67
|
-
@name ||=
|
73
|
+
@name ||= descriptor.name.to_sym.inspect
|
68
74
|
end
|
69
75
|
|
70
76
|
def number
|
@@ -64,6 +64,11 @@ module Protobuf
|
|
64
64
|
# the value is an array of field descriptors.
|
65
65
|
#
|
66
66
|
def map_extensions(descriptor, namespaces)
|
67
|
+
if fully_qualified_token?(descriptor.name)
|
68
|
+
fully_qualified_namespace = descriptor.name
|
69
|
+
elsif !(namespace = namespaces.reject(&:empty?).join(".")).empty?
|
70
|
+
fully_qualified_namespace = ".#{namespace}"
|
71
|
+
end
|
67
72
|
# Record all the message descriptor name's we encounter (should be the whole tree).
|
68
73
|
if descriptor.is_a?(::Google::Protobuf::DescriptorProto)
|
69
74
|
if fully_qualified_token?(descriptor.name)
|
@@ -75,6 +80,9 @@ module Protobuf
|
|
75
80
|
end
|
76
81
|
|
77
82
|
descriptor.extension.each do |field_descriptor|
|
83
|
+
unless fully_qualified_token?(field_descriptor.name) && fully_qualified_namespace
|
84
|
+
field_descriptor.name = "#{fully_qualified_namespace}.#{field_descriptor.name}"
|
85
|
+
end
|
78
86
|
@extension_fields[field_descriptor.extendee] << field_descriptor
|
79
87
|
end
|
80
88
|
|
@@ -15,10 +15,10 @@ module Protobuf
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def build_method(method_descriptor)
|
18
|
-
name = method_descriptor.name
|
19
18
|
request_klass = modulize(method_descriptor.input_type)
|
20
19
|
response_klass = modulize(method_descriptor.output_type)
|
21
|
-
|
20
|
+
name = ENV.key?('PB_USE_RAW_RPC_NAMES') ? method_descriptor.name : method_descriptor.name.underscore
|
21
|
+
"rpc :#{name}, #{request_klass}, #{response_klass}"
|
22
22
|
end
|
23
23
|
|
24
24
|
end
|
data/lib/protobuf/message.rb
CHANGED
@@ -77,14 +77,14 @@ module Protobuf
|
|
77
77
|
return to_enum(:each_field) unless block_given?
|
78
78
|
|
79
79
|
self.class.all_fields.each do |field|
|
80
|
-
value =
|
80
|
+
value = self[field.name]
|
81
81
|
yield(field, value)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
def each_field_for_serialization
|
86
86
|
self.class.all_fields.each do |field|
|
87
|
-
value = @values[field.
|
87
|
+
value = @values[field.fully_qualified_name]
|
88
88
|
if value.nil?
|
89
89
|
fail ::Protobuf::SerializationError, "Required field #{self.class.name}##{field.name} does not have a value." if field.required?
|
90
90
|
next
|
@@ -95,13 +95,15 @@ module Protobuf
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def field?(name)
|
98
|
-
|
98
|
+
field = self.class.get_field(name, true)
|
99
|
+
return false if field.nil?
|
100
|
+
@values.key?(field.fully_qualified_name)
|
99
101
|
end
|
100
102
|
::Protobuf.deprecator.define_deprecated_methods(self, :has_field? => :field?)
|
101
103
|
|
102
104
|
def inspect
|
103
105
|
attrs = self.class.fields.map do |field|
|
104
|
-
[field.name,
|
106
|
+
[field.name, self[field.name].inspect].join('=')
|
105
107
|
end.join(' ')
|
106
108
|
|
107
109
|
"#<#{self.class} #{attrs}>"
|
@@ -113,7 +115,7 @@ module Protobuf
|
|
113
115
|
|
114
116
|
def respond_to_has_and_present?(key)
|
115
117
|
respond_to_has?(key) &&
|
116
|
-
(
|
118
|
+
(self[key].present? || [true, false].include?(self[key]))
|
117
119
|
end
|
118
120
|
|
119
121
|
# Return a hash-representation of the given fields for this message type.
|
@@ -121,9 +123,10 @@ module Protobuf
|
|
121
123
|
result = {}
|
122
124
|
|
123
125
|
@values.each_key do |field_name|
|
124
|
-
value =
|
126
|
+
value = self[field_name]
|
127
|
+
field = self.class.get_field(field_name, true)
|
125
128
|
hashed_value = value.respond_to?(:to_hash_value) ? value.to_hash_value : value
|
126
|
-
result[
|
129
|
+
result[field.name] = hashed_value
|
127
130
|
end
|
128
131
|
|
129
132
|
result
|
@@ -140,20 +143,52 @@ module Protobuf
|
|
140
143
|
def ==(other)
|
141
144
|
return false unless other.is_a?(self.class)
|
142
145
|
each_field do |field, value|
|
143
|
-
return false unless value == other
|
146
|
+
return false unless value == other[field.name]
|
144
147
|
end
|
145
148
|
true
|
146
149
|
end
|
147
150
|
|
148
151
|
def [](name)
|
149
152
|
if (field = self.class.get_field(name, true))
|
150
|
-
|
153
|
+
if field.repeated?
|
154
|
+
@values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldArray.new(field)
|
155
|
+
elsif @values.key?(field.fully_qualified_name)
|
156
|
+
@values[field.fully_qualified_name]
|
157
|
+
else
|
158
|
+
field.default_value
|
159
|
+
end
|
160
|
+
else
|
161
|
+
fail ArgumentError, "invalid field name=#{name.inspect}"
|
151
162
|
end
|
152
163
|
end
|
153
164
|
|
154
165
|
def []=(name, value)
|
155
166
|
if (field = self.class.get_field(name, true))
|
156
|
-
|
167
|
+
if field.repeated?
|
168
|
+
if value.is_a?(Array)
|
169
|
+
value = value.compact
|
170
|
+
else
|
171
|
+
fail TypeError, <<-TYPE_ERROR
|
172
|
+
Expected repeated value of type '#{field.type_class}'
|
173
|
+
Got '#{value.class}' for repeated protobuf field #{field.name}
|
174
|
+
TYPE_ERROR
|
175
|
+
end
|
176
|
+
|
177
|
+
if value.empty?
|
178
|
+
@values.delete(field.fully_qualified_name)
|
179
|
+
else
|
180
|
+
@values[field.fully_qualified_name] ||= ::Protobuf::Field::FieldArray.new(field)
|
181
|
+
@values[field.fully_qualified_name].replace(value)
|
182
|
+
end
|
183
|
+
else
|
184
|
+
if value.nil?
|
185
|
+
@values.delete(field.fully_qualified_name)
|
186
|
+
elsif field.acceptable?(value)
|
187
|
+
@values[field.fully_qualified_name] = field.coerce!(value)
|
188
|
+
else
|
189
|
+
fail TypeError, "Unacceptable value #{value} for field #{field.name} of type #{field.type_class}"
|
190
|
+
end
|
191
|
+
end
|
157
192
|
else
|
158
193
|
unless ::Protobuf.ignore_unknown_fields?
|
159
194
|
fail ::Protobuf::FieldNotDefinedError, name
|
@@ -193,9 +228,9 @@ module Protobuf
|
|
193
228
|
object.__send__(:initialize)
|
194
229
|
@values.each do |name, value|
|
195
230
|
if value.is_a?(::Protobuf::Field::FieldArray)
|
196
|
-
object
|
231
|
+
object[name].replace(value.map { |v| duplicate.call(v) })
|
197
232
|
else
|
198
|
-
object
|
233
|
+
object[name] = duplicate.call(value)
|
199
234
|
end
|
200
235
|
end
|
201
236
|
object
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require "set"
|
2
|
+
|
1
3
|
module Protobuf
|
2
4
|
class Message
|
3
5
|
module Fields
|
4
6
|
|
7
|
+
ACCESSOR_SUFFIXES = ["", "=", "!", "?"].freeze
|
8
|
+
|
5
9
|
def self.extended(other)
|
6
10
|
other.extend(ClassMethods)
|
7
11
|
::Protobuf.deprecator.define_deprecated_methods(
|
@@ -92,20 +96,88 @@ module Protobuf
|
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
95
|
-
def define_field(rule, type_class,
|
96
|
-
raise_if_tag_collision(tag,
|
97
|
-
raise_if_name_collision(
|
99
|
+
def define_field(rule, type_class, fully_qualified_field_name, tag, options)
|
100
|
+
raise_if_tag_collision(tag, fully_qualified_field_name)
|
101
|
+
raise_if_name_collision(fully_qualified_field_name)
|
102
|
+
|
103
|
+
# Determine appropirate accessor for fields depending on name collisions via extensions:
|
104
|
+
|
105
|
+
# Case 1: Base field = "string_field" and no extensions of the same name
|
106
|
+
# Result:
|
107
|
+
# message.string_field #=> @values["string_field"]
|
108
|
+
# message[:string_field] #=> @values["string_field"]
|
109
|
+
# message['string_field'] #=> @values["string_field"]
|
110
|
+
|
111
|
+
# Case 2: Base field = "string_field" and extension 1 = ".my_package.string_field", extension N = ".package_N.string_field"...
|
112
|
+
# Result:
|
113
|
+
# message.string_field #=> @values["string_field"]
|
114
|
+
# message[:string_field] #=> @values["string_field"]
|
115
|
+
# message['string_field'] #=> @values["string_field"]
|
116
|
+
# message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
|
117
|
+
# message['.my_package.string_field'] #=> @values[".my_package.string_field"]
|
118
|
+
|
119
|
+
# Case 3: No base field, extension 1 = ".my_package.string_field", extension 2 = ".other_package.string_field", extension N...
|
120
|
+
# Result:
|
121
|
+
# message.string_field #=> raise NoMethodError (no simple accessor allowed)
|
122
|
+
# message[:string_field] #=> raise NoMethodError (no simple accessor allowed)
|
123
|
+
# message['string_field'] #=> raise NoMethodError (no simple accessor allowed)
|
124
|
+
# message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
|
125
|
+
# message['.my_package.string_field'] #=> @values[".my_package.string_field"]
|
126
|
+
# message[:'.other_package.string_field'] #=> @values[".other_package.string_field"]
|
127
|
+
# message['.other_package.string_field'] #=> @values[".other_package.string_field"]
|
128
|
+
|
129
|
+
# Case 4: No base field, extension = ".my_package.string_field", no other extensions
|
130
|
+
# Result:
|
131
|
+
# message.string_field #=> @values[".my_package.string_field"]
|
132
|
+
# message[:string_field] #=> @values[".my_package.string_field"]
|
133
|
+
# message['string_field'] #=> @values[".my_package.string_field"]
|
134
|
+
# message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
|
135
|
+
# message[:'.my_package.string_field'] #=> @values[".my_package.string_field"]
|
136
|
+
|
137
|
+
if options[:extension]
|
138
|
+
base_name = fully_qualified_field_name.to_s.split('.').last.to_sym
|
139
|
+
if field_store[base_name]
|
140
|
+
# Case 3
|
141
|
+
if field_store[base_name].extension?
|
142
|
+
remove_existing_accessors(base_name)
|
143
|
+
simple_name = nil
|
144
|
+
# Case 2
|
145
|
+
else
|
146
|
+
simple_name = nil
|
147
|
+
end
|
148
|
+
# Case 4
|
149
|
+
else
|
150
|
+
simple_name = base_name
|
151
|
+
end
|
152
|
+
else
|
153
|
+
# Case 1
|
154
|
+
simple_name = fully_qualified_field_name
|
155
|
+
end
|
98
156
|
|
99
|
-
field = ::Protobuf::Field.build(self, rule, type_class,
|
157
|
+
field = ::Protobuf::Field.build(self, rule, type_class, fully_qualified_field_name,
|
158
|
+
tag, simple_name, options)
|
100
159
|
field_store[tag] = field
|
101
|
-
field_store[
|
102
|
-
field_store[
|
160
|
+
field_store[fully_qualified_field_name.to_sym] = field
|
161
|
+
field_store[fully_qualified_field_name.to_s] = field
|
162
|
+
if simple_name && simple_name != fully_qualified_field_name
|
163
|
+
field_store[simple_name.to_sym] = field
|
164
|
+
field_store[simple_name.to_s] = field
|
165
|
+
end
|
103
166
|
# defining a new field for the message will cause cached @all_fields, @extension_fields,
|
104
167
|
# and @fields to be incorrect; reset them
|
105
168
|
@all_fields = @extension_fields = @fields = nil
|
169
|
+
end
|
106
170
|
|
107
|
-
|
108
|
-
|
171
|
+
def remove_existing_accessors(accessor)
|
172
|
+
field_store.delete(accessor.to_sym).try(:fully_qualified_name_only!)
|
173
|
+
field_store.delete(accessor.to_s)
|
174
|
+
ACCESSOR_SUFFIXES.each do |modifier|
|
175
|
+
begin
|
176
|
+
remove_method("#{accessor}#{modifier}")
|
177
|
+
# rubocop: disable Lint/HandleExceptions
|
178
|
+
rescue NameError
|
179
|
+
# Do not remove the method
|
180
|
+
end
|
109
181
|
end
|
110
182
|
end
|
111
183
|
|