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.
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,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe Protobuf::Field::Int32Field do
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 '#define_setter' do
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
- describe '#define_getter' do
53
- context 'when required bool field is set to false' do
54
- subject { instance.required_bool = false; instance.required_bool }
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 false' do
57
- expect(subject).to eq(false)
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 '#define_setter' do
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 '#define_setter' do
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
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'protobuf/field'
3
+ require PROTOS_PATH.join('resource.pb')
3
4
 
4
5
  RSpec.describe ::Protobuf::Field do
5
6
 
@@ -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 "#define_setter" do
436
+ describe "#define_accessor" do
472
437
  subject { ::Test::Resource.new }
473
438
 
474
- it "allows string fields to be set to nil" do
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 "does not allow string fields to be set to Numeric" do
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