protobuf 3.6.12 → 3.7.0.pre0
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 +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
|
|