protobuffy 3.6.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +67 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +25 -5
  6. data/CHANGES.md +55 -0
  7. data/CONTRIBUTING.md +1 -1
  8. data/LICENSE.txt +17 -9
  9. data/README.md +13 -12
  10. data/Rakefile +15 -11
  11. data/bin/protoc-gen-ruby +8 -3
  12. data/bin/rpc_server +1 -0
  13. data/examples/lib/example/reverse-client.rb +2 -2
  14. data/install-protobuf.sh +28 -0
  15. data/lib/protobuf.rb +57 -53
  16. data/lib/protobuf/cli.rb +94 -74
  17. data/lib/protobuf/code_generator.rb +60 -9
  18. data/lib/protobuf/decoder.rb +19 -65
  19. data/lib/protobuf/deprecation.rb +117 -0
  20. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +11 -1
  21. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +55 -3
  22. data/lib/protobuf/encoder.rb +13 -53
  23. data/lib/protobuf/enum.rb +58 -63
  24. data/lib/protobuf/field.rb +4 -4
  25. data/lib/protobuf/field/base_field.rb +101 -173
  26. data/lib/protobuf/field/bool_field.rb +17 -11
  27. data/lib/protobuf/field/bytes_field.rb +21 -35
  28. data/lib/protobuf/field/double_field.rb +0 -1
  29. data/lib/protobuf/field/enum_field.rb +23 -22
  30. data/lib/protobuf/field/field_array.rb +5 -4
  31. data/lib/protobuf/field/fixed32_field.rb +1 -1
  32. data/lib/protobuf/field/fixed64_field.rb +0 -1
  33. data/lib/protobuf/field/float_field.rb +4 -1
  34. data/lib/protobuf/field/int32_field.rb +0 -1
  35. data/lib/protobuf/field/int64_field.rb +0 -1
  36. data/lib/protobuf/field/integer_field.rb +0 -1
  37. data/lib/protobuf/field/message_field.rb +13 -28
  38. data/lib/protobuf/field/sfixed32_field.rb +0 -1
  39. data/lib/protobuf/field/sfixed64_field.rb +0 -1
  40. data/lib/protobuf/field/signed_integer_field.rb +0 -1
  41. data/lib/protobuf/field/sint32_field.rb +0 -1
  42. data/lib/protobuf/field/sint64_field.rb +0 -1
  43. data/lib/protobuf/field/string_field.rb +2 -4
  44. data/lib/protobuf/field/uint32_field.rb +0 -1
  45. data/lib/protobuf/field/uint64_field.rb +0 -1
  46. data/lib/protobuf/field/varint_field.rb +30 -13
  47. data/lib/protobuf/generators/base.rb +30 -16
  48. data/lib/protobuf/generators/enum_generator.rb +6 -9
  49. data/lib/protobuf/generators/extension_generator.rb +1 -2
  50. data/lib/protobuf/generators/field_generator.rb +25 -13
  51. data/lib/protobuf/generators/file_generator.rb +157 -35
  52. data/lib/protobuf/generators/group_generator.rb +22 -17
  53. data/lib/protobuf/generators/message_generator.rb +13 -14
  54. data/lib/protobuf/generators/option_generator.rb +17 -0
  55. data/lib/protobuf/generators/printable.rb +12 -13
  56. data/lib/protobuf/generators/service_generator.rb +2 -3
  57. data/lib/protobuf/http.rb +2 -2
  58. data/lib/protobuf/lifecycle.rb +20 -33
  59. data/lib/protobuf/logging.rb +39 -0
  60. data/lib/protobuf/message.rb +114 -47
  61. data/lib/protobuf/message/fields.rb +170 -88
  62. data/lib/protobuf/message/serialization.rb +19 -18
  63. data/lib/protobuf/optionable.rb +53 -6
  64. data/lib/protobuf/rpc/buffer.rb +18 -19
  65. data/lib/protobuf/rpc/client.rb +22 -50
  66. data/lib/protobuf/rpc/connectors/base.rb +177 -12
  67. data/lib/protobuf/rpc/connectors/http.rb +14 -9
  68. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  69. data/lib/protobuf/rpc/connectors/socket.rb +13 -8
  70. data/lib/protobuf/rpc/connectors/zmq.rb +178 -73
  71. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +4 -1
  72. data/lib/protobuf/rpc/env.rb +12 -12
  73. data/lib/protobuf/rpc/error.rb +3 -3
  74. data/lib/protobuf/rpc/error/client_error.rb +4 -4
  75. data/lib/protobuf/rpc/error/server_error.rb +9 -9
  76. data/lib/protobuf/rpc/middleware/exception_handler.rb +6 -2
  77. data/lib/protobuf/rpc/middleware/logger.rb +8 -4
  78. data/lib/protobuf/rpc/middleware/request_decoder.rb +17 -21
  79. data/lib/protobuf/rpc/middleware/response_encoder.rb +22 -27
  80. data/lib/protobuf/rpc/middleware/statsd.rb +3 -3
  81. data/lib/protobuf/rpc/rpc.pb.rb +4 -1
  82. data/lib/protobuf/rpc/server.rb +1 -1
  83. data/lib/protobuf/rpc/servers/http/server.rb +19 -17
  84. data/lib/protobuf/rpc/servers/socket/server.rb +78 -70
  85. data/lib/protobuf/rpc/servers/socket/worker.rb +4 -4
  86. data/lib/protobuf/rpc/servers/socket_runner.rb +27 -15
  87. data/lib/protobuf/rpc/servers/zmq/broker.rb +70 -31
  88. data/lib/protobuf/rpc/servers/zmq/server.rb +55 -47
  89. data/lib/protobuf/rpc/servers/zmq/util.rb +14 -13
  90. data/lib/protobuf/rpc/servers/zmq/worker.rb +16 -16
  91. data/lib/protobuf/rpc/servers/zmq_runner.rb +26 -7
  92. data/lib/protobuf/rpc/service.rb +21 -27
  93. data/lib/protobuf/rpc/service_directory.rb +43 -27
  94. data/lib/protobuf/rpc/service_dispatcher.rb +9 -10
  95. data/lib/protobuf/rpc/service_filters.rb +32 -55
  96. data/lib/protobuf/rpc/stat.rb +4 -8
  97. data/lib/protobuf/socket.rb +1 -2
  98. data/lib/protobuf/tasks/compile.rake +3 -4
  99. data/lib/protobuf/varint.rb +9 -0
  100. data/lib/protobuf/varint_pure.rb +13 -0
  101. data/lib/protobuf/version.rb +1 -1
  102. data/lib/protobuf/zmq.rb +2 -2
  103. data/proto/google/protobuf/descriptor.proto +190 -31
  104. data/protobuffy.gemspec +30 -17
  105. data/spec/benchmark/tasks.rb +27 -19
  106. data/spec/bin/protoc-gen-ruby_spec.rb +11 -6
  107. data/spec/encoding/all_types_spec.rb +96 -84
  108. data/spec/encoding/extreme_values_spec.rb +0 -0
  109. data/spec/functional/class_inheritance_spec.rb +52 -0
  110. data/spec/functional/code_generator_spec.rb +38 -0
  111. data/spec/functional/socket_server_spec.rb +15 -15
  112. data/spec/functional/zmq_server_spec.rb +29 -27
  113. data/spec/lib/protobuf/cli_spec.rb +82 -67
  114. data/spec/lib/protobuf/code_generator_spec.rb +37 -10
  115. data/spec/lib/protobuf/enum_spec.rb +77 -46
  116. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  117. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  118. data/spec/lib/protobuf/field/enum_field_spec.rb +26 -0
  119. data/spec/lib/protobuf/field/field_array_spec.rb +69 -0
  120. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  121. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  122. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  123. data/spec/lib/protobuf/field/int32_field_spec.rb +114 -1
  124. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  125. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  126. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  127. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  129. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  130. data/spec/lib/protobuf/field/string_field_spec.rb +44 -11
  131. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field_spec.rb +4 -6
  134. data/spec/lib/protobuf/generators/base_spec.rb +80 -13
  135. data/spec/lib/protobuf/generators/enum_generator_spec.rb +35 -21
  136. data/spec/lib/protobuf/generators/extension_generator_spec.rb +12 -13
  137. data/spec/lib/protobuf/generators/field_generator_spec.rb +73 -21
  138. data/spec/lib/protobuf/generators/file_generator_spec.rb +89 -6
  139. data/spec/lib/protobuf/generators/service_generator_spec.rb +25 -13
  140. data/spec/lib/protobuf/lifecycle_spec.rb +25 -20
  141. data/spec/lib/protobuf/message_spec.rb +578 -79
  142. data/spec/lib/protobuf/optionable_spec.rb +202 -26
  143. data/spec/lib/protobuf/rpc/client_spec.rb +16 -16
  144. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +167 -13
  145. data/spec/lib/protobuf/rpc/connectors/connector_spec.rb +4 -5
  146. data/spec/lib/protobuf/rpc/connectors/http_spec.rb +13 -11
  147. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  148. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +6 -7
  149. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +35 -52
  150. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +10 -10
  151. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +11 -11
  152. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +23 -23
  153. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +11 -11
  154. data/spec/lib/protobuf/rpc/middleware/statsd_spec.rb +6 -6
  155. data/spec/lib/protobuf/rpc/servers/http/server_spec.rb +47 -44
  156. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +6 -6
  157. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +12 -10
  158. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +11 -11
  159. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +7 -7
  160. data/spec/lib/protobuf/rpc/service_directory_spec.rb +47 -49
  161. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +8 -25
  162. data/spec/lib/protobuf/rpc/service_filters_spec.rb +102 -69
  163. data/spec/lib/protobuf/rpc/service_spec.rb +37 -36
  164. data/spec/lib/protobuf/rpc/stat_spec.rb +7 -9
  165. data/spec/lib/protobuf/varint_spec.rb +29 -0
  166. data/spec/lib/protobuf_spec.rb +55 -28
  167. data/spec/spec_helper.rb +12 -27
  168. data/spec/support/all.rb +0 -1
  169. data/spec/support/packed_field.rb +4 -3
  170. data/spec/support/{test → protos}/all_types.data.bin +0 -0
  171. data/spec/support/{test → protos}/all_types.data.txt +0 -0
  172. data/spec/support/{test → protos}/enum.pb.rb +8 -4
  173. data/spec/support/{test → protos}/enum.proto +4 -1
  174. data/spec/support/{test → protos}/extreme_values.data.bin +0 -0
  175. data/spec/support/protos/google_unittest.bin +0 -0
  176. data/spec/support/protos/google_unittest.pb.rb +798 -0
  177. data/spec/support/{test → protos}/google_unittest.proto +237 -66
  178. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  179. data/spec/support/protos/google_unittest_custom_options.pb.rb +268 -0
  180. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  181. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  182. data/spec/support/{test → protos}/google_unittest_import.proto +19 -10
  183. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  184. data/spec/support/{test → protos}/google_unittest_import_public.proto +8 -5
  185. data/spec/support/{test → protos}/multi_field_extensions.pb.rb +5 -2
  186. data/spec/support/{test → protos}/multi_field_extensions.proto +2 -0
  187. data/spec/support/{test → protos}/resource.pb.rb +47 -11
  188. data/spec/support/{test → protos}/resource.proto +24 -1
  189. data/spec/support/resource_service.rb +23 -0
  190. data/spec/support/server.rb +32 -61
  191. metadata +119 -59
  192. data/lib/protobuf/deprecator.rb +0 -42
  193. data/lib/protobuf/logger.rb +0 -93
  194. data/lib/protobuf/rpc/connector.rb +0 -21
  195. data/lib/protobuf/rpc/connectors/common.rb +0 -172
  196. data/spec/data/data.bin +0 -3
  197. data/spec/data/types.bin +0 -0
  198. data/spec/lib/protobuf/logger_spec.rb +0 -145
  199. data/spec/lib/protobuf/rpc/connector_spec.rb +0 -26
  200. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +0 -170
  201. data/spec/support/test/defaults.pb.rb +0 -25
  202. data/spec/support/test/defaults.proto +0 -9
  203. data/spec/support/test/extended.pb.rb +0 -22
  204. data/spec/support/test/extended.proto +0 -10
  205. data/spec/support/test/google_unittest.pb.rb +0 -543
  206. data/spec/support/test/google_unittest_import.pb.rb +0 -37
  207. data/spec/support/test/google_unittest_import_public.pb.rb +0 -8
  208. data/spec/support/test/resource_service.rb +0 -26
  209. data/spec/support/tolerance_matcher.rb +0 -40
