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,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe Protobuf::Field::
|
3
|
+
RSpec.describe Protobuf::Field::BoolField do
|
4
4
|
|
5
5
|
class SomeBoolMessage < ::Protobuf::Message
|
6
6
|
optional :bool, :some_bool, 1
|
@@ -9,7 +9,7 @@ RSpec.describe Protobuf::Field::Int32Field do
|
|
9
9
|
|
10
10
|
let(:instance) { SomeBoolMessage.new }
|
11
11
|
|
12
|
-
describe '
|
12
|
+
describe 'setting and getting field' do
|
13
13
|
subject { instance.some_bool = value; instance.some_bool }
|
14
14
|
|
15
15
|
[true, false].each do |val|
|
@@ -49,12 +49,38 @@ RSpec.describe Protobuf::Field::Int32Field do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
it 'defines ? method' do
|
53
|
+
instance.required_bool = false
|
54
|
+
expect(instance.required_bool?).to be(false)
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#default_value' do
|
58
|
+
context 'optional and required fields' do
|
59
|
+
it 'returns the class default' do
|
60
|
+
expect(SomeBoolMessage.get_field('some_bool').default).to be nil
|
61
|
+
expect(::Protobuf::Field::BoolField.default).to be false
|
62
|
+
expect(instance.some_bool).to be false
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with field default' do
|
66
|
+
class AnotherBoolMessage < ::Protobuf::Message
|
67
|
+
optional :bool, :set_bool, 1, :default => true
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns the set default' do
|
71
|
+
expect(AnotherBoolMessage.get_field('set_bool').default).to be true
|
72
|
+
expect(AnotherBoolMessage.new.set_bool).to be true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'repeated field' do
|
78
|
+
class RepeatedBoolMessage < ::Protobuf::Message
|
79
|
+
repeated :bool, :repeated_bool, 1
|
80
|
+
end
|
55
81
|
|
56
|
-
it 'returns
|
57
|
-
expect(
|
82
|
+
it 'returns the set default' do
|
83
|
+
expect(RepeatedBoolMessage.new.repeated_bool).to eq []
|
58
84
|
end
|
59
85
|
end
|
60
86
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Protobuf::Field::EnumField do
|
4
|
+
let(:message) do
|
5
|
+
Class.new(::Protobuf::Message) do
|
6
|
+
enum_class = Class.new(::Protobuf::Enum) do
|
7
|
+
define :POSITIVE, 22
|
8
|
+
define :NEGATIVE, -33
|
9
|
+
end
|
10
|
+
|
11
|
+
optional enum_class, :enum, 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.{encode, decode}' do
|
16
|
+
it 'handles positive enum constants' do
|
17
|
+
instance = message.new(:enum => :POSITIVE)
|
18
|
+
expect(message.decode(instance.encode).enum).to eq(22)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'handles negative enum constants' do
|
22
|
+
instance = message.new(:enum => :NEGATIVE)
|
23
|
+
expect(message.decode(instance.encode).enum).to eq(-33)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -8,7 +8,7 @@ RSpec.describe Protobuf::Field::FloatField do
|
|
8
8
|
|
9
9
|
let(:instance) { SomeFloatMessage.new }
|
10
10
|
|
11
|
-
describe '
|
11
|
+
describe 'setting and getting field' do
|
12
12
|
subject { instance.some_float = value; instance.some_float }
|
13
13
|
|
14
14
|
context 'when set with an int' do
|
@@ -52,4 +52,35 @@ RSpec.describe Protobuf::Field::FloatField do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
describe '#default_value' do
|
56
|
+
context 'optional and required fields' do
|
57
|
+
it 'returns the class default' do
|
58
|
+
expect(SomeFloatMessage.get_field('some_float').default).to be nil
|
59
|
+
expect(::Protobuf::Field::FloatField.default).to eq 0.0
|
60
|
+
expect(instance.some_float).to eq 0.0
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with field default' do
|
64
|
+
class AnotherFloatMessage < ::Protobuf::Message
|
65
|
+
optional :float, :set_float, 1, :default => 3.6
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns the set default' do
|
69
|
+
expect(AnotherFloatMessage.get_field('set_float').default).to eq 3.6
|
70
|
+
expect(AnotherFloatMessage.new.set_float).to eq 3.6
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'repeated field' do
|
76
|
+
class RepeatedFloatMessage < ::Protobuf::Message
|
77
|
+
repeated :float, :repeated_float, 1
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns the set default' do
|
81
|
+
expect(RepeatedFloatMessage.new.repeated_float).to eq []
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
55
86
|
end
|
@@ -10,7 +10,7 @@ RSpec.describe Protobuf::Field::Int32Field do
|
|
10
10
|
|
11
11
|
let(:instance) { SomeInt32Message.new }
|
12
12
|
|
13
|
-
describe '
|
13
|
+
describe 'setting and getting a field' do
|
14
14
|
subject { instance.some_int = value; instance.some_int }
|
15
15
|
|
16
16
|
context 'when set with an int' do
|
@@ -86,4 +86,35 @@ RSpec.describe Protobuf::Field::Int32Field do
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
describe '#default_value' do
|
90
|
+
context 'optional and required fields' do
|
91
|
+
it 'returns the class default' do
|
92
|
+
expect(SomeInt32Message.get_field('some_int').default).to be nil
|
93
|
+
expect(::Protobuf::Field::Int32Field.default).to eq 0
|
94
|
+
expect(instance.some_int).to eq 0
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with field default' do
|
98
|
+
class AnotherIntMessage < ::Protobuf::Message
|
99
|
+
optional :int32, :set_int, 1, :default => 3
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns the set default' do
|
103
|
+
expect(AnotherIntMessage.get_field('set_int').default).to eq 3
|
104
|
+
expect(AnotherIntMessage.new.set_int).to eq 3
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'repeated field' do
|
110
|
+
class RepeatedIntMessage < ::Protobuf::Message
|
111
|
+
repeated :int32, :repeated_int, 1
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns the set default' do
|
115
|
+
expect(RepeatedIntMessage.new.repeated_int).to eq []
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
89
120
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Protobuf::Field::MessageField do
|
4
|
+
let(:field_message) do
|
5
|
+
Class.new(::Protobuf::Message) do
|
6
|
+
optional :int32, :field, 1
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:message) do
|
11
|
+
Class.new(::Protobuf::Message) do
|
12
|
+
optional FieldMessage, :message_field, 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
stub_const('FieldMessage', field_message)
|
18
|
+
stub_const('Message', message)
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:instance) { message.new }
|
22
|
+
|
23
|
+
describe 'setting and getting field' do
|
24
|
+
context "when set with the message type" do
|
25
|
+
it 'is readable as a message' do
|
26
|
+
value = field_message.new(:field => 34)
|
27
|
+
instance.message_field = value
|
28
|
+
expect(instance.message_field).to eq(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when set with #to_proto" do
|
33
|
+
let(:to_proto_message) do
|
34
|
+
Class.new do
|
35
|
+
def to_proto
|
36
|
+
FieldMessage.new(:field => 42)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'is readable as a message' do
|
42
|
+
value = to_proto_message.new
|
43
|
+
instance.message_field = value
|
44
|
+
expect(instance.message_field).to eq(value.to_proto)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when set with #to_proto that returns the wrong message type" do
|
49
|
+
let(:to_proto_is_wrong_message) do
|
50
|
+
Class.new do
|
51
|
+
def to_proto
|
52
|
+
Message.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'fails' do
|
58
|
+
value = to_proto_is_wrong_message.new
|
59
|
+
expect { instance.message_field = value }.to raise_error TypeError
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when set with #to_hash" do
|
64
|
+
let(:to_hash_message) do
|
65
|
+
Class.new do
|
66
|
+
def to_hash
|
67
|
+
{ :field => 989 }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'is readable as a message' do
|
73
|
+
value = to_hash_message.new
|
74
|
+
instance.message_field = value
|
75
|
+
expect(instance.message_field).to eq(field_message.new(value.to_hash))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -42,4 +42,38 @@ RSpec.describe ::Protobuf::Field::StringField do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe '#default_value' do
|
46
|
+
context 'optional and required fields' do
|
47
|
+
it 'returns the class default' do
|
48
|
+
class SomeStringMessage < ::Protobuf::Message
|
49
|
+
optional :string, :some_string, 1
|
50
|
+
end
|
51
|
+
expect(SomeStringMessage.get_field('some_string').default).to be nil
|
52
|
+
expect(::Protobuf::Field::StringField.default).to eq ""
|
53
|
+
expect(SomeStringMessage.new.some_string).to eq ""
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with field default' do
|
57
|
+
class AnotherStringMessage < ::Protobuf::Message
|
58
|
+
optional :string, :set_string, 1, :default => "default value this is"
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'returns the set default' do
|
62
|
+
expect(AnotherStringMessage.get_field('set_string').default).to eq "default value this is"
|
63
|
+
expect(AnotherStringMessage.new.set_string).to eq "default value this is"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'repeated field' do
|
69
|
+
class RepeatedStringMessage < ::Protobuf::Message
|
70
|
+
repeated :string, :repeated_string, 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns the set default' do
|
74
|
+
expect(RepeatedStringMessage.new.repeated_string).to eq []
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
45
79
|
end
|
@@ -68,6 +68,15 @@ end
|
|
68
68
|
it 'returns a string identifying the given enum value' do
|
69
69
|
expect(subject.build_value(enum.value.first)).to eq("define :FOO, 1")
|
70
70
|
end
|
71
|
+
|
72
|
+
context 'with PB_UPCASE_ENUMS set' do
|
73
|
+
before { allow(ENV).to receive(:key?).with('PB_UPCASE_ENUMS').and_return(true) }
|
74
|
+
let(:values) { [{ :name => 'boom', :number => 1 }] }
|
75
|
+
|
76
|
+
it 'returns a string with the given enum name in ALL CAPS' do
|
77
|
+
expect(subject.build_value(enum.value.first)).to eq("define :BOOM, 1")
|
78
|
+
end
|
79
|
+
end
|
71
80
|
end
|
72
81
|
|
73
82
|
end
|
@@ -41,6 +41,15 @@ end
|
|
41
41
|
it 'returns a string identifying the given method descriptor' do
|
42
42
|
expect(subject.build_method(service.method.first)).to eq("rpc :search, FooRequest, FooResponse")
|
43
43
|
end
|
44
|
+
|
45
|
+
context 'with PB_USE_RAW_RPC_NAMES in the environemnt' do
|
46
|
+
before { allow(ENV).to receive(:key?).with('PB_USE_RAW_RPC_NAMES').and_return(true) }
|
47
|
+
|
48
|
+
it 'uses the raw RPC name and does not underscore it' do
|
49
|
+
expect(subject.build_method(service.method.first)).to eq("rpc :Search, FooRequest, FooResponse")
|
50
|
+
expect(subject.build_method(service.method.last)).to eq("rpc :FooBar, ::Foo::Request, ::Bar::Response")
|
51
|
+
end
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
end
|
@@ -181,64 +181,6 @@ RSpec.describe Protobuf::Message do
|
|
181
181
|
test_enum = Test::EnumTestMessage.new { |p| p.non_default_enum = 2 }
|
182
182
|
expect(test_enum.non_default_enum).to eq(2)
|
183
183
|
end
|
184
|
-
|
185
|
-
context 'ignoring unknown fields' do
|
186
|
-
around do |example|
|
187
|
-
orig = ::Protobuf.ignore_unknown_fields?
|
188
|
-
::Protobuf.ignore_unknown_fields = true
|
189
|
-
example.call
|
190
|
-
::Protobuf.ignore_unknown_fields = orig
|
191
|
-
end
|
192
|
-
|
193
|
-
context 'with valid fields' do
|
194
|
-
let(:values) { { :name => "Jim" } }
|
195
|
-
|
196
|
-
it "does not raise an error" do
|
197
|
-
expect { ::Test::Resource.new(values) }.to_not raise_error
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
context 'with non-existent field' do
|
202
|
-
let(:values) { { :name => "Jim", :othername => "invalid" } }
|
203
|
-
|
204
|
-
it "does not raise an error" do
|
205
|
-
expect { ::Test::Resource.new(values) }.to_not raise_error
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
context 'not ignoring unknown fields' do
|
211
|
-
around do |example|
|
212
|
-
orig = ::Protobuf.ignore_unknown_fields?
|
213
|
-
::Protobuf.ignore_unknown_fields = false
|
214
|
-
example.call
|
215
|
-
::Protobuf.ignore_unknown_fields = orig
|
216
|
-
end
|
217
|
-
|
218
|
-
context 'with valid fields' do
|
219
|
-
let(:values) { { :name => "Jim" } }
|
220
|
-
|
221
|
-
it "does not raise an error" do
|
222
|
-
expect { ::Test::Resource.new(values) }.to_not raise_error
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
context 'with non-existent field' do
|
227
|
-
let(:values) { { :name => "Jim", :othername => "invalid" } }
|
228
|
-
|
229
|
-
it "raises an error and mentions the erroneous field" do
|
230
|
-
expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
|
231
|
-
end
|
232
|
-
|
233
|
-
context 'with a nil value' do
|
234
|
-
let(:values) { { :name => "Jim", :othername => nil } }
|
235
|
-
|
236
|
-
it "raises an error and mentions the erroneous field" do
|
237
|
-
expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
184
|
end
|
243
185
|
|
244
186
|
describe '#encode' do
|
@@ -448,6 +390,29 @@ RSpec.describe Protobuf::Message do
|
|
448
390
|
)
|
449
391
|
end
|
450
392
|
end
|
393
|
+
|
394
|
+
it 'uses simple field names as keys when possible and fully qualified names otherwise' do
|
395
|
+
message = Class.new(::Protobuf::Message) do
|
396
|
+
optional :int32, :field, 1
|
397
|
+
optional :int32, :colliding_field, 2
|
398
|
+
extensions 100...200
|
399
|
+
optional :int32, :".ext.normal_ext_field", 100, :extension => true
|
400
|
+
optional :int32, :".ext.colliding_field", 101, :extension => true
|
401
|
+
optional :int32, :".ext.colliding_field2", 102, :extension => true
|
402
|
+
optional :int32, :".ext2.colliding_field2", 103, :extension => true
|
403
|
+
end
|
404
|
+
|
405
|
+
hash = {
|
406
|
+
:field => 1,
|
407
|
+
:colliding_field => 2,
|
408
|
+
:normal_ext_field => 3,
|
409
|
+
:".ext.colliding_field" => 4,
|
410
|
+
:".ext.colliding_field2" => 5,
|
411
|
+
:".ext2.colliding_field2" => 6,
|
412
|
+
}
|
413
|
+
instance = message.new(hash)
|
414
|
+
expect(instance.to_hash).to eq(hash)
|
415
|
+
end
|
451
416
|
end
|
452
417
|
|
453
418
|
describe '#to_json' do
|
@@ -468,16 +433,40 @@ RSpec.describe Protobuf::Message do
|
|
468
433
|
end
|
469
434
|
end
|
470
435
|
|
471
|
-
describe "#
|
436
|
+
describe "#define_accessor" do
|
472
437
|
subject { ::Test::Resource.new }
|
473
438
|
|
474
|
-
it
|
439
|
+
it 'allows string fields to be set to nil' do
|
475
440
|
expect { subject.name = nil }.to_not raise_error
|
476
441
|
end
|
477
442
|
|
478
|
-
it
|
443
|
+
it 'does not allow string fields to be set to Numeric' do
|
479
444
|
expect { subject.name = 1 }.to raise_error(/name/)
|
480
445
|
end
|
446
|
+
|
447
|
+
context '#{simple_field_name}!' do
|
448
|
+
it 'returns value of set field' do
|
449
|
+
expect(::Test::Resource.new(:name => "Joe").name!).to eq("Joe")
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'returns value of set field with default' do
|
453
|
+
expect(::Test::Resource.new(:name => "").name!).to eq("")
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'returns nil if extension field is unset' do
|
457
|
+
expect(subject.ext_is_searchable!).to be_nil
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'returns value of set extension field' do
|
461
|
+
message = ::Test::Resource.new(:ext_is_searchable => true)
|
462
|
+
expect(message.ext_is_searchable!).to be(true)
|
463
|
+
end
|
464
|
+
|
465
|
+
it 'returns value of set extension field with default' do
|
466
|
+
message = ::Test::Resource.new(:ext_is_searchable => false)
|
467
|
+
expect(message.ext_is_searchable!).to be(false)
|
468
|
+
end
|
469
|
+
end
|
481
470
|
end
|
482
471
|
|
483
472
|
describe '.get_extension_field' do
|
@@ -486,12 +475,15 @@ RSpec.describe Protobuf::Message do
|
|
486
475
|
expect(field).to be_a(::Protobuf::Field::BoolField)
|
487
476
|
expect(field.tag).to eq(100)
|
488
477
|
expect(field.name).to eq(:ext_is_searchable)
|
478
|
+
expect(field.fully_qualified_name).to eq(:'.test.Searchable.ext_is_searchable')
|
489
479
|
expect(field).to be_extension
|
490
480
|
end
|
491
481
|
|
492
482
|
it 'fetches an extension field by its symbolized name' do
|
493
483
|
expect(::Test::Resource.get_extension_field(:ext_is_searchable)).to be_a(::Protobuf::Field::BoolField)
|
494
484
|
expect(::Test::Resource.get_extension_field('ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
|
485
|
+
expect(::Test::Resource.get_extension_field(:'.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
|
486
|
+
expect(::Test::Resource.get_extension_field('.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
|
495
487
|
end
|
496
488
|
|
497
489
|
it 'returns nil when attempting to get a non-extension field' do
|
@@ -504,12 +496,71 @@ RSpec.describe Protobuf::Message do
|
|
504
496
|
end
|
505
497
|
end
|
506
498
|
|
499
|
+
describe '#field?' do
|
500
|
+
it 'returns false for non-existent field' do
|
501
|
+
expect(::Test::Resource.get_field('doesnotexist')).to be_nil
|
502
|
+
expect(::Test::Resource.new.field?('doesnotexist')).to be(false)
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'returns false for unset field' do
|
506
|
+
expect(::Test::Resource.get_field('name')).to be
|
507
|
+
expect(::Test::Resource.new.field?('name')).to be(false)
|
508
|
+
end
|
509
|
+
|
510
|
+
it 'returns false for unset field from tag' do
|
511
|
+
expect(::Test::Resource.get_field(1)).to be
|
512
|
+
expect(::Test::Resource.new.field?(1)).to be(false)
|
513
|
+
end
|
514
|
+
|
515
|
+
it 'returns true for set field' do
|
516
|
+
expect(::Test::Resource.new(:name => "Joe").field?('name')).to be(true)
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'returns true for set field with default' do
|
520
|
+
expect(::Test::Resource.new(:name => "").field?('name')).to be(true)
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'returns true from field tag value' do
|
524
|
+
expect(::Test::Resource.new(:name => "Joe").field?(1)).to be(true)
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'returns false for unset extension field' do
|
528
|
+
ext_field = :".test.Searchable.ext_is_searchable"
|
529
|
+
expect(::Test::Resource.get_extension_field(ext_field)).to be
|
530
|
+
expect(::Test::Resource.new.field?(ext_field)).to be(false)
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'returns false for unset extension field from tag' do
|
534
|
+
expect(::Test::Resource.get_extension_field(100)).to be
|
535
|
+
expect(::Test::Resource.new.field?(100)).to be(false)
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'returns true for set extension field' do
|
539
|
+
ext_field = :".test.Searchable.ext_is_searchable"
|
540
|
+
message = ::Test::Resource.new(ext_field => true)
|
541
|
+
expect(message.field?(ext_field)).to be(true)
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'returns true for set extension field with default' do
|
545
|
+
ext_field = :".test.Searchable.ext_is_searchable"
|
546
|
+
message = ::Test::Resource.new(ext_field => false)
|
547
|
+
expect(message.field?(ext_field)).to be(true)
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'returns true for set extension field from tag' do
|
551
|
+
ext_field = :".test.Searchable.ext_is_searchable"
|
552
|
+
message = ::Test::Resource.new(ext_field => false)
|
553
|
+
expect(message.field?(100)).to be(true)
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
507
557
|
describe '.get_field' do
|
508
558
|
it 'fetches a non-extension field by its tag' do
|
509
559
|
field = ::Test::Resource.get_field(1)
|
510
560
|
expect(field).to be_a(::Protobuf::Field::StringField)
|
511
561
|
expect(field.tag).to eq(1)
|
512
562
|
expect(field.name).to eq(:name)
|
563
|
+
expect(field.fully_qualified_name).to eq(:name)
|
513
564
|
expect(field).not_to be_extension
|
514
565
|
end
|
515
566
|
|
@@ -520,8 +571,8 @@ RSpec.describe Protobuf::Message do
|
|
520
571
|
|
521
572
|
it 'fetches an extension field when forced' do
|
522
573
|
expect(::Test::Resource.get_field(100, true)).to be_a(::Protobuf::Field::BoolField)
|
523
|
-
expect(::Test::Resource.get_field(:ext_is_searchable, true)).to be_a(::Protobuf::Field::BoolField)
|
524
|
-
expect(::Test::Resource.get_field('ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
|
574
|
+
expect(::Test::Resource.get_field(:'.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
|
575
|
+
expect(::Test::Resource.get_field('.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
|
525
576
|
end
|
526
577
|
|
527
578
|
it 'returns nil when attempting to get an extension field' do
|
@@ -534,4 +585,218 @@ RSpec.describe Protobuf::Message do
|
|
534
585
|
end
|
535
586
|
end
|
536
587
|
|
588
|
+
describe 'defining a field' do
|
589
|
+
# Case 1
|
590
|
+
context 'single base field' do
|
591
|
+
let(:klass) do
|
592
|
+
Class.new(Protobuf::Message) do
|
593
|
+
optional :string, :foo, 1
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
it 'has an accessor for foo' do
|
598
|
+
message = klass.new(:foo => 'bar')
|
599
|
+
expect(message.foo).to eq('bar')
|
600
|
+
expect(message[:foo]).to eq('bar')
|
601
|
+
expect(message['foo']).to eq('bar')
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
# Case 2
|
606
|
+
context 'base field and extension field name collision' do
|
607
|
+
let(:klass) do
|
608
|
+
Class.new(Protobuf::Message) do
|
609
|
+
optional :string, :foo, 1
|
610
|
+
optional :string, :".boom.foo", 2, :extension => true
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'has an accessor for foo that refers to the base field' do
|
615
|
+
message = klass.new(:foo => 'bar', '.boom.foo' => 'bam')
|
616
|
+
expect(message.foo).to eq('bar')
|
617
|
+
expect(message[:foo]).to eq('bar')
|
618
|
+
expect(message['foo']).to eq('bar')
|
619
|
+
expect(message[:'.boom.foo']).to eq('bam')
|
620
|
+
expect(message['.boom.foo']).to eq('bam')
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
# Case 3
|
625
|
+
context 'no base field with extension fields with name collision' do
|
626
|
+
let(:klass) do
|
627
|
+
Class.new(Protobuf::Message) do
|
628
|
+
optional :string, :".boom.foo", 2, :extension => true
|
629
|
+
optional :string, :".goat.foo", 3, :extension => true
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
it 'has an accessor for foo that refers to the extension field' do
|
634
|
+
message = klass.new('.boom.foo' => 'bam', '.goat.foo' => 'red')
|
635
|
+
expect { message.foo }.to raise_error(NoMethodError)
|
636
|
+
expect { message[:foo] }.to raise_error(ArgumentError)
|
637
|
+
expect { message['foo'] }.to raise_error(ArgumentError)
|
638
|
+
expect(message[:'.boom.foo']).to eq('bam')
|
639
|
+
expect(message['.boom.foo']).to eq('bam')
|
640
|
+
expect(message[:'.goat.foo']).to eq('red')
|
641
|
+
expect(message['.goat.foo']).to eq('red')
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
# Case 4
|
646
|
+
context 'no base field with an extension field' do
|
647
|
+
let(:klass) do
|
648
|
+
Class.new(Protobuf::Message) do
|
649
|
+
optional :string, :".boom.foo", 2, :extension => true
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'has an accessor for foo that refers to the extension field' do
|
654
|
+
message = klass.new('.boom.foo' => 'bam')
|
655
|
+
expect(message.foo).to eq('bam')
|
656
|
+
expect(message[:foo]).to eq('bam')
|
657
|
+
expect(message['foo']).to eq('bam')
|
658
|
+
expect(message[:'.boom.foo']).to eq('bam')
|
659
|
+
expect(message['.boom.foo']).to eq('bam')
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
describe '.[]=' do
|
665
|
+
context 'clearing fields' do
|
666
|
+
it 'clears repeated fields with an empty array' do
|
667
|
+
instance = ::Test::Resource.new(:repeated_enum => [::Test::StatusType::ENABLED])
|
668
|
+
expect(instance.field?(:repeated_enum)).to be(true)
|
669
|
+
instance[:repeated_enum] = []
|
670
|
+
expect(instance.field?(:repeated_enum)).to be(false)
|
671
|
+
end
|
672
|
+
|
673
|
+
it 'clears optional fields with nil' do
|
674
|
+
instance = ::Test::Resource.new(:name => "Joe")
|
675
|
+
expect(instance.field?(:name)).to be(true)
|
676
|
+
instance[:name] = nil
|
677
|
+
expect(instance.field?(:name)).to be(false)
|
678
|
+
end
|
679
|
+
|
680
|
+
it 'clears optional extenstion fields with nil' do
|
681
|
+
instance = ::Test::Resource.new(:ext_is_searchable => true)
|
682
|
+
expect(instance.field?(:ext_is_searchable)).to be(true)
|
683
|
+
instance[:ext_is_searchable] = nil
|
684
|
+
expect(instance.field?(:ext_is_searchable)).to be(false)
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
context 'setting fields' do
|
689
|
+
let(:instance) { ::Test::Resource.new }
|
690
|
+
|
691
|
+
it 'sets and replaces repeated fields' do
|
692
|
+
initial = [::Test::StatusType::ENABLED, ::Test::StatusType::DISABLED]
|
693
|
+
instance[:repeated_enum] = initial
|
694
|
+
expect(instance[:repeated_enum]).to eq(initial)
|
695
|
+
replacement = [::Test::StatusType::DELETED]
|
696
|
+
instance[:repeated_enum] = replacement
|
697
|
+
expect(instance[:repeated_enum]).to eq(replacement)
|
698
|
+
end
|
699
|
+
|
700
|
+
it 'sets acceptable optional field values' do
|
701
|
+
instance[:name] = "Joe"
|
702
|
+
expect(instance[:name]).to eq("Joe")
|
703
|
+
instance[1] = "Tom"
|
704
|
+
expect(instance[:name]).to eq("Tom")
|
705
|
+
end
|
706
|
+
|
707
|
+
it 'sets acceptable empty string field values' do
|
708
|
+
instance[:name] = ""
|
709
|
+
expect(instance.name!).to eq("")
|
710
|
+
end
|
711
|
+
|
712
|
+
it 'sets acceptable empty message field values' do
|
713
|
+
instance = ::Test::Nested.new
|
714
|
+
instance[:resource] = {}
|
715
|
+
expect(instance.resource!).to eq(::Test::Resource.new)
|
716
|
+
end
|
717
|
+
|
718
|
+
it 'sets acceptable extension field values' do
|
719
|
+
instance[:ext_is_searchable] = true
|
720
|
+
expect(instance[:ext_is_searchable]).to eq(true)
|
721
|
+
instance[:".test.Searchable.ext_is_searchable"] = false
|
722
|
+
expect(instance[:ext_is_searchable]).to eq(false)
|
723
|
+
instance[100] = true
|
724
|
+
expect(instance[:ext_is_searchable]).to eq(true)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
context 'throwing TypeError' do
|
729
|
+
let(:instance) { ::Test::Resource.new }
|
730
|
+
|
731
|
+
it 'throws when a repeated value is set with a non array' do
|
732
|
+
expect { instance[:repeated_enum] = "string" }.to raise_error(TypeError)
|
733
|
+
end
|
734
|
+
|
735
|
+
it 'throws when a repeated value is set with an array of the wrong type' do
|
736
|
+
expect { instance[:repeated_enum] = [true, false] }.to raise_error(TypeError)
|
737
|
+
end
|
738
|
+
|
739
|
+
it 'throws when an optional value is not #acceptable?' do
|
740
|
+
expect { instance[:name] = 1 }.to raise_error(TypeError)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
context 'ignoring unknown fields' do
|
745
|
+
around do |example|
|
746
|
+
orig = ::Protobuf.ignore_unknown_fields?
|
747
|
+
::Protobuf.ignore_unknown_fields = true
|
748
|
+
example.call
|
749
|
+
::Protobuf.ignore_unknown_fields = orig
|
750
|
+
end
|
751
|
+
|
752
|
+
context 'with valid fields' do
|
753
|
+
let(:values) { { :name => "Jim" } }
|
754
|
+
|
755
|
+
it "does not raise an error" do
|
756
|
+
expect { ::Test::Resource.new(values) }.to_not raise_error
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
context 'with non-existent field' do
|
761
|
+
let(:values) { { :name => "Jim", :othername => "invalid" } }
|
762
|
+
|
763
|
+
it "does not raise an error" do
|
764
|
+
expect { ::Test::Resource.new(values) }.to_not raise_error
|
765
|
+
end
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
context 'not ignoring unknown fields' do
|
770
|
+
around do |example|
|
771
|
+
orig = ::Protobuf.ignore_unknown_fields?
|
772
|
+
::Protobuf.ignore_unknown_fields = false
|
773
|
+
example.call
|
774
|
+
::Protobuf.ignore_unknown_fields = orig
|
775
|
+
end
|
776
|
+
|
777
|
+
context 'with valid fields' do
|
778
|
+
let(:values) { { :name => "Jim" } }
|
779
|
+
|
780
|
+
it "does not raise an error" do
|
781
|
+
expect { ::Test::Resource.new(values) }.to_not raise_error
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
785
|
+
context 'with non-existent field' do
|
786
|
+
let(:values) { { :name => "Jim", :othername => "invalid" } }
|
787
|
+
|
788
|
+
it "raises an error and mentions the erroneous field" do
|
789
|
+
expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
|
790
|
+
end
|
791
|
+
|
792
|
+
context 'with a nil value' do
|
793
|
+
let(:values) { { :name => "Jim", :othername => nil } }
|
794
|
+
|
795
|
+
it "raises an error and mentions the erroneous field" do
|
796
|
+
expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
|
797
|
+
end
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
end
|
537
802
|
end
|