protobuf-cucumber 3.10.4

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 (204) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +70 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +40 -0
  6. data/.yardopts +5 -0
  7. data/CHANGES.md +344 -0
  8. data/CONTRIBUTING.md +16 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +33 -0
  12. data/Rakefile +64 -0
  13. data/bin/protoc-gen-ruby +22 -0
  14. data/bin/rpc_server +5 -0
  15. data/install-protobuf.sh +28 -0
  16. data/lib/protobuf.rb +129 -0
  17. data/lib/protobuf/cli.rb +257 -0
  18. data/lib/protobuf/code_generator.rb +120 -0
  19. data/lib/protobuf/decoder.rb +28 -0
  20. data/lib/protobuf/deprecation.rb +117 -0
  21. data/lib/protobuf/descriptors.rb +3 -0
  22. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
  23. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
  24. data/lib/protobuf/encoder.rb +11 -0
  25. data/lib/protobuf/enum.rb +365 -0
  26. data/lib/protobuf/exceptions.rb +9 -0
  27. data/lib/protobuf/field.rb +74 -0
  28. data/lib/protobuf/field/base_field.rb +380 -0
  29. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  30. data/lib/protobuf/field/bool_field.rb +64 -0
  31. data/lib/protobuf/field/bytes_field.rb +78 -0
  32. data/lib/protobuf/field/double_field.rb +25 -0
  33. data/lib/protobuf/field/enum_field.rb +61 -0
  34. data/lib/protobuf/field/field_array.rb +104 -0
  35. data/lib/protobuf/field/field_hash.rb +122 -0
  36. data/lib/protobuf/field/fixed32_field.rb +25 -0
  37. data/lib/protobuf/field/fixed64_field.rb +28 -0
  38. data/lib/protobuf/field/float_field.rb +43 -0
  39. data/lib/protobuf/field/int32_field.rb +21 -0
  40. data/lib/protobuf/field/int64_field.rb +34 -0
  41. data/lib/protobuf/field/integer_field.rb +23 -0
  42. data/lib/protobuf/field/message_field.rb +51 -0
  43. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  44. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  45. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  46. data/lib/protobuf/field/sint32_field.rb +21 -0
  47. data/lib/protobuf/field/sint64_field.rb +21 -0
  48. data/lib/protobuf/field/string_field.rb +51 -0
  49. data/lib/protobuf/field/uint32_field.rb +21 -0
  50. data/lib/protobuf/field/uint64_field.rb +21 -0
  51. data/lib/protobuf/field/varint_field.rb +77 -0
  52. data/lib/protobuf/generators/base.rb +85 -0
  53. data/lib/protobuf/generators/enum_generator.rb +39 -0
  54. data/lib/protobuf/generators/extension_generator.rb +27 -0
  55. data/lib/protobuf/generators/field_generator.rb +193 -0
  56. data/lib/protobuf/generators/file_generator.rb +262 -0
  57. data/lib/protobuf/generators/group_generator.rb +122 -0
  58. data/lib/protobuf/generators/message_generator.rb +104 -0
  59. data/lib/protobuf/generators/option_generator.rb +17 -0
  60. data/lib/protobuf/generators/printable.rb +160 -0
  61. data/lib/protobuf/generators/service_generator.rb +50 -0
  62. data/lib/protobuf/lifecycle.rb +33 -0
  63. data/lib/protobuf/logging.rb +39 -0
  64. data/lib/protobuf/message.rb +260 -0
  65. data/lib/protobuf/message/fields.rb +233 -0
  66. data/lib/protobuf/message/serialization.rb +85 -0
  67. data/lib/protobuf/optionable.rb +70 -0
  68. data/lib/protobuf/rpc/buffer.rb +78 -0
  69. data/lib/protobuf/rpc/client.rb +140 -0
  70. data/lib/protobuf/rpc/connectors/base.rb +221 -0
  71. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  72. data/lib/protobuf/rpc/connectors/socket.rb +78 -0
  73. data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
  74. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
  75. data/lib/protobuf/rpc/env.rb +60 -0
  76. data/lib/protobuf/rpc/error.rb +28 -0
  77. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  78. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  79. data/lib/protobuf/rpc/middleware.rb +25 -0
  80. data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
  81. data/lib/protobuf/rpc/middleware/logger.rb +95 -0
  82. data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
  83. data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
  84. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  85. data/lib/protobuf/rpc/rpc.pb.rb +64 -0
  86. data/lib/protobuf/rpc/rpc_method.rb +16 -0
  87. data/lib/protobuf/rpc/server.rb +39 -0
  88. data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
  89. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  90. data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
  91. data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
  92. data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
  93. data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
  94. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  95. data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
  96. data/lib/protobuf/rpc/service.rb +172 -0
  97. data/lib/protobuf/rpc/service_directory.rb +261 -0
  98. data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
  99. data/lib/protobuf/rpc/service_filters.rb +250 -0
  100. data/lib/protobuf/rpc/stat.rb +119 -0
  101. data/lib/protobuf/socket.rb +21 -0
  102. data/lib/protobuf/tasks.rb +1 -0
  103. data/lib/protobuf/tasks/compile.rake +80 -0
  104. data/lib/protobuf/varint.rb +20 -0
  105. data/lib/protobuf/varint_pure.rb +31 -0
  106. data/lib/protobuf/version.rb +3 -0
  107. data/lib/protobuf/wire_type.rb +10 -0
  108. data/lib/protobuf/zmq.rb +21 -0
  109. data/profile.html +5103 -0
  110. data/proto/dynamic_discovery.proto +44 -0
  111. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  112. data/proto/google/protobuf/descriptor.proto +779 -0
  113. data/proto/rpc.proto +69 -0
  114. data/protobuf-cucumber.gemspec +57 -0
  115. data/spec/benchmark/tasks.rb +143 -0
  116. data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
  117. data/spec/encoding/all_types_spec.rb +103 -0
  118. data/spec/encoding/extreme_values_spec.rb +0 -0
  119. data/spec/functional/class_inheritance_spec.rb +52 -0
  120. data/spec/functional/code_generator_spec.rb +58 -0
  121. data/spec/functional/socket_server_spec.rb +59 -0
  122. data/spec/functional/zmq_server_spec.rb +105 -0
  123. data/spec/lib/protobuf/cli_spec.rb +317 -0
  124. data/spec/lib/protobuf/code_generator_spec.rb +87 -0
  125. data/spec/lib/protobuf/enum_spec.rb +307 -0
  126. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  127. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
  129. data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
  130. data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
  131. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  134. data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
  135. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  136. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  137. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  138. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  139. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  140. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  141. data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
  142. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  143. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  144. data/spec/lib/protobuf/field_spec.rb +192 -0
  145. data/spec/lib/protobuf/generators/base_spec.rb +154 -0
  146. data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
  147. data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
  148. data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
  149. data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
  150. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  151. data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
  152. data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
  153. data/spec/lib/protobuf/message_spec.rb +944 -0
  154. data/spec/lib/protobuf/optionable_spec.rb +265 -0
  155. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  156. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
  157. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  158. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
  159. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
  160. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  161. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  162. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  163. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
  164. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  165. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
  166. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  167. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  168. data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
  169. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
  170. data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
  171. data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
  172. data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
  173. data/spec/lib/protobuf/varint_spec.rb +29 -0
  174. data/spec/lib/protobuf_spec.rb +105 -0
  175. data/spec/spec_helper.rb +42 -0
  176. data/spec/support/all.rb +6 -0
  177. data/spec/support/packed_field.rb +23 -0
  178. data/spec/support/protos/all_types.data.bin +0 -0
  179. data/spec/support/protos/all_types.data.txt +119 -0
  180. data/spec/support/protos/enum.pb.rb +63 -0
  181. data/spec/support/protos/enum.proto +37 -0
  182. data/spec/support/protos/extreme_values.data.bin +0 -0
  183. data/spec/support/protos/google_unittest.bin +0 -0
  184. data/spec/support/protos/google_unittest.pb.rb +798 -0
  185. data/spec/support/protos/google_unittest.proto +884 -0
  186. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  187. data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
  188. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  189. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  190. data/spec/support/protos/google_unittest_import.proto +73 -0
  191. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  192. data/spec/support/protos/google_unittest_import_public.proto +41 -0
  193. data/spec/support/protos/map-test.bin +157 -0
  194. data/spec/support/protos/map-test.pb.rb +85 -0
  195. data/spec/support/protos/map-test.proto +68 -0
  196. data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
  197. data/spec/support/protos/multi_field_extensions.proto +35 -0
  198. data/spec/support/protos/resource.pb.rb +172 -0
  199. data/spec/support/protos/resource.proto +137 -0
  200. data/spec/support/resource_service.rb +23 -0
  201. data/spec/support/server.rb +65 -0
  202. data/spec/support/test_app_file.rb +2 -0
  203. data/varint_prof.rb +82 -0
  204. metadata +579 -0