@@ -2,28 +2,111 @@ require 'spec_helper'
2
2
 
3
3
  require 'protobuf/generators/file_generator'
4
4
 
5
- describe ::Protobuf::Generators::FileGenerator do
5
+ RSpec.describe ::Protobuf::Generators::FileGenerator do
6
6
 
7
7
  let(:base_descriptor_fields) { { :name => 'test/foo.proto' } }
8
8
  let(:descriptor_fields) { base_descriptor_fields }
9
9
  let(:file_descriptor) { ::Google::Protobuf::FileDescriptorProto.new(descriptor_fields) }
10
10
 
11
11
  subject { described_class.new(file_descriptor) }
12
- its(:file_name) { should eq 'test/foo.pb.rb' }
12
+ specify { expect(subject.file_name).to eq('test/foo.pb.rb') }
13
13
 
14
14
  describe '#print_import_requires' do
15
15
  let(:descriptor_fields) do
16
- base_descriptor_fields.merge!({ :dependency => [ 'test/bar.proto',
17
- 'test/baz.proto' ] })
16
+ base_descriptor_fields.merge(
17
+ :dependency => [
18
+ 'test/bar.proto',
19
+ 'test/baz.proto',
20
+ ]
21
+ )
18
22
  end
19
23
 
20
24
  it 'prints a ruby require for each dependency' do
21
- subject.should_receive(:print_require).with('test/bar.pb')
22
- subject.should_receive(:print_require).with('test/baz.pb')
25
+ expect(subject).to receive(:print_require).with('test/bar.pb')
26
+ expect(subject).to receive(:print_require).with('test/baz.pb')
23
27
  subject.print_import_requires
24
28
  end
25
29
 
26
30
  end
27
31
 
32
+ describe '#compile' do
33
+ it 'generates the file contents' do
34
+ subject.compile
35
+ expect(subject.to_s).to eq <<EOF
36
+ # encoding: utf-8
37
+
38
+ ##
39
+ # This file is auto-generated. DO NOT EDIT!
40
+ #
41
+ require 'protobuf'
42
+
43
+ EOF
44
+ end
45
+
46
+ it 'generates the file contents using default package name' do
47
+ allow(ENV).to receive(:key?).with('PB_ALLOW_DEFAULT_PACKAGE_NAME')
48
+ .and_return(true)
49
+ subject.compile
50
+ expect(subject.to_s).to eq <<EOF
51
+ # encoding: utf-8
52
+
53
+ ##
54
+ # This file is auto-generated. DO NOT EDIT!
55
+ #
56
+ require 'protobuf'
57
+
58
+ module Foo
59
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
28
60
  end
29
61
 
