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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +4 -4
  3. data/CHANGES.md +14 -3
  4. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +39 -2
  5. data/lib/protobuf/field.rb +2 -2
  6. data/lib/protobuf/field/base_field.rb +27 -84
  7. data/lib/protobuf/field/bool_field.rb +3 -13
  8. data/lib/protobuf/field/bytes_field.rb +10 -26
  9. data/lib/protobuf/field/enum_field.rb +10 -20
  10. data/lib/protobuf/field/message_field.rb +13 -23
  11. data/lib/protobuf/generators/enum_generator.rb +1 -0
  12. data/lib/protobuf/generators/field_generator.rb +8 -2
  13. data/lib/protobuf/generators/file_generator.rb +8 -0
  14. data/lib/protobuf/generators/service_generator.rb +2 -2
  15. data/lib/protobuf/message.rb +47 -12
  16. data/lib/protobuf/message/fields.rb +80 -8
  17. data/lib/protobuf/rpc/connectors/common.rb +1 -1
  18. data/lib/protobuf/rpc/connectors/ping.rb +2 -2
  19. data/lib/protobuf/rpc/connectors/zmq.rb +1 -1
  20. data/lib/protobuf/rpc/middleware/exception_handler.rb +0 -4
  21. data/lib/protobuf/rpc/middleware/logger.rb +0 -4
  22. data/lib/protobuf/rpc/middleware/request_decoder.rb +16 -11
  23. data/lib/protobuf/rpc/middleware/response_encoder.rb +20 -15
  24. data/lib/protobuf/rpc/service.rb +10 -2
  25. data/lib/protobuf/rpc/service_directory.rb +0 -8
  26. data/lib/protobuf/rpc/service_dispatcher.rb +6 -5
  27. data/lib/protobuf/rpc/service_filters.rb +30 -8
  28. data/lib/protobuf/version.rb +1 -1
  29. data/proto/google/protobuf/descriptor.proto +190 -31
  30. data/spec/lib/protobuf/field/bool_field_spec.rb +33 -7
  31. data/spec/lib/protobuf/field/enum_field_spec.rb +26 -0
  32. data/spec/lib/protobuf/field/float_field_spec.rb +32 -1
  33. data/spec/lib/protobuf/field/int32_field_spec.rb +32 -1
  34. data/spec/lib/protobuf/field/message_field_spec.rb +79 -0
  35. data/spec/lib/protobuf/field/string_field_spec.rb +34 -0
  36. data/spec/lib/protobuf/field_spec.rb +1 -0
  37. data/spec/lib/protobuf/generators/enum_generator_spec.rb +9 -0
  38. data/spec/lib/protobuf/generators/service_generator_spec.rb +9 -0
  39. data/spec/lib/protobuf/message_spec.rb +328 -63
  40. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +3 -3
  41. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +18 -1
  42. data/spec/support/protos/enum.pb.rb +1 -1
  43. data/spec/support/protos/google_unittest.pb.rb +113 -111
  44. data/spec/support/protos/google_unittest.proto +7 -0
  45. data/spec/support/protos/multi_field_extensions.pb.rb +1 -1
  46. data/spec/support/protos/resource.pb.rb +9 -9
  47. metadata +79 -5
@@ -1,8 +1,8 @@
1
- require 'protobuf/field/varint_field'
1
+ require 'protobuf/field/integer_field'
2
2
 
3
3
  module Protobuf
4
4
  module Field
5
- class EnumField < VarintField
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
- value if acceptable?(value)
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
- private
33
+ def coerce!(value)
34
+ return nil if value.nil?
34
35
 
35
- ##
36
- # Private Instance Methods
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
- def define_setter
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
@@ -32,6 +32,7 @@ module Protobuf
32
32
 
33
33
  def build_value(enum_value_descriptor)
34
34
  name = enum_value_descriptor.name
35
+ name.upcase! if ENV.key?('PB_UPCASE_ENUMS')
35
36
  number = enum_value_descriptor.number
36
37
  "define :#{name}, #{number}"
37
38
  end
@@ -20,7 +20,13 @@ module Protobuf
20
20
  attr_reader :field_options
21
21
 
22
22
  def applicable_options
23
- @applicable_options ||= field_options.map { |k, v| ":#{k} => #{v}" }
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 ||= ":#{descriptor.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
- "rpc :#{name.underscore}, #{request_klass}, #{response_klass}"
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
@@ -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 = __send__(field.getter)
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.getter]
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
- @values.key?(name)
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, send(field.name).inspect].join('=')
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
- (__send__(key).present? || [true, false].include?(__send__(key)))
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 = __send__(field_name)
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[field_name] = hashed_value
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.__send__(field.name)
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
- __send__(field.getter)
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
- __send__(field.setter, value) unless value.nil?
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.__send__(name).replace(value.map { |v| duplicate.call(v) })
231
+ object[name].replace(value.map { |v| duplicate.call(v) })
197
232
  else
198
- object.__send__("#{name}=", duplicate.call(value))
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, field_name, tag, options)
96
- raise_if_tag_collision(tag, field_name)
97
- raise_if_name_collision(field_name)
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, field_name, tag, options)
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[field_name] = field
102
- field_store[field_name.to_s] = field
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
- define_method("#{field_name}!") do
108
- @values[field_name]
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
 
@@ -119,7 +119,7 @@ module Protobuf
119
119
  def setup_connection
120
120
  initialize_stats
121
121
  @request_data = request_bytes
122
- @stats.request_size = @request_data.size
122
+ @stats.request_size = request_bytes.size
123
123
  end
124
124
 
125
125
  def succeed(response)
@@ -29,9 +29,9 @@ module Protobuf
29
29
  def timeout
30
30
  @timeout ||= begin
31
31
  if ::ENV.key?("PB_RPC_PING_PORT_TIMEOUT")
32
- ::ENV["PB_RPC_PING_PORT_TIMEOUT"].to_f / 1000
32
+ ::ENV["PB_RPC_PING_PORT_TIMEOUT"].to_i
33
33
  else
34
- 0.2 # 200 ms
34
+ 5 # 5 seconds
35
35
  end
36
36
  end
37
37
  end
@@ -226,7 +226,7 @@ module Protobuf
226
226
 
227
227
  # Alias for ::Protobuf::Rpc::ServiceDirectory.instance
228
228
  def service_directory
229
- ::Protobuf::Rpc.service_directory
229
+ ::Protobuf::Rpc::ServiceDirectory.instance
230
230
  end
231
231
 
232
232
  def snd_timeout
@@ -11,10 +11,6 @@ module Protobuf
11
11
  end
12
12
 
13
13
  def call(env)
14
- dup._call(env)
15
- end
16
-
17
- def _call(env)
18
14
  app.call(env)
19
15
  rescue => exception
20
16
  log_exception(exception)