@@ -0,0 +1,85 @@
1
+ require 'stringio'
2
+ require 'protobuf/decoder'
3
+ require 'protobuf/encoder'
4
+
5
+ module Protobuf
6
+ class Message
7
+ module Serialization
8
+
9
+ module ClassMethods
10
+ def decode(bytes)
11
+ new.decode(bytes)
12
+ end
13
+
14
+ def decode_from(stream)
15
+ new.decode_from(stream)
16
+ end
17
+
18
+ # Create a new object with the given values and return the encoded bytes.
19
+ def encode(fields = {})
20
+ new(fields).encode
21
+ end
22
+ end
23
+
24
+ def self.included(other)
25
+ other.extend(ClassMethods)
26
+ end
27
+
28
+ ##
29
+ # Instance Methods
30
+ #
31
+
32
+ # Decode the given non-stream bytes into this message.
33
+ #
34
+ def decode(bytes)
35
+ decode_from(::StringIO.new(bytes))
36
+ end
37
+
38
+ # Decode the given stream into this message.
39
+ #
40
+ def decode_from(stream)
41
+ ::Protobuf::Decoder.decode_each_field(stream) do |tag, bytes|
42
+ set_field_bytes(tag, bytes)
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ # Encode this message
49
+ #
50
+ def encode
51
+ stream = ::StringIO.new
52
+ stream.set_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
53
+ encode_to(stream)
54
+ stream.string
55
+ end
56
+
57
+ # Encode this message to the given stream.
58
+ #
59
+ def encode_to(stream)
60
+ ::Protobuf::Encoder.encode(self, stream)
61
+ end
62
+
63
+ ##
64
+ # Instance Aliases
65
+ #
66
+ alias :parse_from_string decode
67
+ alias :deserialize decode
68
+ alias :parse_from decode_from
69
+ alias :deserialize_from decode_from
70
+ alias :to_s encode
71
+ alias :bytes encode
72
+ alias :serialize encode
73
+ alias :serialize_to_string encode
74
+ alias :serialize_to encode_to
75
+
76
+ private
77
+
78
+ def set_field_bytes(tag, bytes)
79
+ field = _protobuf_message_field[tag]
80
+ field.set(self, bytes) if field
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,70 @@
1
+ module Protobuf
2
+ module Optionable
3
+ module ClassMethods
4
+ def get_option(name)
5
+ name = name.to_s
6
+ option = optionable_descriptor_class.get_field(name, true)
7
+ fail ArgumentError, "invalid option=#{name}" unless option
8
+ unless option.fully_qualified_name.to_s == name
9
+ # Eventually we'll deprecate the use of simple names of fields completely, but for now make sure people
10
+ # are accessing options correctly. We allow simple names in other places for backwards compatibility.
11
+ fail ArgumentError, "must access option using its fully qualified name: #{option.fully_qualified_name.inspect}"
12
+ end
13
+ value =
14
+ if @_optionable_options.try(:key?, name)
15
+ @_optionable_options[name]
16
+ else
17
+ option.default_value
18
+ end
19
+ if option.type_class < ::Protobuf::Message
20
+ option.type_class.new(value)
21
+ else
22
+ value
23
+ end
24
+ end
25
+
26
+ def get_option!(name)
27
+ get_option(name) if @_optionable_options.try(:key?, name.to_s)
28
+ end
29
+
30
+ private
31
+
32
+ def set_option(name, value = true)
33
+ @_optionable_options ||= {}
34
+ @_optionable_options[name.to_s] = value
35
+ end
36
+ end
37
+
38
+ def get_option(name)
39
+ self.class.get_option(name)
40
+ end
41
+
42
+ def get_option!(name)
43
+ self.class.get_option!(name)
44
+ end
45
+
46
+ def self.inject(base_class, extend_class = true, &block)
47
+ unless block_given?
48
+ fail ArgumentError, 'missing option class block (e.g: ::Google::Protobuf::MessageOptions)'
49
+ end
50
+ if extend_class
51
+ # Check if optionable_descriptor_class is already defined and short circuit if so.
52
+ # File options are injected per module, and since a module can be defined more than once,
53
+ # we will get a warning if we try to define optionable_descriptor_class twice.
54
+ if base_class.respond_to?(:optionable_descriptor_class)
55
+ # Don't define optionable_descriptor_class twice
56
+ return if base_class.optionable_descriptor_class == block.call
57
+
58
+ fail 'A class is being defined with two different descriptor classes, something is very wrong'
59
+ end
60
+
61
+ base_class.extend(ClassMethods)
62
+ base_class.__send__(:include, self)
63
+ base_class.define_singleton_method(:optionable_descriptor_class, block)
64
+ else
65
+ base_class.__send__(:include, ClassMethods)
66
+ base_class.module_eval { define_method(:optionable_descriptor_class, block) }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,78 @@
1
+ module Protobuf
2
+ module Rpc
3
+ class Buffer
4
+
5
+ attr_accessor :mode, :data, :size
6
+
7
+ MODES = [:read, :write].freeze
8
+
9
+ # constantize this so we don't re-initialize the regex every time we need it
10
+ SIZE_REGEX = /^\d+-/
11
+
12
+ def initialize(mode = :read)
13
+ @flush = false
14
+ @data = ""
15
+ @size = 0
16
+ self.mode = mode
17
+ end
18
+
19
+ def mode=(mode)
20
+ @mode =
21
+ if MODES.include?(mode)
22
+ mode
23
+ else
24
+ :read
25
+ end
26
+ end
27
+
28
+ def write(force_mode = true)
29
+ if force_mode && reading?
30
+ self.mode = :write
31
+ elsif !force_mode && reading?
32
+ fail 'You chose to write the buffer when in read mode'
33
+ end
34
+
35
+ @size = @data.length
36
+ "#{@size}-#{@data}"
37
+ end
38
+
39
+ def <<(data)
40
+ @data << data
41
+ if reading?
42
+ get_data_size
43
+ check_for_flush
44
+ end
45
+ end
46
+
47
+ def set_data(data) # rubocop:disable Style/AccessorMethodName
48
+ @data = data.to_s
49
+ @size = @data.size
50
+ end
51
+
52
+ def reading?
53
+ mode == :read
54
+ end
55
+
56
+ def writing?
57
+ mode == :write
58
+ end
59
+
60
+ def flushed?
61
+ @flush
62
+ end
63
+
64
+ def get_data_size # rubocop:disable Style/AccessorMethodName
65
+ if @size == 0 || @data.match(SIZE_REGEX)
66
+ sliced_size = @data.slice!(SIZE_REGEX)
67
+ @size = sliced_size.delete('-').to_i unless sliced_size.nil?
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def check_for_flush
74
+ @flush = true if !@size.nil? && @data.length == @size
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,140 @@
1
+ require 'forwardable'
2
+ require 'protobuf'
3
+ require 'protobuf/logging'
4
+ require 'protobuf/rpc/error'
5
+
6
+ module Protobuf
7
+ module Rpc
8
+ class Client
9
+ extend Forwardable
10
+ include Protobuf::Logging
11
+
12
+ def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb, :send_request
13
+ attr_reader :connector
14
+
15
+ # Create a new client with default options (defined in ClientConnection)
16
+ # See Service#client for a more convenient way to create a client, as well
17
+ # as Client#method_missing defined below.
18
+ #
19
+ # request = WidgetFindRequest.new
20
+ # client = Client.new({
21
+ # :service => WidgetService,
22
+ # :method => "find",
23
+ # :request_type => "WidgetFindRequest",
24
+ # :response_type => "WidgetList",
25
+ # :request => request
26
+ # })
27
+ #
28
+ def initialize(options = {})
29
+ fail "Invalid client configuration. Service must be defined." if options[:service].nil?
30
+ @connector = ::Protobuf.connector_type_class.new(options)
31
+ logger.debug { sign_message("Initialized with options: #{options.inspect}") }
32
+ end
33
+
34
+ def log_signature
35
+ @_log_signature ||= "[client-#{self.class}]"
36
+ end
37
+
38
+ # Set a complete callback on the client to return the object (self).
39
+ #
40
+ # client = Client.new(:service => WidgetService)
41
+ # client.on_complete {|obj| ... }
42
+ #
43
+ def on_complete(&complete_cb)
44
+ @connector.complete_cb = complete_cb
45
+ end
46
+
47
+ def on_complete=(callable)
48
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
49
+ fail "callable must take a single argument and respond to :call"
50
+ end
51
+
52
+ @connector.complete_cb = callable
53
+ end
54
+
55
+ # Set a failure callback on the client to return the
56
+ # error returned by the service, if any. If this callback
57
+ # is called, success_cb will NOT be called.
58
+ #
59
+ # client = Client.new(:service => WidgetService)
60
+ # client.on_failure {|err| ... }
61
+ #
62
+ def on_failure(&failure_cb)
63
+ @connector.failure_cb = failure_cb
64
+ end
65
+
66
+ def on_failure=(callable)
67
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
68
+ fail "Callable must take a single argument and respond to :call"
69
+ end
70
+
71
+ @connector.failure_cb = callable
72
+ end
73
+
74
+ # Set a success callback on the client to return the
75
+ # successful response from the service when it is returned.
76
+ # If this callback is called, failure_cb will NOT be called.
77
+ #
78
+ # client = Client.new(:service => WidgetService)
79
+ # client.on_success {|res| ... }
80
+ #
81
+ def on_success(&success_cb)
82
+ @connector.success_cb = success_cb
83
+ end
84
+
85
+ def on_success=(callable)
86
+ if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
87
+ fail "Callable must take a single argument and respond to :call"
88
+ end
89
+
90
+ @connector.success_cb = callable
91
+ end
92
+
93
+ # Provides a mechanism to call the service method against the client
94
+ # which will automatically setup the service_class and method_name
95
+ # in the wrapper protobuf request.
96
+ #
97
+ # # The :find method is not defined by Client which will trigger method_missing
98
+ # Client.new(:service => WidgetService).find do |c|
99
+ # # This block will be invoked before the request is made
100
+ # # `c` in this case is the client object you created above
101
+ # c.on_success {|res| ... }
102
+ # c.on_failure {|err| ... }
103
+ # end
104
+ #
105
+ def method_missing(method_name, *params)
106
+ service = options[:service]
107
+ if service.rpc_method?(method_name)
108
+ logger.debug { sign_message("#{service.name}##{method_name}") }
109
+ rpc = service.rpcs[method_name.to_sym]
110
+
111
+ options[:request_type] = rpc.request_type
112
+ logger.debug { sign_message("Request Type: #{options[:request_type].name}") }
113
+
114
+ options[:response_type] = rpc.response_type
115
+ logger.debug { sign_message("Response Type: #{options[:response_type].name}") }
116
+
117
+ options[:method] = method_name.to_s
118
+ options[:request] = params[0].is_a?(Hash) ? options[:request_type].new(params[0]) : params[0]
119
+ logger.debug { sign_message("Request Data: #{options[:request].inspect}") }
120
+
121
+ # Call client to setup on_success and on_failure event callbacks
122
+ if block_given?
123
+ logger.debug { sign_message("client setup callback given, invoking") }
124
+ yield(self)
125
+ else
126
+ logger.debug { sign_message("no block given for callbacks") }
127
+ end
128
+
129
+ send_request
130
+ else
131
+ logger.error { sign_message("#{service.name}##{method_name} not rpc method, passing to super") }
132
+ super(method_name, *params)
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ ActiveSupport.run_load_hooks(:protobuf_rpc_client, Client)
139
+ end
140
+ end
@@ -0,0 +1,221 @@
1
+ require 'timeout'
2
+ require 'protobuf/logging'
3
+ require 'protobuf/rpc/rpc.pb'
4
+ require 'protobuf/rpc/buffer'
5
+ require 'protobuf/rpc/error'
6
+ require 'protobuf/rpc/stat'
7
+
8
+ module Protobuf
9
+ module Rpc
10
+ module Connectors
11
+ DEFAULT_OPTIONS = {
12
+ :service => nil, # Fully-qualified Service class
13
+ :method => nil, # Service method to invoke
14
+ :host => '127.0.0.1', # The hostname or address of the service (usually overridden)
15
+ :port => '9399', # The port of the service (usually overridden or pre-configured)
16
+ :request => nil, # The request object sent by the client
17
+ :request_type => nil, # The request type expected by the client
18
+ :response_type => nil, # The response type expected by the client
19
+ :timeout => nil, # The timeout for the request, also handled by client.rb
20
+ :client_host => nil, # The hostname or address of this client
21
+ :first_alive_load_balance => false, # Do we want to use check_avail frames before request
22
+ }.freeze
23
+
24
+ class Base
25
+ include Protobuf::Logging
26
+
27
+ attr_reader :options, :error
28
+ attr_accessor :success_cb, :failure_cb, :complete_cb, :stats
29
+
30
+ def initialize(options)
31
+ @options = DEFAULT_OPTIONS.merge(options)
32
+ @stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
33
+ end
34
+
35
+ def any_callbacks?
36
+ [@complete_cb, @failure_cb, @success_cb].any?
37
+ end
38
+
39
+ def close_connection
40
+ fail 'If you inherit a Connector from Base you must implement close_connection'
41
+ end
42
+
43
+ def complete
44
+ @stats.stop
45
+ logger.info { @stats.to_s }
46
+ logger.debug { sign_message('Response proceessing complete') }
47
+ @complete_cb.call(self) unless @complete_cb.nil?
48
+ rescue => e
49
+ logger.error { sign_message('Complete callback error encountered') }
50
+ log_exception(e)
51
+ raise
52
+ end
53
+
54
+ def data_callback(data)
55
+ logger.debug { sign_message('Using data_callback') }
56
+ @used_data_callback = true
57
+ @data = data
58
+ end
59
+
60
+ # All failures should be routed through this method.
61
+ #
62
+ # @param [Symbol] code The code we're using (see ::Protobuf::Socketrpc::ErrorReason)
63
+ # @param [String] message The error message
64
+ def failure(code, message)
65
+ @error = ClientError.new
66
+ @stats.status = @error.code = ::Protobuf::Socketrpc::ErrorReason.fetch(code)
67
+ @error.message = message
68
+
69
+ logger.debug { sign_message("Server failed request (invoking on_failure): #{@error.inspect}") }
70
+
71
+ @failure_cb.call(@error) unless @failure_cb.nil?
72
+ rescue => e
73
+ logger.error { sign_message("Failure callback error encountered") }
74
+ log_exception(e)
75
+ raise
76
+ ensure
77
+ complete
78
+ end
79
+
80
+ def first_alive_load_balance?
81
+ ENV.key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
82
+ options[:first_alive_load_balance]
83
+ end
84
+
85
+ def initialize_stats
86
+ @stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
87
+ @stats.server = [@options[:port], @options[:host]]
88
+ @stats.service = @options[:service].name
89
+ @stats.method_name = @options[:method].to_s
90
+ rescue => ex
91
+ log_exception(ex)
92
+ failure(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
93
+ end
94
+
95
+ def log_signature
96
+ @_log_signature ||= "[client-#{self.class}]"
97
+ end
98
+
99
+ def parse_response
100
+ # Close up the connection as we no longer need it
101
+ close_connection
102
+
103
+ logger.debug { sign_message("Parsing response from server (connection closed)") }
104
+
105
+ # Parse out the raw response
106
+ @stats.response_size = @response_data.size unless @response_data.nil?
107
+ response_wrapper = ::Protobuf::Socketrpc::Response.decode(@response_data)
108
+ @stats.server = response_wrapper.server if response_wrapper.field?(:server)
109
+
110
+ # Determine success or failure based on parsed data
111
+ if response_wrapper.field?(:error_reason)
112
+ logger.debug { sign_message("Error response parsed") }
113
+
114
+ # fail the call if we already know the client is failed
115
+ # (don't try to parse out the response payload)
116
+ failure(response_wrapper.error_reason, response_wrapper.error)
117
+ else
118
+ logger.debug { sign_message("Successful response parsed") }
119
+
120
+ # Ensure client_response is an instance
121
+ parsed = @options[:response_type].decode(response_wrapper.response_proto.to_s)
122
+
123
+ if parsed.nil? && !response_wrapper.field?(:error_reason)
124
+ failure(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
125
+ else
126
+ verify_callbacks
127
+ succeed(parsed)
128
+ return @data if @used_data_callback
129
+ end
130
+ end
131
+ end
132
+
133
+ def ping_port
134
+ @ping_port ||= ENV["PB_RPC_PING_PORT"]
135
+ end
136
+
137
+ def ping_port_enabled?
138
+ ENV.key?("PB_RPC_PING_PORT")
139
+ end
140
+
141
+ def request_bytes
142
+ validate_request_type!
143
+ return ::Protobuf::Socketrpc::Request.encode(request_fields)
144
+ rescue => e
145
+ failure(:INVALID_REQUEST_PROTO, "Could not set request proto: #{e.message}")
146
+ end
147
+
148
+ def request_caller
149
+ @options[:client_host] || ::Protobuf.client_host
150
+ end
151
+
152
+ def request_fields
153
+ { :service_name => @options[:service].name,
154
+ :method_name => @options[:method].to_s,
155
+ :request_proto => @options[:request],
156
+ :caller => request_caller }
157
+ end
158
+
159
+ def send_request
160
+ fail 'If you inherit a Connector from Base you must implement send_request'
161
+ end
162
+
163
+ def setup_connection
164
+ initialize_stats
165
+ @request_data = request_bytes
166
+ @stats.request_size = @request_data.size
167
+ end
168
+
169
+ def succeed(response)
170
+ logger.debug { sign_message("Server succeeded request (invoking on_success)") }
171
+ @success_cb.call(response) unless @success_cb.nil?
172
+ rescue => e
173
+ logger.error { sign_message("Success callback error encountered") }
174
+ log_exception(e)
175
+ failure(:RPC_ERROR, "An exception occurred while calling on_success: #{e.message}")
176
+ ensure
177
+ complete
178
+ end
179
+
180
+ def timeout
181
+ if options[:timeout]
182
+ options[:timeout]
183
+ else
184
+ 300 # seconds
185
+ end
186
+ end
187
+
188
+ # Wrap the given block in a timeout of the configured number of seconds.
189
+ #
190
+ def timeout_wrap(&block)
191
+ ::Timeout.timeout(timeout, &block)
192
+ rescue ::Timeout::Error
193
+ failure(:RPC_FAILED, "The server took longer than #{timeout} seconds to respond")
194
+ end
195
+
196
+ def validate_request_type!
197
+ unless @options[:request].class == @options[:request_type]
198
+ expected = @options[:request_type].name
199
+ actual = @options[:request].class.name
200
+ failure(:INVALID_REQUEST_PROTO, "Expected request type to be type of #{expected}, got #{actual} instead")
201
+ end
202
+ end
203
+
204
+ def verify_callbacks
205
+ unless any_callbacks?
206
+ logger.debug { sign_message("No callbacks set, using data_callback") }
207
+ @success_cb = @failure_cb = method(:data_callback)
208
+ end
209
+ end
210
+
211
+ def verify_options!
212
+ # Verify the options that are necessary and merge them in
213
+ [:service, :method, :host, :port].each do |opt|
214
+ failure(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
215
+ end
216
+ end
217
+
218
+ end
219
+ end
220
+ end
221
+ end