62
+ EOF
63
+ end
64
+
65
+ context 'with extended messages' do
66
+ let(:descriptor_fields) do
67
+ base_descriptor_fields.merge(
68
+ :package => 'test.pkg.file_generator_spec',
69
+ :extension => [{
70
+ :name => 'boom',
71
+ :number => 20_000,
72
+ :label => Google::Protobuf::FieldDescriptorProto::Label::LABEL_OPTIONAL,
73
+ :type => Google::Protobuf::FieldDescriptorProto::Type::TYPE_STRING,
74
+ :extendee => '.google.protobuf.FieldOptions',
75
+ }]
76
+ )
77
+ end
78
+
79
+ it 'generates the file contents that include the namespaced extension name' do
80
+ subject.compile
81
+ expect(subject.to_s).to eq <<EOF
82
+ # encoding: utf-8
83
+
84
+ ##
85
+ # This file is auto-generated. DO NOT EDIT!
86
+ #
87
+ require 'protobuf'
88
+
89
+ module Test
90
+ module Pkg
91
+ module File_generator_spec
92
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
93
+
94
+ ##
95
+ # Extended Message Fields
96
+ #
97
+ class ::Google::Protobuf::FieldOptions < ::Protobuf::Message
98
+ optional :string, :".test.pkg.file_generator_spec.boom", 20000, :extension => true
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ EOF
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -2,42 +2,54 @@ require 'spec_helper'
2
2
 
3
3
  require 'protobuf/generators/service_generator'
4
4
 
5
- describe ::Protobuf::Generators::ServiceGenerator do
5
+ RSpec.describe ::Protobuf::Generators::ServiceGenerator do
6
6
 
7
- let(:methods) {
7
+ let(:methods) do
8
8
  [
9
9
  { :name => 'Search', :input_type => 'FooRequest', :output_type => 'FooResponse' },
10
- { :name => 'FooBar', :input_type => '.foo.Request', :output_type => '.bar.Response' }
10
+ { :name => 'FooBar', :input_type => '.foo.Request', :output_type => '.bar.Response' },
11
11
  ]
12
- }
13
- let(:service_fields) { { :name => 'TestService',
14
- :method => methods } }
12
+ end
13
+ let(:service_fields) do
14
+ {
15
+ :name => 'TestService',
16
+ :method => methods,
17
+ }
18
+ end
15
19
 
16
20
  let(:service) { ::Google::Protobuf::ServiceDescriptorProto.new(service_fields) }
17
21
 
18
22
  subject { described_class.new(service) }
19
23
 
20
24
  describe '#compile' do
21
- let(:compiled) {
22
- %q{class TestService < ::Protobuf::Rpc::Service
25
+ let(:compiled) do
26
+ 'class TestService < ::Protobuf::Rpc::Service
23
27
  rpc :search, FooRequest, FooResponse
24
28
  rpc :foo_bar, ::Foo::Request, ::Bar::Response
25
29
  end
26
30
 
27
- }
28
- }
31
+ '
32
+ end
29
33
 
30
34
  it 'compiles the service and it\'s rpc methods' do
31
35
  subject.compile
32
- subject.to_s.should eq(compiled)
36
+ expect(subject.to_s).to eq(compiled)
33
37
  end
34
38
  end
35
39
 
36
40
  describe '#build_method' do
37
41
  it 'returns a string identifying the given method descriptor' do
38
- subject.build_method(service.method.first).should eq("rpc :search, FooRequest, FooResponse")
42
+ expect(subject.build_method(service.method.first)).to eq("rpc :search, FooRequest, FooResponse")
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
39
52
  end
40
53
  end
41
54
 
42
55
  end
43
-
@@ -1,22 +1,27 @@
1
1
  require 'spec_helper'
2
2
  require 'protobuf/lifecycle'
3
3
 
4
- describe ::Protobuf::Lifecycle do
4
+ RSpec.describe ::Protobuf::Lifecycle do
5
5
  subject { described_class }
6
6
 
7
- before(:each) do
7
+ around do |example|
8
+ # this entire class is deprecated
9
+ ::Protobuf.deprecator.silence(&example)
10
+ end
11
+
12
+ before do
8
13
  ::ActiveSupport::Notifications.notifier = ::ActiveSupport::Notifications::Fanout.new
9
14
  end
10
15
 
11
16
  it "registers a string as the event_name" do
12
- ::ActiveSupport::Notifications.should_receive(:subscribe).with("something")
13
- subject.register("something") { true }
17
+ expect(::ActiveSupport::Notifications).to receive(:subscribe).with("something")
18
+ subject.register("something") { true }
14
19
  end
15
20
 
16
21
  it "only registers blocks for event callbacks" do
17
- expect {
22
+ expect do
18
23
  subject.register("something")
19
- }.to raise_error( /block/ )
24
+ end.to raise_error(/block/)
20
25
  end
21
26
 
22
27
  it "calls the registered block when triggered" do
@@ -26,8 +31,8 @@ describe ::Protobuf::Lifecycle do
26
31
  end
27
32
 
28
33
  subject.trigger("this")
29
- this.should_not be_nil
30
- this.should eq("not nil")
34
+ expect(this).to_not be_nil
35
+ expect(this).to eq("not nil")
31
36
  end
32
37
 
33
38
  it "calls multiple registered blocks when triggered" do
@@ -43,10 +48,10 @@ describe ::Protobuf::Lifecycle do
43
48
  end
44
49
 
45
50
  subject.trigger("this")
46
- this.should_not be_nil
47
- this.should eq("not nil")
48
- that.should_not be_nil
49
- that.should eq("not nil")
51
+ expect(this).to_not be_nil
52
+ expect(this).to eq("not nil")
53
+ expect(that).to_not be_nil
54
+ expect(that).to eq("not nil")
50
55
  end
51
56
 
52
57
  context 'when the registered block has arity' do
@@ -55,12 +60,12 @@ describe ::Protobuf::Lifecycle do
55
60
  outer_bar = nil
56
61
 
57
62
  subject.register('foo') do |bar|
58
- bar.should be_nil
63
+ expect(bar).to be_nil
59
64
  outer_bar = 'triggered'
60
65
  end
61
66
 
62
67
  subject.trigger('foo')
63
- outer_bar.should eq 'triggered'
68
+ expect(outer_bar).to eq 'triggered'
64
69
  end
65
70
  end
66
71
 
@@ -69,21 +74,21 @@ describe ::Protobuf::Lifecycle do
69
74
  outer_bar = nil
70
75
 
71
76
  subject.register('foo') do |bar|
72
- bar.should_not be_nil
77
+ expect(bar).to_not be_nil
73
78
  outer_bar = bar
74
79
  end
75
80
 
76
81
  subject.trigger('foo', 'baz')
77
- outer_bar.should eq 'baz'
82
+ expect(outer_bar).to eq 'baz'
78
83
  end
79
84
  end
80
85
  end
81
86
 
82
87
  context "normalized event names" do
83
- specify { subject.normalized_event_name(:derp).should eq("derp") }
84
- specify { subject.normalized_event_name(:Derp).should eq("derp") }
85
- specify { subject.normalized_event_name("DERP").should eq("derp") }
86
- specify { subject.normalized_event_name("derp").should eq("derp") }
88
+ specify { expect(subject.normalized_event_name(:derp)).to eq("derp") }
89
+ specify { expect(subject.normalized_event_name(:Derp)).to eq("derp") }
90
+ specify { expect(subject.normalized_event_name("DERP")).to eq("derp") }
91
+ specify { expect(subject.normalized_event_name("derp")).to eq("derp") }
87
92
  end
88
93
 
89
94
  end
@@ -1,59 +1,132 @@
1
+ # encoding: utf-8
2
+
3
+ require 'stringio'
1
4
  require 'spec_helper'
5
+ require PROTOS_PATH.join('resource.pb')
6
+ require PROTOS_PATH.join('enum.pb')
2
7
 
3
- describe Protobuf::Message do
8
+ RSpec.describe Protobuf::Message do
4
9
 
