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,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
|