protobuffy 3.6.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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