5
10
  describe '.decode' do
6
11
  let(:message) { ::Test::Resource.new(:name => "Jim") }
7
12
 
8
13
  it 'creates a new message object decoded from the given bytes' do
9
- ::Test::Resource.decode(message.encode).should eq message
14
+ expect(::Test::Resource.decode(message.encode)).to eq message
15
+ end
16
+
17
+ context 'with a new enum value' do
18
+ let(:older_message) do
19
+ Class.new(Protobuf::Message) do
20
+ enum_class = Class.new(::Protobuf::Enum) do
21
+ define :YAY, 1
22
+ end
23
+
24
+ optional enum_class, :enum_field, 1
25
+ repeated enum_class, :enum_list, 2
26
+ end
27
+ end
28
+
29
+ let(:newer_message) do
30
+ Class.new(Protobuf::Message) do
31
+ enum_class = Class.new(::Protobuf::Enum) do
32
+ define :YAY, 1
33
+ define :HOORAY, 2
34
+ end
35
+
36
+ optional enum_class, :enum_field, 1
37
+ repeated enum_class, :enum_list, 2
38
+ end
39
+ end
40
+
41
+ context 'with a singular field' do
42
+ it 'treats the field as if it was unset when decoding' do
43
+ newer = newer_message.new(:enum_field => :HOORAY).serialize
44
+
45
+ expect(older_message.decode(newer).enum_field!).to be_nil
46
+ end
47
+
48
+ it 'rejects an unknown value when using the constructor' do
49
+ expect { older_message.new(:enum_field => :HOORAY) }.to raise_error(TypeError)
50
+ end
51
+
52
+ it 'rejects an unknown value when the setter' do
53
+ older = older_message.new
54
+ expect { older.enum_field = :HOORAY }.to raise_error(TypeError)
55
+ end
56
+ end
57
+
58
+ context 'with a repeated field' do
59
+ it 'treats the field as if it was unset when decoding' do
60
+ newer = newer_message.new(:enum_list => [:HOORAY]).serialize
61
+
62
+ expect(older_message.decode(newer).enum_list).to eq([])
63
+ end
64
+
65
+ it 'rejects an unknown value when using the constructor' do
66
+ expect { older_message.new(:enum_list => [:HOORAY]) }.to raise_error(TypeError)
67
+ end
68
+
69
+ it 'rejects an unknown value when the setter' do
70
+ older = older_message.new
71
+ expect { older.enum_field = [:HOORAY] }.to raise_error(TypeError)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '.decode_from' do
78
+ let(:message) { ::Test::Resource.new(:name => "Jim") }
79
+
80
+ it 'creates a new message object decoded from the given byte stream' do
81
+ stream = ::StringIO.new(message.encode)
82
+ expect(::Test::Resource.decode_from(stream)).to eq message
10
83
  end
11
84
  end
12
85
 
13
86
  describe 'defining a new field' do
14
87
  context 'when defining a field with a tag that has already been used' do
15
88
  it 'raises a TagCollisionError' do
16
- expect {
89
+ expect do
17
90
  Class.new(Protobuf::Message) do
18
91
  optional ::Protobuf::Field::Int32Field, :foo, 1
19
92
  optional ::Protobuf::Field::Int32Field, :bar, 1
20
93
  end
21
- }.to raise_error(Protobuf::TagCollisionError, /Field number 1 has already been used/)
94
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 1 has already been used/)
22
95
  end
23
96
  end
24
97
 
25
98
  context 'when defining an extension field with a tag that has already been used' do
26
99
  it 'raises a TagCollisionError' do
27
- expect {
100
+ expect do
28
101
  Class.new(Protobuf::Message) do
29
102
  extensions 100...110
30
103
  optional ::Protobuf::Field::Int32Field, :foo, 100
31
104
  optional ::Protobuf::Field::Int32Field, :bar, 100, :extension => true
32
105
  end
33
- }.to raise_error(Protobuf::TagCollisionError, /Field number 100 has already been used/)
106
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 100 has already been used/)
34
107
  end
35
108
  end
36
109
 
37
110
  context 'when defining a field with a name that has already been used' do
38
111
  it 'raises a DuplicateFieldNameError' do
39
- expect {
112
+ expect do
40
113
  Class.new(Protobuf::Message) do
41
114
  optional ::Protobuf::Field::Int32Field, :foo, 1
42
115
  optional ::Protobuf::Field::Int32Field, :foo, 2
43
116
  end
44
- }.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
117
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
45
118
  end
46
119
  end
47
120
 
48
121
  context 'when defining an extension field with a name that has already been used' do
49
122
  it 'raises a DuplicateFieldNameError' do
50
- expect {
123
+ expect do
51
124
  Class.new(Protobuf::Message) do
52
125
  extensions 100...110
53
126
  optional ::Protobuf::Field::Int32Field, :foo, 1
54
127
  optional ::Protobuf::Field::Int32Field, :foo, 100, :extension => true
55
128
  end
56
- }.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
129
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
57
130
  end
58
131
  end
59
132
  end
@@ -62,41 +135,57 @@ describe Protobuf::Message do
62
135
  let(:values) { { :name => "Jim" } }
63
136
 
64
137
  it 'creates a new message object with the given values and returns the encoded bytes' do
65
- ::Test::Resource.encode(values).should eq ::Test::Resource.new(values).encode
138
+ expect(::Test::Resource.encode(values)).to eq ::Test::Resource.new(values).encode
66
139
  end
67
140
  end
68
141
 
69
142
  describe '#initialize' do
70
- it "initializes the enum getter to 0" do
143
+ it "defaults to the first value listed in the enum's type definition" do
144
+ test_enum = Test::EnumTestMessage.new
145
+ expect(test_enum.non_default_enum).to eq(Test::EnumTestType.enums.first)
146
+ end
147
+
148
+ it "defaults to a a value with a name" do
71
149
  test_enum = Test::EnumTestMessage.new
72
- test_enum.non_default_enum.should eq(0)
150
+ expect(test_enum.non_default_enum.name).to eq(Test::EnumTestType.enums.first.name)
73
151
  end
74
152
 
75
153
  it "exposes the enum getter raw value through ! method" do
76
154
  test_enum = Test::EnumTestMessage.new
77
- test_enum.non_default_enum!.should be_nil
155
+ expect(test_enum.non_default_enum!).to be_nil
78
156
  end
79
157
 
80
158
  it "exposes the enum getter raw value through ! method (when set)" do
81
159
  test_enum = Test::EnumTestMessage.new
82
160
  test_enum.non_default_enum = 1
83
- test_enum.non_default_enum!.should eq(1)
161
+ expect(test_enum.non_default_enum!).to eq(1)
84
162
  end
85
163
 
86
164
  it "does not try to set attributes which have nil values" do
87
- Test::EnumTestMessage.any_instance.should_not_receive("non_default_enum=")
165
+ expect_any_instance_of(Test::EnumTestMessage).not_to receive("non_default_enum=")
88
166
  Test::EnumTestMessage.new(:non_default_enum => nil)
89
167
  end
90
168
 
91
169
  it "takes a hash as an initialization argument" do
92
170
  test_enum = Test::EnumTestMessage.new(:non_default_enum => 2)
93
- test_enum.non_default_enum.should eq(2)
171
+ expect(test_enum.non_default_enum).to eq(2)
94
172
  end
95
173
 
96
174
  it "initializes with an object that responds to #to_hash" do
97
175
  hashie_object = OpenStruct.new(:to_hash => { :non_default_enum => 2 })
98
176
  test_enum = Test::EnumTestMessage.new(hashie_object)
