protobuf 2.2.5-java
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.
- data/.gitignore +17 -0
- data/.travis.yml +9 -0
- data/.yardopts +5 -0
- data/Gemfile +3 -0
- data/README.md +316 -0
- data/Rakefile +29 -0
- data/UPGRADING.md +60 -0
- data/bin/rpc_server +5 -0
- data/bin/rprotoc +62 -0
- data/examples/addressbook.pb.rb +55 -0
- data/examples/addressbook.proto +24 -0
- data/examples/reading_a_message.rb +32 -0
- data/examples/writing_a_message.rb +46 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/code_generator.h +142 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/command_line_interface.h +318 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_enum.h +99 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_enum_field.h +103 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_extension.h +85 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_field.h +167 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_file.h +98 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_generator.h +72 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_helpers.h +159 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_message.h +170 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_message_field.h +102 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +103 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_service.h +118 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_string_field.h +104 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h +2721 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/importer.h +303 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_enum.h +84 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_enum_field.h +121 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_extension.h +77 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_field.h +108 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_file.h +101 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_generator.h +72 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_helpers.h +213 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_message.h +109 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_message_field.h +134 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_primitive_field.h +121 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_service.h +113 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_string_field.h +120 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/mock_code_generator.h +113 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/package_info.h +64 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/parser.h +434 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/plugin.h +73 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/plugin.pb.h +790 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/python/python_generator.h +156 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/subprocess.h +108 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/compiler/zip_writer.h +93 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/descriptor.h +1367 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/descriptor.pb.h +5223 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/descriptor_database.h +366 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/dynamic_message.h +136 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/extension_set.h +904 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/generated_message_reflection.h +424 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/generated_message_util.h +82 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/coded_stream.h +1102 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/coded_stream_inl.h +64 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/gzip_stream.h +207 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/package_info.h +54 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/printer.h +136 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/tokenizer.h +313 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream.h +238 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream_impl.h +357 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream_impl_lite.h +340 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/message.h +692 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/message_lite.h +239 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/package_info.h +64 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/reflection_ops.h +80 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/repeated_field.h +1295 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/service.h +291 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/common.h +1211 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/hash.h +220 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/map-util.h +119 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/once.h +123 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/stl_util-inl.h +121 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/strutil.h +457 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/stubs/substitute.h +170 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/test_util.h +174 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/test_util_lite.h +101 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/testing/file.h +83 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/testing/googletest.h +98 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/text_format.h +285 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest.pb.h +11915 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_custom_options.pb.h +2895 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_embed_optimize_for.pb.h +211 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_empty.pb.h +56 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_import.pb.h +188 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_import_lite.pb.h +151 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_lite.pb.h +4752 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_lite_imports_nonlite.pb.h +150 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_mset.pb.h +816 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_no_generic_services.pb.h +197 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unittest_optimize_for.pb.h +403 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/unknown_field_set.h +268 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/wire_format.h +304 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/wire_format_lite.h +620 -0
- data/ext/protobuf-2.4.1/src/google/protobuf/wire_format_lite_inl.h +774 -0
- data/ext/ruby_generator/Makefile +10 -0
- data/ext/ruby_generator/RubyGenerator.cpp +450 -0
- data/ext/ruby_generator/RubyGenerator.h +199 -0
- data/ext/ruby_generator/extconf.rb +36 -0
- data/ext/ruby_generator/protoc-ruby +0 -0
- data/lib/protobuf/cli.rb +188 -0
- data/lib/protobuf/enum.rb +58 -0
- data/lib/protobuf/enum_value.rb +59 -0
- data/lib/protobuf/evented.rb +22 -0
- data/lib/protobuf/exceptions.rb +11 -0
- data/lib/protobuf/ext/eventmachine.rb +14 -0
- data/lib/protobuf/field/base_field.rb +240 -0
- data/lib/protobuf/field/bool_field.rb +36 -0
- data/lib/protobuf/field/bytes_field.rb +38 -0
- data/lib/protobuf/field/double_field.rb +19 -0
- data/lib/protobuf/field/enum_field.rb +50 -0
- data/lib/protobuf/field/extension_fields.rb +32 -0
- data/lib/protobuf/field/field_array.rb +65 -0
- data/lib/protobuf/field/fixed32_field.rb +19 -0
- data/lib/protobuf/field/fixed64_field.rb +22 -0
- data/lib/protobuf/field/float_field.rb +31 -0
- data/lib/protobuf/field/int32_field.rb +12 -0
- data/lib/protobuf/field/int64_field.rb +12 -0
- data/lib/protobuf/field/integer_field.rb +19 -0
- data/lib/protobuf/field/message_field.rb +53 -0
- data/lib/protobuf/field/sfixed32_field.rb +21 -0
- data/lib/protobuf/field/sfixed64_field.rb +24 -0
- data/lib/protobuf/field/signed_integer_field.rb +23 -0
- data/lib/protobuf/field/sint32_field.rb +12 -0
- data/lib/protobuf/field/sint64_field.rb +12 -0
- data/lib/protobuf/field/string_field.rb +14 -0
- data/lib/protobuf/field/uint32_field.rb +12 -0
- data/lib/protobuf/field/uint64_field.rb +12 -0
- data/lib/protobuf/field/varint_field.rb +61 -0
- data/lib/protobuf/field.rb +57 -0
- data/lib/protobuf/logger.rb +86 -0
- data/lib/protobuf/message/decoder.rb +83 -0
- data/lib/protobuf/message/encoder.rb +48 -0
- data/lib/protobuf/message/extend.rb +8 -0
- data/lib/protobuf/message/message.rb +1 -0
- data/lib/protobuf/message.rb +320 -0
- data/lib/protobuf/rpc/buffer.rb +79 -0
- data/lib/protobuf/rpc/client.rb +166 -0
- data/lib/protobuf/rpc/connector.rb +19 -0
- data/lib/protobuf/rpc/connectors/base.rb +38 -0
- data/lib/protobuf/rpc/connectors/common.rb +156 -0
- data/lib/protobuf/rpc/connectors/em_client.rb +84 -0
- data/lib/protobuf/rpc/connectors/eventmachine.rb +87 -0
- data/lib/protobuf/rpc/connectors/socket.rb +73 -0
- data/lib/protobuf/rpc/connectors/zmq.rb +69 -0
- data/lib/protobuf/rpc/error/client_error.rb +31 -0
- data/lib/protobuf/rpc/error/server_error.rb +43 -0
- data/lib/protobuf/rpc/error.rb +25 -0
- data/lib/protobuf/rpc/rpc.pb.rb +118 -0
- data/lib/protobuf/rpc/server.rb +89 -0
- data/lib/protobuf/rpc/servers/evented/server.rb +41 -0
- data/lib/protobuf/rpc/servers/evented_runner.rb +21 -0
- data/lib/protobuf/rpc/servers/socket/server.rb +111 -0
- data/lib/protobuf/rpc/servers/socket/worker.rb +66 -0
- data/lib/protobuf/rpc/servers/socket_runner.rb +27 -0
- data/lib/protobuf/rpc/servers/zmq/broker.rb +87 -0
- data/lib/protobuf/rpc/servers/zmq/server.rb +50 -0
- data/lib/protobuf/rpc/servers/zmq/util.rb +27 -0
- data/lib/protobuf/rpc/servers/zmq/worker.rb +60 -0
- data/lib/protobuf/rpc/servers/zmq_runner.rb +25 -0
- data/lib/protobuf/rpc/service.rb +173 -0
- data/lib/protobuf/rpc/service_dispatcher.rb +130 -0
- data/lib/protobuf/rpc/service_filters.rb +267 -0
- data/lib/protobuf/rpc/stat.rb +83 -0
- data/lib/protobuf/socket.rb +22 -0
- data/lib/protobuf/version.rb +4 -0
- data/lib/protobuf/wire_type.rb +10 -0
- data/lib/protobuf/zmq.rb +21 -0
- data/lib/protobuf.rb +86 -0
- data/proto/rpc.pb.rb +48 -0
- data/proto/rpc.proto +73 -0
- data/protobuf.gemspec +44 -0
- data/spec/benchmark/tasks.rb +179 -0
- data/spec/functional/embedded_service_spec.rb +7 -0
- data/spec/functional/evented_server_spec.rb +64 -0
- data/spec/functional/socket_server_spec.rb +58 -0
- data/spec/functional/zmq_server_spec.rb +58 -0
- data/spec/lib/protobuf/cli_spec.rb +212 -0
- data/spec/lib/protobuf/enum_spec.rb +98 -0
- data/spec/lib/protobuf/enum_value_spec.rb +15 -0
- data/spec/lib/protobuf/logger_spec.rb +131 -0
- data/spec/lib/protobuf/message/encoder_spec.rb +19 -0
- data/spec/lib/protobuf/message_spec.rb +209 -0
- data/spec/lib/protobuf/rpc/client_spec.rb +158 -0
- data/spec/lib/protobuf/rpc/connector_spec.rb +32 -0
- data/spec/lib/protobuf/rpc/connectors/base_spec.rb +50 -0
- data/spec/lib/protobuf/rpc/connectors/common_spec.rb +128 -0
- data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +36 -0
- data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +22 -0
- data/spec/lib/protobuf/rpc/servers/evented_server_spec.rb +18 -0
- data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +43 -0
- data/spec/lib/protobuf/rpc/servers/zmq/broker_spec.rb +35 -0
- data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +41 -0
- data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +45 -0
- data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +44 -0
- data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +116 -0
- data/spec/lib/protobuf/rpc/service_filters_spec.rb +451 -0
- data/spec/lib/protobuf/rpc/service_spec.rb +165 -0
- data/spec/lib/protobuf_spec.rb +62 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/all.rb +6 -0
- data/spec/support/server.rb +101 -0
- data/spec/support/test/enum.pb.rb +34 -0
- data/spec/support/test/enum.proto +12 -0
- data/spec/support/test/resource.pb.rb +58 -0
- data/spec/support/test/resource.proto +31 -0
- data/spec/support/test/resource_service.rb +14 -0
- data/spec/support/test_app_file.rb +2 -0
- data/spec/support/tolerance_matcher.rb +40 -0
- data/test/data/data.bin +3 -0
- data/test/data/data_source.py +14 -0
- data/test/data/types.bin +0 -0
- data/test/data/types_source.py +22 -0
- data/test/data/unk.png +0 -0
- data/test/proto/addressbook.pb.rb +66 -0
- data/test/proto/addressbook.proto +33 -0
- data/test/proto/addressbook_base.pb.rb +58 -0
- data/test/proto/addressbook_base.proto +26 -0
- data/test/proto/addressbook_ext.pb.rb +20 -0
- data/test/proto/addressbook_ext.proto +6 -0
- data/test/proto/collision.pb.rb +17 -0
- data/test/proto/collision.proto +5 -0
- data/test/proto/ext_collision.pb.rb +24 -0
- data/test/proto/ext_collision.proto +8 -0
- data/test/proto/ext_range.pb.rb +22 -0
- data/test/proto/ext_range.proto +7 -0
- data/test/proto/float_default.proto +10 -0
- data/test/proto/lowercase.pb.rb +30 -0
- data/test/proto/lowercase.proto +9 -0
- data/test/proto/merge.pb.rb +39 -0
- data/test/proto/merge.proto +15 -0
- data/test/proto/nested.pb.rb +30 -0
- data/test/proto/nested.proto +9 -0
- data/test/proto/optional_field.pb.rb +35 -0
- data/test/proto/optional_field.proto +12 -0
- data/test/proto/packed.pb.rb +22 -0
- data/test/proto/packed.proto +6 -0
- data/test/proto/rpc.proto +6 -0
- data/test/proto/types.pb.rb +84 -0
- data/test/proto/types.proto +37 -0
- data/test/test_addressbook.rb +56 -0
- data/test/test_enum_value.rb +41 -0
- data/test/test_extension.rb +36 -0
- data/test/test_lowercase.rb +11 -0
- data/test/test_message.rb +128 -0
- data/test/test_optional_field.rb +103 -0
- data/test/test_packed_field.rb +40 -0
- data/test/test_parse.rb +15 -0
- data/test/test_repeated_types.rb +132 -0
- data/test/test_serialize.rb +61 -0
- data/test/test_standard_message.rb +96 -0
- data/test/test_types.rb +226 -0
- metadata +461 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'protobuf/rpc/server'
|
|
2
|
+
require 'protobuf/rpc/servers/zmq/util'
|
|
3
|
+
module Protobuf
|
|
4
|
+
module Rpc
|
|
5
|
+
module Zmq
|
|
6
|
+
|
|
7
|
+
class Worker
|
|
8
|
+
include ::Protobuf::Rpc::Server
|
|
9
|
+
include ::Protobuf::Rpc::Zmq::Util
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Constructor
|
|
13
|
+
#
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
host = options[:host]
|
|
16
|
+
port = options[:port]
|
|
17
|
+
|
|
18
|
+
@zmq_context = ::ZMQ::Context.new
|
|
19
|
+
@socket = @zmq_context.socket(::ZMQ::REP)
|
|
20
|
+
zmq_error_check(@socket.connect("tcp://#{resolve_ip(host)}:#{port}"))
|
|
21
|
+
|
|
22
|
+
@poller = ::ZMQ::Poller.new
|
|
23
|
+
@poller.register(@socket, ::ZMQ::POLLIN)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Instance Methods
|
|
28
|
+
#
|
|
29
|
+
def handle_request(socket)
|
|
30
|
+
@request_data = ''
|
|
31
|
+
zmq_error_check(socket.recv_string(@request_data))
|
|
32
|
+
log_debug { sign_message("handling request") } if !@request_data.nil?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run
|
|
36
|
+
while ::Protobuf::Rpc::Zmq::Server.running? do
|
|
37
|
+
# poll for 1_000 milliseconds then continue looping
|
|
38
|
+
# This lets us see whether we need to die
|
|
39
|
+
@poller.poll(1_000)
|
|
40
|
+
@poller.readables.each do |socket|
|
|
41
|
+
initialize_request!
|
|
42
|
+
handle_request(socket)
|
|
43
|
+
handle_client unless @request_data.nil?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
ensure
|
|
47
|
+
@socket.close
|
|
48
|
+
@zmq_context.terminate
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def send_data
|
|
52
|
+
response_data = @response.is_a?(::Protobuf::Message) ? @response.serialize_to_string : @response.to_s
|
|
53
|
+
@stats.response_size = response_data.size
|
|
54
|
+
zmq_error_check(@socket.send_string(response_data))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Protobuf
|
|
2
|
+
module Rpc
|
|
3
|
+
class ZmqRunner
|
|
4
|
+
|
|
5
|
+
def self.stop
|
|
6
|
+
Protobuf::Rpc::Zmq::Server.stop
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.run(server)
|
|
10
|
+
server_config = case
|
|
11
|
+
when server.is_a?(OpenStruct) then
|
|
12
|
+
server.marshal_dump
|
|
13
|
+
when server.respond_to?(:to_hash) then
|
|
14
|
+
server.to_hash
|
|
15
|
+
else
|
|
16
|
+
raise "Cannot parser Zmq Server - server options"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
yield if block_given?
|
|
20
|
+
Protobuf::Rpc::Zmq::Server.run(server_config)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require 'protobuf/logger'
|
|
2
|
+
require 'protobuf/rpc/client'
|
|
3
|
+
require 'protobuf/rpc/error'
|
|
4
|
+
require 'protobuf/rpc/service_filters'
|
|
5
|
+
|
|
6
|
+
module Protobuf
|
|
7
|
+
module Rpc
|
|
8
|
+
|
|
9
|
+
# Object to encapsulate the request/response types for a given service method
|
|
10
|
+
#
|
|
11
|
+
RpcMethod = Struct.new("RpcMethod", :method, :request_type, :response_type)
|
|
12
|
+
|
|
13
|
+
class Service
|
|
14
|
+
include Protobuf::Rpc::ServiceFilters
|
|
15
|
+
include Protobuf::Logger::LogMethods
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
attr_reader :response, :rpc
|
|
19
|
+
|
|
20
|
+
DEFAULT_HOST = '127.0.0.1'.freeze
|
|
21
|
+
DEFAULT_PORT = 9399
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Class Methods
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
# Create a new client for the given service.
|
|
28
|
+
# See Client#initialize and ClientConnection::DEFAULT_OPTIONS
|
|
29
|
+
# for all available options.
|
|
30
|
+
#
|
|
31
|
+
def self.client(options = {})
|
|
32
|
+
::Protobuf::Rpc::Client.new({ :service => self,
|
|
33
|
+
:host => host,
|
|
34
|
+
:port => port }.merge(options))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Allows service-level configuration of location.
|
|
38
|
+
# Useful for system-startup configuration of a service
|
|
39
|
+
# so that any Clients using the Service.client sugar
|
|
40
|
+
# will not have to configure the location each time.
|
|
41
|
+
#
|
|
42
|
+
def self.configure(config = {})
|
|
43
|
+
self.host = config[:host] if config.key?(:host)
|
|
44
|
+
self.port = config[:port] if config.key?(:port)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The host location of the service.
|
|
48
|
+
#
|
|
49
|
+
def self.host
|
|
50
|
+
@_host ||= DEFAULT_HOST
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The host location setter.
|
|
54
|
+
#
|
|
55
|
+
def self.host=(new_host)
|
|
56
|
+
@_host = new_host
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Shorthand call to configure, passing a string formatted as hostname:port
|
|
60
|
+
# e.g. 127.0.0.1:9933
|
|
61
|
+
# e.g. localhost:0
|
|
62
|
+
#
|
|
63
|
+
def self.located_at(location)
|
|
64
|
+
return if location.nil? || location.downcase.strip !~ /.+:\d+/
|
|
65
|
+
host, port = location.downcase.strip.split ':'
|
|
66
|
+
configure(:host => host, :port => port.to_i)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The port of the service on the destination server.
|
|
70
|
+
#
|
|
71
|
+
def self.port
|
|
72
|
+
@_port ||= DEFAULT_PORT
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The port location setter.
|
|
76
|
+
#
|
|
77
|
+
def self.port=(new_port)
|
|
78
|
+
@_port = new_port
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Define an rpc method with the given request and response types.
|
|
82
|
+
# This methods is only used by the generated service definitions
|
|
83
|
+
# and not useful for user code.
|
|
84
|
+
#
|
|
85
|
+
def self.rpc(method, request_type, response_type)
|
|
86
|
+
rpcs[method] = RpcMethod.new(method, request_type, response_type)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Hash containing the set of methods defined via `rpc`.
|
|
90
|
+
#
|
|
91
|
+
def self.rpcs
|
|
92
|
+
@_rpcs ||= {}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check if the given method name is a known rpc endpoint.
|
|
96
|
+
#
|
|
97
|
+
def self.rpc_method?(name)
|
|
98
|
+
rpcs.key?(name)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Instance Methods
|
|
104
|
+
#
|
|
105
|
+
|
|
106
|
+
# Initialize a service with the rpc endpoint name and the bytes
|
|
107
|
+
# for the request.
|
|
108
|
+
def initialize(rpc, request_bytes)
|
|
109
|
+
@rpc = rpc
|
|
110
|
+
@request_bytes = request_bytes
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Register a failure callback for use when rpc_failed is invoked.
|
|
114
|
+
#
|
|
115
|
+
def on_rpc_failed(callable)
|
|
116
|
+
@rpc_failed_callback ||= callable
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Response object for this rpc cycle. Not assignable.
|
|
120
|
+
#
|
|
121
|
+
def response
|
|
122
|
+
@_response ||= rpcs[@rpc].response_type.new
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Convenience method to get back to class method.
|
|
126
|
+
#
|
|
127
|
+
def rpc_method?(name)
|
|
128
|
+
self.class.rpc_method?(name)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Convenience method to get back to class rpcs hash.
|
|
132
|
+
#
|
|
133
|
+
def rpcs
|
|
134
|
+
self.class.rpcs
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Get a callable object that will be used by the dispatcher
|
|
138
|
+
# to invoke the specified rpc method. Facilitates callback dispatch.
|
|
139
|
+
# The returned lambda is expected to be called at a later time (which
|
|
140
|
+
# is why we wrap the method call).
|
|
141
|
+
#
|
|
142
|
+
def callable_rpc_method(method_name)
|
|
143
|
+
lambda { run_filters(method_name) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
# Request object for this rpc cycle. Not assignable.
|
|
149
|
+
#
|
|
150
|
+
def request
|
|
151
|
+
@_request ||= rpcs[@rpc].request_type.new.parse_from_string(@request_bytes)
|
|
152
|
+
rescue => e
|
|
153
|
+
raise BadRequestProto, "Unable to parse request: #{e.message}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Sugar to make an rpc method feel like a controller method.
|
|
157
|
+
# If this method is not called, the response will be the memoized
|
|
158
|
+
# object returned by the response reader.
|
|
159
|
+
#
|
|
160
|
+
def respond_with(candidate)
|
|
161
|
+
@_response = candidate
|
|
162
|
+
end
|
|
163
|
+
alias_method :return_from_whence_you_came, :respond_with
|
|
164
|
+
|
|
165
|
+
# Automatically fail a service method.
|
|
166
|
+
#
|
|
167
|
+
def rpc_failed(message)
|
|
168
|
+
@rpc_failed_callback.call(message)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require 'protobuf/logger'
|
|
2
|
+
|
|
3
|
+
module Protobuf
|
|
4
|
+
module Rpc
|
|
5
|
+
class ServiceDispatcher
|
|
6
|
+
|
|
7
|
+
include ::Protobuf::Logger::LogMethods
|
|
8
|
+
|
|
9
|
+
attr_accessor :service, :service_klass, :callable_method, :outer_request
|
|
10
|
+
attr_accessor :definition, :response, :error
|
|
11
|
+
|
|
12
|
+
def initialize(wrapper_request)
|
|
13
|
+
self.error = nil
|
|
14
|
+
self.outer_request = wrapper_request
|
|
15
|
+
|
|
16
|
+
init_service
|
|
17
|
+
init_method if service_klass.present?
|
|
18
|
+
register_rpc_failed if service.present?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Call the given service method. If we get to this point and an error
|
|
22
|
+
# has already occurred, do not invoke the method and simply respond.
|
|
23
|
+
#
|
|
24
|
+
def invoke!
|
|
25
|
+
unless error?
|
|
26
|
+
callable_method.call
|
|
27
|
+
validate_response
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return error || response
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# We're successful if the error is not populated.
|
|
34
|
+
#
|
|
35
|
+
def success?
|
|
36
|
+
error.nil?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# We're in error if the error is populated.
|
|
40
|
+
#
|
|
41
|
+
def error?
|
|
42
|
+
! success?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def assign_error(error_klass, message)
|
|
48
|
+
self.error = error_klass.new(message)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Prod the object to see if we can produce a proto object as a response
|
|
52
|
+
# candidate. Either way, return the candidate for validation.
|
|
53
|
+
def coerced_response
|
|
54
|
+
candidate = service.response
|
|
55
|
+
|
|
56
|
+
case
|
|
57
|
+
when candidate.is_a?(::Protobuf::Message) then
|
|
58
|
+
# no-op
|
|
59
|
+
when candidate.respond_to?(:to_proto) then
|
|
60
|
+
candidate = candidate.to_proto
|
|
61
|
+
when candidate.respond_to?(:to_proto_hash) then
|
|
62
|
+
candidate = definition.response_type.new(candidate.to_proto_hash)
|
|
63
|
+
when candidate.respond_to?(:to_hash) then
|
|
64
|
+
candidate = definition.response_type.new(candidate.to_hash)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
candidate
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get the method for the current request.
|
|
71
|
+
#
|
|
72
|
+
def init_method
|
|
73
|
+
method_name = outer_request.method_name.underscore.to_sym
|
|
74
|
+
if service_klass.rpc_method?(method_name)
|
|
75
|
+
self.service = service_klass.new(method_name, outer_request.request_proto)
|
|
76
|
+
self.callable_method = service.callable_rpc_method(method_name)
|
|
77
|
+
self.definition = service.rpcs[method_name]
|
|
78
|
+
else
|
|
79
|
+
assign_error(MethodNotFound, "#{service.class.name}##{method_name} is not a defined rpc method.")
|
|
80
|
+
end
|
|
81
|
+
rescue NameError => e
|
|
82
|
+
# FIXME I think this is no longer applicable since the method extract
|
|
83
|
+
# is now wrapped in a lambda (@see Service#callable_rpc_method).
|
|
84
|
+
log_exception(e)
|
|
85
|
+
assign_error(MethodNotFound, "#{service.class.name}##{method_name} is not implemented.")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Constantize the service for this request. Initialization of the service
|
|
89
|
+
# happens when we verify that the method is callable for this service.
|
|
90
|
+
#
|
|
91
|
+
def init_service
|
|
92
|
+
self.service_klass = outer_request.service_name.constantize
|
|
93
|
+
rescue NameError => e
|
|
94
|
+
log_exception(e)
|
|
95
|
+
assign_error(ServiceNotFound, "Service class #{outer_request.service_name} is not defined.")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Make sure we get rpc errors back.
|
|
99
|
+
#
|
|
100
|
+
def register_rpc_failed
|
|
101
|
+
service.on_rpc_failed(method(:rpc_failed_callback))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Receive the failure message from the service. This method is registered
|
|
105
|
+
# as the callable to the service when an `rpc_failed` call is invoked.
|
|
106
|
+
#
|
|
107
|
+
def rpc_failed_callback(message)
|
|
108
|
+
assign_error(RpcFailed, (message.respond_to?(:message) ? message.message : message))
|
|
109
|
+
log_error { sign_message("RPC Failed: #{error.message}") }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Ensure that the response candidate we've been given is of the type
|
|
113
|
+
# we expect so that deserialization on the client side works.
|
|
114
|
+
#
|
|
115
|
+
def validate_response
|
|
116
|
+
candidate = coerced_response
|
|
117
|
+
expected = definition.response_type
|
|
118
|
+
actual = candidate.class
|
|
119
|
+
|
|
120
|
+
if expected == actual
|
|
121
|
+
self.response = candidate
|
|
122
|
+
else
|
|
123
|
+
assign_error(BadResponseProto, "Response proto changed from #{expected.name} to #{actual.name}")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
module Protobuf
|
|
2
|
+
module Rpc
|
|
3
|
+
module ServiceFilters
|
|
4
|
+
|
|
5
|
+
def self.included(other)
|
|
6
|
+
other.class_eval do
|
|
7
|
+
extend Protobuf::Rpc::ServiceFilters::ClassMethods
|
|
8
|
+
include Protobuf::Rpc::ServiceFilters::InstanceMethods
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module ClassMethods
|
|
13
|
+
|
|
14
|
+
[:after, :around, :before].each do |type|
|
|
15
|
+
# Setter DSL method for given filter types.
|
|
16
|
+
#
|
|
17
|
+
define_method "#{type}_filter" do |*args|
|
|
18
|
+
set_filters(type, *args)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Filters hash keyed based on filter type (e.g. :before, :after, :around),
|
|
23
|
+
# whose values are Sets.
|
|
24
|
+
#
|
|
25
|
+
def filters
|
|
26
|
+
@filters ||= Hash.new { |h,k| h[k] = [] }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Filters hash keyed based on filter type (e.g. :before, :after, :around),
|
|
30
|
+
# whose values are Sets.
|
|
31
|
+
#
|
|
32
|
+
def rescue_filters
|
|
33
|
+
@rescue_filters ||= {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def rescue_from(*ex_klasses, &block)
|
|
37
|
+
options = ex_klasses.last.is_a?(Hash) ? ex_klasses.pop : {}
|
|
38
|
+
callable = options.delete(:with) { block }
|
|
39
|
+
raise ArgumentError, 'Option :with missing from rescue_from options' if callable.nil?
|
|
40
|
+
ex_klasses.each { |ex_klass| rescue_filters[ex_klass] = callable }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def define_filter(type, filter, options = {})
|
|
46
|
+
return if filter_defined?(type, filter)
|
|
47
|
+
filters[type] << options.merge({ :callable => filter })
|
|
48
|
+
remember_filter(type, filter)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def defined_filters
|
|
52
|
+
@defined_filters ||= Hash.new { |h,k| h[k] = Set.new }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Check to see if the filter has been defined.
|
|
56
|
+
#
|
|
57
|
+
def filter_defined?(type, filter)
|
|
58
|
+
defined_filters[type].include?(filter)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Remember that we stored the filter.
|
|
62
|
+
#
|
|
63
|
+
def remember_filter(type, filter)
|
|
64
|
+
defined_filters[type] << filter
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Takes a list of actually (or potentially) callable objects.
|
|
68
|
+
# TODO add support for if/unless
|
|
69
|
+
# TODO add support for only/except sub-filters
|
|
70
|
+
#
|
|
71
|
+
def set_filters(type, *args)
|
|
72
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
73
|
+
args.each do |filter|
|
|
74
|
+
define_filter(type, filter, options)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module InstanceMethods
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Get back to class filters.
|
|
85
|
+
#
|
|
86
|
+
def filters
|
|
87
|
+
self.class.filters
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Predicate which uses the filter options to determine if the filter
|
|
91
|
+
# should be called. Specifically checks the :if, :unless, :only, and :except
|
|
92
|
+
# options for every filter. Each option check is expected to return false
|
|
93
|
+
# if the filter should not be invoked, true if invocation should occur.
|
|
94
|
+
#
|
|
95
|
+
def invoke_filter?(rpc_method, filter)
|
|
96
|
+
return invoke_via_only?(rpc_method, filter) \
|
|
97
|
+
&& invoke_via_except?(rpc_method, filter) \
|
|
98
|
+
&& invoke_via_if?(rpc_method, filter) \
|
|
99
|
+
&& invoke_via_unless?(rpc_method, filter)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# If the target rpc endpoint method is listed under an :except option,
|
|
103
|
+
# return false to indicate that the filter should not be invoked. Any
|
|
104
|
+
# other target rpc endpoint methods not listed should be invoked.
|
|
105
|
+
# This option is the opposite of :only.
|
|
106
|
+
#
|
|
107
|
+
# Value should be a symbol/string or an array of symbols/strings.
|
|
108
|
+
#
|
|
109
|
+
def invoke_via_except?(rpc_method, filter)
|
|
110
|
+
except = [ filter.fetch(:except) { [] } ].flatten
|
|
111
|
+
return except.empty? || ! except.include?(rpc_method)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Invoke the given :if callable (if any) and return its return value.
|
|
115
|
+
# Used by `invoke_filter?` which expects a true/false
|
|
116
|
+
# return value to determine if we should invoke the target filter.
|
|
117
|
+
#
|
|
118
|
+
# Value can either be a symbol/string indicating an instance method to call
|
|
119
|
+
# or an object that responds to `call`.
|
|
120
|
+
#
|
|
121
|
+
def invoke_via_if?(rpc_method, filter)
|
|
122
|
+
if_check = filter.fetch(:if) { lambda { |service| return true } }
|
|
123
|
+
do_invoke = case
|
|
124
|
+
when if_check.nil? then
|
|
125
|
+
true
|
|
126
|
+
else
|
|
127
|
+
call_or_send(if_check)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
return do_invoke
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# If the target rpc endpoint method is listed in the :only option,
|
|
134
|
+
# it should be invoked. Any target rpc endpoint methods not listed in this
|
|
135
|
+
# option should not be invoked. This option is the opposite of :except.
|
|
136
|
+
#
|
|
137
|
+
# Value should be a symbol/string or an array of symbols/strings.
|
|
138
|
+
#
|
|
139
|
+
def invoke_via_only?(rpc_method, filter)
|
|
140
|
+
only = [ filter.fetch(:only) { [] } ].flatten
|
|
141
|
+
return only.empty? || only.include?(rpc_method)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Invoke the given :unless callable (if any) and return the opposite
|
|
145
|
+
# of it's return value. Used by `invoke_filter?` which expects a true/false
|
|
146
|
+
# return value to determine if we should invoke the target filter.
|
|
147
|
+
#
|
|
148
|
+
# Value can either be a symbol/string indicating an instance method to call
|
|
149
|
+
# or an object that responds to `call`.
|
|
150
|
+
#
|
|
151
|
+
def invoke_via_unless?(rpc_method, filter)
|
|
152
|
+
unless_check = filter.fetch(:unless) { lambda { |service| return false } }
|
|
153
|
+
skip_invoke = case
|
|
154
|
+
when unless_check.nil? then
|
|
155
|
+
false
|
|
156
|
+
else
|
|
157
|
+
call_or_send(unless_check)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
return ! skip_invoke
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def rescue_filters
|
|
164
|
+
self.class.rescue_filters
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Loop over the unwrapped filters and invoke them. An unwrapped filter
|
|
168
|
+
# is either a before or after filter, not an around filter.
|
|
169
|
+
#
|
|
170
|
+
def run_unwrapped_filters(unwrapped_filters, rpc_method, stop_on_false_return = false)
|
|
171
|
+
unwrapped_filters.each do |filter|
|
|
172
|
+
if invoke_filter?(rpc_method, filter)
|
|
173
|
+
return_value = call_or_send(filter[:callable])
|
|
174
|
+
return false if stop_on_false_return && return_value == false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
return true
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Reverse build a chain of around filters. To implement an around chain,
|
|
182
|
+
# simply build a method that yields control when it expects the underlying
|
|
183
|
+
# method to be invoked. If the endpoint should not be run (due to some
|
|
184
|
+
# condition), simply do not yield.
|
|
185
|
+
#
|
|
186
|
+
# Around filters are invoked in the order they are defined, outer to inner,
|
|
187
|
+
# with the inner-most method called being the actual rpc endpoint.
|
|
188
|
+
#
|
|
189
|
+
# Let's say you have a class defined with the following filters:
|
|
190
|
+
#
|
|
191
|
+
# class MyService
|
|
192
|
+
# around_filter :filter1, :filter2, :filter3
|
|
193
|
+
#
|
|
194
|
+
# def my_endpoint
|
|
195
|
+
# # do stuff
|
|
196
|
+
# end
|
|
197
|
+
# end
|
|
198
|
+
#
|
|
199
|
+
# When the my_endpoint method is invoked using Service#callable_rpc_method,
|
|
200
|
+
# It is similar to this call chain:
|
|
201
|
+
#
|
|
202
|
+
# filter1 do
|
|
203
|
+
# filter2 do
|
|
204
|
+
# filter3 do
|
|
205
|
+
# my_endpoint
|
|
206
|
+
# end
|
|
207
|
+
# end
|
|
208
|
+
# end
|
|
209
|
+
#
|
|
210
|
+
def run_around_filters(rpc_method)
|
|
211
|
+
final = lambda { __send__(rpc_method) }
|
|
212
|
+
filters[:around].reverse.inject(final) { |previous, filter|
|
|
213
|
+
if invoke_filter?(rpc_method, filter)
|
|
214
|
+
lambda { call_or_send(filter[:callable], &previous) }
|
|
215
|
+
else
|
|
216
|
+
previous
|
|
217
|
+
end
|
|
218
|
+
}.call
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# Entry method to call each filter type in the appropriate order. This should
|
|
223
|
+
# be used instead of the other run methods directly.
|
|
224
|
+
#
|
|
225
|
+
def run_filters(rpc_method)
|
|
226
|
+
run_rescue_filters do
|
|
227
|
+
continue = run_unwrapped_filters(filters[:before], rpc_method, true)
|
|
228
|
+
if continue
|
|
229
|
+
run_around_filters(rpc_method)
|
|
230
|
+
run_unwrapped_filters(filters[:after], rpc_method)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def run_rescue_filters
|
|
236
|
+
if rescue_filters.keys.empty?
|
|
237
|
+
yield
|
|
238
|
+
else
|
|
239
|
+
begin
|
|
240
|
+
yield
|
|
241
|
+
rescue *rescue_filters.keys => ex
|
|
242
|
+
call_or_send(rescue_filters[ex.class], ex)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Call the object if it is callable, otherwise invoke the method using
|
|
248
|
+
# __send__ assuming that we respond_to it. Return the call's return value.
|
|
249
|
+
#
|
|
250
|
+
def call_or_send(callable, *args, &block)
|
|
251
|
+
return_value = case
|
|
252
|
+
when callable.respond_to?(:call) then
|
|
253
|
+
callable.call(self, *args, &block)
|
|
254
|
+
when respond_to?(callable, true) then
|
|
255
|
+
__send__(callable, *args, &block)
|
|
256
|
+
else
|
|
257
|
+
raise "Object #{callable} is not callable"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
return return_value
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|