99
- test_enum.non_default_enum.should eq(2)
177
+ expect(test_enum.non_default_enum).to eq(2)
178
+ end
179
+
180
+ it "initializes with an object with a block" do
181
+ test_enum = Test::EnumTestMessage.new { |p| p.non_default_enum = 2 }
182
+ expect(test_enum.non_default_enum).to eq(2)
183
+ end
184
+
185
+ # to be deprecated
186
+ it "allows you to pass nil to repeated fields" do
187
+ test = Test::Resource.new(:repeated_enum => nil)
188
+ expect(test.repeated_enum).to eq([])
100
189
  end
101
190
  end
102
191
 
@@ -109,21 +198,21 @@ describe Protobuf::Message do
109
198
  end
110
199
 
111
200
  it "keeps utf-8 when utf-8 is input for string fields" do
112
- name = "my name\xC3"
113
- name.force_encoding("UTF-8")
201
+ name = 'my name💩'
202
+ name.force_encoding(Encoding::UTF_8)
114
203
 
115
204
  message = ::Test::Resource.new(:name => name)
116
205
  new_message = ::Test::Resource.decode(message.encode)
117
- (new_message.name == name).should be_true
206
+ expect(new_message.name == name).to be true
118
207
  end
119
208
 
120
209
  it "trims binary when binary is input for string fields" do
121
210
  name = "my name\xC3"
122
- name.force_encoding("ASCII-8BIT")
211
+ name.force_encoding(Encoding::BINARY)
123
212
 
124
213
  message = ::Test::Resource.new(:name => name)
125
214
  new_message = ::Test::Resource.decode(message.encode)
126
- (new_message.name == "my name").should be_true
215
+ expect(new_message.name == "my name").to be true
127
216
  end
128
217
  end
129
218
 
@@ -131,9 +220,9 @@ describe Protobuf::Message do
131
220
  let(:message) { ::Test::ResourceWithRequiredField.new }
132
221
 
133
222
  it "raises a 'message not initialized' error" do
134
- expect {
223
+ expect do
135
224
  message.encode
136
- }.to raise_error(Protobuf::SerializationError, /required/i)
225
+ end.to raise_error(Protobuf::SerializationError, /required/i)
137
226
  end
138
227
  end
139
228
 
@@ -141,33 +230,33 @@ describe Protobuf::Message do
141
230
  let(:message) { ::Test::Resource.new(:name => "something") }
142
231
 
143
232
  it "does not raise an error when repeated fields are []" do
144
- expect {
233
+ expect do
145
234
  message.repeated_enum = []
146
235
  message.encode
147
- }.to_not raise_error
236
+ end.to_not raise_error
148
237
  end
149
238
 
150
239
  it "sets the value to nil when empty array is passed" do
151
240
  message.repeated_enum = []
152
- message.instance_variable_get("@values")[:repeated_enum].should be_nil
241
+ expect(message.instance_variable_get("@values")[:repeated_enum]).to be_nil
153
242
  end
154
243
 
155
244
  it "does not compact the edit original array" do
156
245
  a = [nil].freeze
157
246
  message.repeated_enum = a
158
- message.repeated_enum.should eq([])
159
- a.should eq([nil].freeze)
247
+ expect(message.repeated_enum).to eq([])
248
+ expect(a).to eq([nil].freeze)
160
249
  end
161
250
 
162
251
  it "compacts the set array" do
163
252
  message.repeated_enum = [nil]
164
- message.repeated_enum.should eq([])
253
+ expect(message.repeated_enum).to eq([])
165
254
  end
166
255
 
167
256
  it "raises TypeError when a non-array replaces it" do
168
- expect {
257
+ expect do
169
258
  message.repeated_enum = 2
170
- }.to raise_error(/value of type/)
259
+ end.to raise_error(/value of type/)
171
260
  end
172
261
  end
173
262
  end
@@ -175,20 +264,20 @@ describe Protobuf::Message do
175
264
  describe "boolean predicate methods" do
176
265
  subject { Test::ResourceFindRequest.new(:name => "resource") }
177
266
 
178
- it { should respond_to(:active?) }
267
+ it { is_expected.to respond_to(:active?) }
179
268
 
180
269
  it "sets the predicate to true when the boolean value is true" do
181
270
  subject.active = true
182
- subject.active?.should be_true
271
+ expect(subject.active?).to be true
183
272
  end
184
273
 
185
274
  it "sets the predicate to false when the boolean value is false" do
186
275
  subject.active = false
187
- subject.active?.should be_false
276
+ expect(subject.active?).to be false
188
277
  end
189
278
 
190
279
  it "does not put predicate methods on non-boolean fields" do
191
- Test::ResourceFindRequest.new(:name => "resource").should_not respond_to(:name?)
280
+ expect(Test::ResourceFindRequest.new(:name => "resource")).to_not respond_to(:name?)
192
281
  end
193
282
  end
194
283
 
@@ -196,11 +285,11 @@ describe Protobuf::Message do
196
285
  subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
197
286
 
198
287
  it "is false when the message does not have the field" do
199
- subject.respond_to_and_has?(:other_field).should be_false
288
+ expect(subject.respond_to_and_has?(:other_field)).to be false
200
289
  end
201
290
 
202
291
  it "is true when the message has the field" do
203
- subject.respond_to_and_has?(:non_default_enum).should be_true
292
+ expect(subject.respond_to_and_has?(:non_default_enum)).to be true
204
293
  end
205
294
  end
206
295
 
@@ -208,106 +297,204 @@ describe Protobuf::Message do
208
297
  subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
209
298
 
210
299
  it "is false when the message does not have the field" do
211
- subject.respond_to_and_has_and_present?(:other_field).should be_false
300
+ expect(subject.respond_to_and_has_and_present?(:other_field)).to be false
212
301
  end
213
302
 
214
303
  it "is false when the field is repeated and a value is not present" do
215
- subject.respond_to_and_has_and_present?(:repeated_enums).should be_false
304
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
216
305
  end
217
306
 
218
307
  it "is false when the field is repeated and the value is empty array" do
219
308
  subject.repeated_enums = []
220
- subject.respond_to_and_has_and_present?(:repeated_enums).should be_false
309
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
221
310
  end
222
311
 
223
312
  it "is true when the field is repeated and a value is present" do
224
313
  subject.repeated_enums = [2]
225
- subject.respond_to_and_has_and_present?(:repeated_enums).should be_true
314
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be true
226
315
  end
227
316
 
228
317
  it "is true when the message has the field" do
229
- subject.respond_to_and_has_and_present?(:non_default_enum).should be_true
318
+ expect(subject.respond_to_and_has_and_present?(:non_default_enum)).to be true
230
319
  end
231
320
 
232
321
  context "#API" do
233
322
  subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
234
323
 
235
- it { should respond_to(:respond_to_and_has_and_present?) }
236
- it { should respond_to(:responds_to_and_has_and_present?) }
237
- it { should respond_to(:responds_to_has?) }
238
- it { should respond_to(:respond_to_has?) }
239
- it { should respond_to(:respond_to_has_present?) }
240
- it { should respond_to(:responds_to_has_present?) }
241
- it { should respond_to(:respond_to_and_has_present?) }
242
- it { should respond_to(:responds_to_and_has_present?) }
324
+ specify { expect(subject).to respond_to(:respond_to_and_has_and_present?) }
325
+ specify { expect(subject).to respond_to(:responds_to_and_has_and_present?) }
326
+ specify { expect(subject).to respond_to(:responds_to_has?) }
327
+ specify { expect(subject).to respond_to(:respond_to_has?) }
328
+ specify { expect(subject).to respond_to(:respond_to_has_present?) }
329
+ specify { expect(subject).to respond_to(:responds_to_has_present?) }
330
+ specify { expect(subject).to respond_to(:respond_to_and_has_present?) }
331
+ specify { expect(subject).to respond_to(:responds_to_and_has_present?) }
243
332
  end
244
333
 
245
334
  end
246
335
 
336
+ describe '#inspect' do
337
+ let(:klass) do
338
+ Class.new(Protobuf::Message) do |klass|
339
+ enum_class = Class.new(Protobuf::Enum) do
340
+ define :YAY, 1
341
+ end
342
+
343
+ klass.const_set(:EnumKlass, enum_class)
344
+
345
+ optional :string, :name, 1
346
+ repeated :int32, :counts, 2
347
+ optional enum_class, :enum, 3
348
+ end
349
+ end
350
+
351
+ before { stub_const('MyMessage', klass) }
352
+
353
+ it 'lists the fields' do
354
+ proto = klass.new(:name => 'wooo', :counts => [1, 2, 3], :enum => klass::EnumKlass::YAY)
355
+ expect(proto.inspect).to eq('#<MyMessage name="wooo" counts=[1, 2, 3] enum=#<Protobuf::Enum(MyMessage::EnumKlass)::YAY=1>>')
356
+ end
357
+ end
358
+
247
359
  describe '#to_hash' do
248
360
  context 'generating values for an ENUM field' do
249
361
  it 'converts the enum to its tag representation' do
250
362
  hash = Test::EnumTestMessage.new(:non_default_enum => :TWO).to_hash
251
- hash.should eq({ :non_default_enum => 2 })
363
+ expect(hash).to eq(:non_default_enum => 2)
252
364
  end
253
365
 
254
366
  it 'does not populate default values' do
255
367
  hash = Test::EnumTestMessage.new.to_hash
256
- hash.should eq(Hash.new)
368
+ expect(hash).to eq({})
257
369
  end
258
370
 
259
371
  it 'converts repeated enum fields to an array of the tags' do
260
- hash = Test::EnumTestMessage.new(:repeated_enums => [ :ONE, :TWO, :TWO, :ONE ]).to_hash
261
- hash.should eq({ :repeated_enums => [ 1, 2, 2, 1 ] })
372
+ hash = Test::EnumTestMessage.new(:repeated_enums => [:ONE, :TWO, :TWO, :ONE]).to_hash
373
+ expect(hash).to eq(:repeated_enums => [1, 2, 2, 1])
262
374
  end
263
375
  end
264
376
 
265
377
  context 'generating values for a Message field' do
266
378
  it 'recursively hashes field messages' do
267
- hash = Test::Nested.new({ :resource => { :name => 'Nested' } }).to_hash
268
- hash.should eq({ :resource => { :name => 'Nested' } })
379
+ hash = Test::Nested.new(:resource => { :name => 'Nested' }).to_hash
380
+ expect(hash).to eq(:resource => { :name => 'Nested' })
269
381
  end
270
382
 
271
383
  it 'recursively hashes a repeated set of messages' do
272
- proto = Test::Nested.new(:multiple_resources => [
273
- Test::Resource.new(:name => 'Resource 1'),
274
- Test::Resource.new(:name => 'Resource 2')
275
- ])
276
-
277
- proto.to_hash.should eq({ :multiple_resources => [ { :name => 'Resource 1' },
278
- { :name => 'Resource 2' } ] })
384
+ proto = Test::Nested.new(
385
+ :multiple_resources => [
386
+ Test::Resource.new(:name => 'Resource 1'),
387
+ Test::Resource.new(:name => 'Resource 2'),
388
+ ]
389
+ )
390
+
391
+ expect(proto.to_hash).to eq(
392
+ :multiple_resources => [
393
+ { :name => 'Resource 1' },
394
+ { :name => 'Resource 2' },
395
+ ]
396
+ )
397
+ end
398
+ end
279
399
 
400
+ it 'uses simple field names as keys when possible and fully qualified names otherwise' do
401
+ message = Class.new(::Protobuf::Message) do
402
+ optional :int32, :field, 1
403
+ optional :int32, :colliding_field, 2
404
+ extensions 100...200
405
+ optional :int32, :".ext.normal_ext_field", 100, :extension => true
406
+ optional :int32, :".ext.colliding_field", 101, :extension => true
407
+ optional :int32, :".ext.colliding_field2", 102, :extension => true
408
+ optional :int32, :".ext2.colliding_field2", 103, :extension => true
280
409
  end
410
+
411
+ hash = {
412
+ :field => 1,
413
+ :colliding_field => 2,
414
+ :normal_ext_field => 3,
415
+ :".ext.colliding_field" => 4,
416
+ :".ext.colliding_field2" => 5,
417
+ :".ext2.colliding_field2" => 6,
418
+ }
419
+ instance = message.new(hash)
420
+ expect(instance.to_hash).to eq(hash)
281
421
  end
282
422
  end
283
423
 
284
424
  describe '#to_json' do
285
425
  subject do
286
- ::Test::ResourceFindRequest.new({ :name => 'Test Name', :active => false })
426
+ ::Test::ResourceFindRequest.new(:name => 'Test Name', :active => false)
287
427
  end
288
428
 
289
- its(:to_json) { should eq '{"name":"Test Name","active":false}' }
429
+ specify { expect(subject.to_json).to eq '{"name":"Test Name","active":false}' }
290
430
  end
291
431
 
292
432
  describe '.to_json' do
293
433
  it 'returns the class name of the message for use in json encoding' do
294
- expect {
434
+ expect do
295
435
  ::Timeout.timeout(0.1) do
296
436
  expect(::Test::Resource.to_json).to eq("Test::Resource")
297
437
  end
298
- }.not_to raise_error
438
+ end.not_to raise_error
299
439
  end
300
440
  end
301
441
 
302
- describe "#define_setter" do
442
+ describe "#define_accessor" do
303
443
  subject { ::Test::Resource.new }
304
444
 
305
- it "allows string fields to be set to nil" do
445
+ it 'allows string fields to be set to nil' do
306
446
  expect { subject.name = nil }.to_not raise_error
307
447
  end
308
448
 
309
- it "does not allow string fields to be set to Numeric" do
310
- expect { subject.name = 1}.to raise_error(/name/)
449
+ it 'does not allow string fields to be set to Numeric' do
450
+ expect { subject.name = 1 }.to raise_error(/name/)
451
+ end
452
+
453
+ it 'does not allow a repeated field is set to nil' do
454
+ expect { subject.repeated_enum = nil }.to raise_error(TypeError)
455
+ end
456
+
457
+ context '#{simple_field_name}!' do
458
+ it 'returns value of set field' do
459
+ expect(::Test::Resource.new(:name => "Joe").name!).to eq("Joe")
460
+ end
461
+
462
+ it 'returns value of set field with default' do
463
+ expect(::Test::Resource.new(:name => "").name!).to eq("")
464
+ end
465
+
466
+ it 'returns nil if extension field is unset' do
467
+ expect(subject.ext_is_searchable!).to be_nil
468
+ end
469
+
470
+ it 'returns value of set extension field' do
471
+ message = ::Test::Resource.new(:ext_is_searchable => true)
472
+ expect(message.ext_is_searchable!).to be(true)
473
+ end
474
+
475
+ it 'returns value of set extension field with default' do
476
+ message = ::Test::Resource.new(:ext_is_searchable => false)
477
+ expect(message.ext_is_searchable!).to be(false)
478
+ end
479
+
480
+ it 'returns nil for an unset repeated field that has only be read' do
481
+ message = ::Test::Resource.new
482
+ expect(message.repeated_enum!).to be_nil
483
+ message.repeated_enum
484
+ expect(message.repeated_enum!).to be_nil
485
+ end
486
+
487
+ it 'returns value for an unset repeated field has been read and appended to' do
488
+ message = ::Test::Resource.new
489
+ message.repeated_enum << 1
490
+ expect(message.repeated_enum!).to eq([1])
491
+ end
492
+
493
+ it 'returns value for an unset repeated field has been explicitly set' do
494
+ message = ::Test::Resource.new
495
+ message.repeated_enum = [1]
496
+ expect(message.repeated_enum!).to eq([1])
497
+ end
311
498
  end
312
499
  end
313
500
 
@@ -317,12 +504,15 @@ describe Protobuf::Message do
317
504
  expect(field).to be_a(::Protobuf::Field::BoolField)
318
505
  expect(field.tag).to eq(100)
319
506
  expect(field.name).to eq(:ext_is_searchable)
507
+ expect(field.fully_qualified_name).to eq(:'.test.Searchable.ext_is_searchable')
320
508
  expect(field).to be_extension
321
509
  end
322
510
 
323
511
  it 'fetches an extension field by its symbolized name' do
324
512
  expect(::Test::Resource.get_extension_field(:ext_is_searchable)).to be_a(::Protobuf::Field::BoolField)
325
513
  expect(::Test::Resource.get_extension_field('ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
514
+ expect(::Test::Resource.get_extension_field(:'.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
515
+ expect(::Test::Resource.get_extension_field('.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
326
516
  end
327
517
 
328
518
  it 'returns nil when attempting to get a non-extension field' do
@@ -330,8 +520,91 @@ describe Protobuf::Message do
330
520
  end
331
521
 
332
522
  it 'returns nil when field is not found' do
333
- ::Test::Resource.get_extension_field(-1).should be_nil
334
- ::Test::Resource.get_extension_field(nil).should be_nil
523
+ expect(::Test::Resource.get_extension_field(-1)).to be_nil
524
+ expect(::Test::Resource.get_extension_field(nil)).to be_nil
525
+ end
526
+ end
527
+
528
+ describe '#field?' do
529
+ it 'returns false for non-existent field' do
530
+ expect(::Test::Resource.get_field('doesnotexist')).to be_nil
531
+ expect(::Test::Resource.new.field?('doesnotexist')).to be(false)
532
+ end
533
+
534
+ it 'returns false for unset field' do
535
+ expect(::Test::Resource.get_field('name')).to be
536
+ expect(::Test::Resource.new.field?('name')).to be(false)
537
+ end
538
+
539
+ it 'returns false for unset field from tag' do
540
+ expect(::Test::Resource.get_field(1)).to be
541
+ expect(::Test::Resource.new.field?(1)).to be(false)
542
+ end
543
+
544
+ it 'returns true for set field' do
545
+ expect(::Test::Resource.new(:name => "Joe").field?('name')).to be(true)
546
+ end
547
+
548
+ it 'returns true for set field with default' do
549
+ expect(::Test::Resource.new(:name => "").field?('name')).to be(true)
550
+ end
551
+
552
+ it 'returns true from field tag value' do
553
+ expect(::Test::Resource.new(:name => "Joe").field?(1)).to be(true)
554
+ end
555
+
556
+ it 'returns false for unset extension field' do
557
+ ext_field = :".test.Searchable.ext_is_searchable"
558
+ expect(::Test::Resource.get_extension_field(ext_field)).to be
559
+ expect(::Test::Resource.new.field?(ext_field)).to be(false)
560
+ end
561
+
562
+ it 'returns false for unset extension field from tag' do
563
+ expect(::Test::Resource.get_extension_field(100)).to be
564
+ expect(::Test::Resource.new.field?(100)).to be(false)
565
+ end
566
+
567
+ it 'returns true for set extension field' do
568
+ ext_field = :".test.Searchable.ext_is_searchable"
569
+ message = ::Test::Resource.new(ext_field => true)
570
+ expect(message.field?(ext_field)).to be(true)
571
+ end
572
+
573
+ it 'returns true for set extension field with default' do
574
+ ext_field = :".test.Searchable.ext_is_searchable"
575
+ message = ::Test::Resource.new(ext_field => false)
576
+ expect(message.field?(ext_field)).to be(true)
577
+ end
578
+
579
+ it 'returns true for set extension field from tag' do
580
+ ext_field = :".test.Searchable.ext_is_searchable"
581
+ message = ::Test::Resource.new(ext_field => false)
582
+ expect(message.field?(100)).to be(true)
583
+ end
584
+
585
+ it 'returns false for repeated field that has been read from' do
586
+ message = ::Test::Resource.new
587
+ expect(message.field?(:repeated_enum)).to be(false)
588
+ message.repeated_enum
589
+ expect(message.field?(:repeated_enum)).to be(false)
590
+ end
591
+
592
+ it 'returns true for a repeated field that has been read from and appended to' do
593
+ message = ::Test::Resource.new
594
+ message.repeated_enum << 1
595
+ expect(message.field?(:repeated_enum)).to be(true)
596
+ end
597
+
598
+ it 'returns true for a repeated field that has been set with the setter' do
599
+ message = ::Test::Resource.new
600
+ message.repeated_enum = [1]
601
+ expect(message.field?(:repeated_enum)).to be(true)
602
+ end
603
+
604
+ it 'returns false for a repeated field that has been replaced with []' do
605
+ message = ::Test::Resource.new
606
+ message.repeated_enum.replace([])
607
+ expect(message.field?(:repeated_enum)).to be(false)
335
608
  end
336
609
  end
337
610
 
@@ -341,6 +614,7 @@ describe Protobuf::Message do
341
614
  expect(field).to be_a(::Protobuf::Field::StringField)
342
615
  expect(field.tag).to eq(1)
343
616
  expect(field.name).to eq(:name)
617
+ expect(field.fully_qualified_name).to eq(:name)
344
618
  expect(field).not_to be_extension
345
619
  end
346
620
 
@@ -351,8 +625,8 @@ describe Protobuf::Message do
351
625
 
352
626
  it 'fetches an extension field when forced' do
353
627
  expect(::Test::Resource.get_field(100, true)).to be_a(::Protobuf::Field::BoolField)
354
- expect(::Test::Resource.get_field(:ext_is_searchable, true)).to be_a(::Protobuf::Field::BoolField)
355
- expect(::Test::Resource.get_field('ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
628
+ expect(::Test::Resource.get_field(:'.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
629
+ expect(::Test::Resource.get_field('.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
356
630
  end
357
631
 
358
632
  it 'returns nil when attempting to get an extension field' do
@@ -360,9 +634,234 @@ describe Protobuf::Message do
360
634
  end
361
635
 
362
636
  it 'returns nil when field is not defined' do
363
- ::Test::Resource.get_field(-1).should be_nil
364
- ::Test::Resource.get_field(nil).should be_nil
637
+ expect(::Test::Resource.get_field(-1)).to be_nil
638
+ expect(::Test::Resource.get_field(nil)).to be_nil
639
+ end
640
+ end
641
+
642
+ describe 'defining a field' do
643
+ # Case 1
644
+ context 'single base field' do
645
+ let(:klass) do
646
+ Class.new(Protobuf::Message) do
647
+ optional :string, :foo, 1
648
+ end
649
+ end
650
+
651
+ it 'has an accessor for foo' do
652
+ message = klass.new(:foo => 'bar')
653
+ expect(message.foo).to eq('bar')
654
+ expect(message[:foo]).to eq('bar')
655
+ expect(message['foo']).to eq('bar')
656
+ end
657
+ end
658
+
659
+ # Case 2
660
+ context 'base field and extension field name collision' do
661
+ let(:klass) do
662
+ Class.new(Protobuf::Message) do
663
+ optional :string, :foo, 1
664
+ optional :string, :".boom.foo", 2, :extension => true
665
+ end
666
+ end
667
+
668
+ it 'has an accessor for foo that refers to the base field' do
669
+ message = klass.new(:foo => 'bar', '.boom.foo' => 'bam')
670
+ expect(message.foo).to eq('bar')
671
+ expect(message[:foo]).to eq('bar')
672
+ expect(message['foo']).to eq('bar')
673
+ expect(message[:'.boom.foo']).to eq('bam')
674
+ expect(message['.boom.foo']).to eq('bam')
675
+ end
676
+ end
677
+
678
+ # Case 3
679
+ context 'no base field with extension fields with name collision' do
680
+ let(:klass) do
681
+ Class.new(Protobuf::Message) do
682
+ optional :string, :".boom.foo", 2, :extension => true
683
+ optional :string, :".goat.foo", 3, :extension => true
684
+ end
685
+ end
686
+
687
+ it 'has an accessor for foo that refers to the extension field' do
688
+ message = klass.new('.boom.foo' => 'bam', '.goat.foo' => 'red')
689
+ expect { message.foo }.to raise_error(NoMethodError)
690
+ expect { message[:foo] }.to raise_error(ArgumentError)
691
+ expect { message['foo'] }.to raise_error(ArgumentError)
692
+ expect(message[:'.boom.foo']).to eq('bam')
693
+ expect(message['.boom.foo']).to eq('bam')
694
+ expect(message[:'.goat.foo']).to eq('red')
695
+ expect(message['.goat.foo']).to eq('red')
696
+ end
697
+ end
698
+
699
+ # Case 4
700
+ context 'no base field with an extension field' do
701
+ let(:klass) do
702
+ Class.new(Protobuf::Message) do
703
+ optional :string, :".boom.foo", 2, :extension => true
704
+ end
705
+ end
706
+
707
+ it 'has an accessor for foo that refers to the extension field' do
708
+ message = klass.new('.boom.foo' => 'bam')
709
+ expect(message.foo).to eq('bam')
710
+ expect(message[:foo]).to eq('bam')
711
+ expect(message['foo']).to eq('bam')
712
+ expect(message[:'.boom.foo']).to eq('bam')
713
+ expect(message['.boom.foo']).to eq('bam')
714
+ end
365
715
  end
366
716
  end
367
717
 
718
+ describe '.[]=' do
719
+ context 'clearing fields' do
720
+ it 'clears repeated fields with an empty array' do
721
+ instance = ::Test::Resource.new(:repeated_enum => [::Test::StatusType::ENABLED])
722
+ expect(instance.field?(:repeated_enum)).to be(true)
723
+ instance[:repeated_enum] = []
724
+ expect(instance.field?(:repeated_enum)).to be(false)
725
+ end
726
+
727
+ it 'clears optional fields with nil' do
728
+ instance = ::Test::Resource.new(:name => "Joe")
729
+ expect(instance.field?(:name)).to be(true)
730
+ instance[:name] = nil
731
+ expect(instance.field?(:name)).to be(false)
732
+ end
733
+
734
+ it 'clears optional extenstion fields with nil' do
735
+ instance = ::Test::Resource.new(:ext_is_searchable => true)
736
+ expect(instance.field?(:ext_is_searchable)).to be(true)
737
+ instance[:ext_is_searchable] = nil
738
+ expect(instance.field?(:ext_is_searchable)).to be(false)
739
+ end
740
+ end
741
+
742
+ context 'setting fields' do
743
+ let(:instance) { ::Test::Resource.new }
744
+
745
+ it 'sets and replaces repeated fields' do
746
+ initial = [::Test::StatusType::ENABLED, ::Test::StatusType::DISABLED]
747
+ instance[:repeated_enum] = initial
748
+ expect(instance[:repeated_enum]).to eq(initial)
749
+ replacement = [::Test::StatusType::DELETED]
750
+ instance[:repeated_enum] = replacement
751
+ expect(instance[:repeated_enum]).to eq(replacement)
752
+ end
753
+
754
+ it 'sets acceptable optional field values' do
755
+ instance[:name] = "Joe"
756
+ expect(instance[:name]).to eq("Joe")
757
+ instance[1] = "Tom"
758
+ expect(instance[:name]).to eq("Tom")
759
+ end
760
+
761
+ it 'sets acceptable empty string field values' do
762
+ instance[:name] = ""
763
+ expect(instance.name!).to eq("")
764
+ end
765
+
766
+ it 'sets acceptable empty message field values' do
767
+ instance = ::Test::Nested.new
768
+ instance[:resource] = {}
769
+ expect(instance.resource!).to eq(::Test::Resource.new)
770
+ end
771
+
772
+ it 'sets acceptable extension field values' do
773
+ instance[:ext_is_searchable] = true
774
+ expect(instance[:ext_is_searchable]).to eq(true)
775
+ instance[:".test.Searchable.ext_is_searchable"] = false
776
+ expect(instance[:ext_is_searchable]).to eq(false)
777
+ instance[100] = true
778
+ expect(instance[:ext_is_searchable]).to eq(true)
779
+ end
780
+
781
+ # to be deprecated
782
+ it 'does nothing when sent an empty array' do
783
+ instance[:repeated_enum] = nil
784
+ expect(instance[:repeated_enum]).to eq([])
785
+ instance[:repeated_enum] = [1, 2]
786
+ expect(instance[:repeated_enum]).to eq([1, 2])
787
+ instance[:repeated_enum] = nil
788
+ # Yes this is very silly, but backwards compatible
789
+ expect(instance[:repeated_enum]).to eq([1, 2])
790
+ end
791
+ end
792
+
793
+ context 'throwing TypeError' do
794
+ let(:instance) { ::Test::Resource.new }
795
+
796
+ it 'throws when a repeated value is set with a non array' do
797
+ expect { instance[:repeated_enum] = "string" }.to raise_error(TypeError)
798
+ end
799
+
800
+ it 'throws when a repeated value is set with an array of the wrong type' do
801
+ expect { instance[:repeated_enum] = [true, false] }.to raise_error(TypeError)
802
+ end
803
+
804
+ it 'throws when an optional value is not #acceptable?' do
805
+ expect { instance[:name] = 1 }.to raise_error(TypeError)
806
+ end
807
+ end
808
+
809
+ context 'ignoring unknown fields' do
810
+ around do |example|
811
+ orig = ::Protobuf.ignore_unknown_fields?
812
+ ::Protobuf.ignore_unknown_fields = true
813
+ example.call
814
+ ::Protobuf.ignore_unknown_fields = orig
815
+ end
816
+
817
+ context 'with valid fields' do
818
+ let(:values) { { :name => "Jim" } }
819
+
820
+ it "does not raise an error" do
821
+ expect { ::Test::Resource.new(values) }.to_not raise_error
822
+ end
823
+ end
824
+
825
+ context 'with non-existent field' do
826
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
827
+
828
+ it "does not raise an error" do
829
+ expect { ::Test::Resource.new(values) }.to_not raise_error
830
+ end
831
+ end
832
+ end
833
+
834
+ context 'not ignoring unknown fields' do
835
+ around do |example|
836
+ orig = ::Protobuf.ignore_unknown_fields?
837
+ ::Protobuf.ignore_unknown_fields = false
838
+ example.call
839
+ ::Protobuf.ignore_unknown_fields = orig
840
+ end
841
+
842
+ context 'with valid fields' do
843
+ let(:values) { { :name => "Jim" } }
844
+
845
+ it "does not raise an error" do
846
+ expect { ::Test::Resource.new(values) }.to_not raise_error
847
+ end
848
+ end
849
+
850
+ context 'with non-existent field' do
851
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
852
+
853
+ it "raises an error and mentions the erroneous field" do
854
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
855
+ end
856
+
857
+ context 'with a nil value' do
858
+ let(:values) { { :name => "Jim", :othername => nil } }
859
+
860
+ it "raises an error and mentions the erroneous field" do
861
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
862
+ end
863
+ end
864
+ end
865
+ end
866
+ end
368
867
  end