msgpack_rpc_client 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +65 -0
- data/Rakefile +6 -0
- data/circle.yml +14 -0
- data/examples/benchmark.rb +25 -0
- data/examples/client.rb +15 -0
- data/examples/server.go +82 -0
- data/lib/msgpack_rpc_client.rb +142 -0
- data/lib/msgpack_rpc_client/version.rb +3 -0
- data/msgpack_rpc_client.gemspec +26 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7feba3f261abbfdb61f1f4f320d4f981fdc4b97f
|
4
|
+
data.tar.gz: 6beec26d066279f6c098ac88df223f80610e6a04
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f84815a076edb16128aba33a22aff7bb3c18af4d2f247656eec5e73cafad0c405191f424b28741b6942c3923d9246571a8720db901f565420110447fc45b5e7c
|
7
|
+
data.tar.gz: 5dbf537b5ff8920da50310857ecd88d5238ca4ed182e1d09141b261725400ce16220f89770f05424ff6640aad9e9062a4188a2f181b41604d5d4d5991baa550e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 BrightBytes
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# MsgpackRpcClient [](https://circleci.com/gh/brightbytes/msgpack_rpc_client)
|
2
|
+
|
3
|
+
## A lean Ruby client for the MessagePack-RPC protocol
|
4
|
+
|
5
|
+
Use this gem to achieve reliable, fault-tolerant RPC with your microservices.
|
6
|
+
|
7
|
+
### Differences from the "official" implementation (the msgpack-rpc gem)
|
8
|
+
|
9
|
+
The official implementation:
|
10
|
+
|
11
|
+
* depends on the cool.io gem, labeled "retired" [on the homepage](https://coolio.github.io).
|
12
|
+
* does not re-establish connections.
|
13
|
+
* designed to be asynchronous
|
14
|
+
* displayed instability under high load in production
|
15
|
+
|
16
|
+
This implementation:
|
17
|
+
|
18
|
+
* has no dependencies
|
19
|
+
* is under 200 lines in one class
|
20
|
+
* automatically re-establishes connections in the case of inevitable network errors, service restarts, deploys, and so on.
|
21
|
+
* supports SSL
|
22
|
+
* threadsafe
|
23
|
+
* reliable
|
24
|
+
* high load tested
|
25
|
+
* used in production, with up to a 1000 requests per second in a single frontend request.
|
26
|
+
* supports JRuby and MRI from version 1.9.3 - for those who have a huge legacy app that you're dying to factor into microservices!
|
27
|
+
|
28
|
+
However, this implementation **does NOT support asynchronous calls** - if you require this feature, it is not for you. However, from my experience, almost no Ruby applications require asynchronous communication.
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
Add this line to your application's Gemfile:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem 'msgpack_rpc_client'
|
36
|
+
```
|
37
|
+
|
38
|
+
And then execute:
|
39
|
+
|
40
|
+
$ bundle
|
41
|
+
|
42
|
+
Or install it yourself as:
|
43
|
+
|
44
|
+
$ gem install msgpack_rpc_client
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
require 'msgpack_rpc_client'
|
50
|
+
|
51
|
+
client = MsgpackRpcClient.new('127.0.0.1', 12345)
|
52
|
+
response = client.call('HelloWorld', name: 'Ruby')
|
53
|
+
```
|
54
|
+
|
55
|
+
See the `examples` directory for a complete server-client example.
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/brightbytes/msgpack_rpc_client.
|
60
|
+
|
61
|
+
|
62
|
+
## License
|
63
|
+
|
64
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
65
|
+
|
data/Rakefile
ADDED
data/circle.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
machine:
|
2
|
+
environment:
|
3
|
+
RUBY_VERSIONS: 1.9.3,2.0.0,2.1.10,2.2.5,2.3.1,jruby-1.7.25,jruby-9.1.2.0
|
4
|
+
|
5
|
+
dependencies:
|
6
|
+
override:
|
7
|
+
- rvm get head
|
8
|
+
- rvm install $RUBY_VERSIONS
|
9
|
+
- rvm $RUBY_VERSIONS --verbose do gem install bundler -v 1.10.6
|
10
|
+
- rvm $RUBY_VERSIONS --verbose do bundle install
|
11
|
+
|
12
|
+
test:
|
13
|
+
override:
|
14
|
+
- rvm $RUBY_VERSIONS --verbose do bundle exec rspec spec
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Run server.go in a parallel process first
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'msgpack_rpc_client'
|
5
|
+
require 'benchmark/ips'
|
6
|
+
|
7
|
+
msgpack_client = MsgpackRpcClient.new('127.0.0.1', 12345)
|
8
|
+
http_uri = URI('http://localhost:12344/hello_world')
|
9
|
+
|
10
|
+
Benchmark.ips do |x|
|
11
|
+
x.report 'http/json' do
|
12
|
+
req = Net::HTTP::Post.new(http_uri, 'Content-Type' => 'application/json')
|
13
|
+
req.body = { name: 'Ruby' }.to_json
|
14
|
+
res = Net::HTTP.start(http_uri.hostname, http_uri.port) do |http|
|
15
|
+
http.request(req)
|
16
|
+
end
|
17
|
+
JSON.parse(res.body, symbolize_keys: true)
|
18
|
+
end
|
19
|
+
|
20
|
+
x.report 'msgpack-rpc' do
|
21
|
+
msgpack_client.call('Greeter.HelloWorld', name: 'Ruby')
|
22
|
+
end
|
23
|
+
|
24
|
+
x.compare!
|
25
|
+
end
|
data/examples/client.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Run server.go in a parallel process first
|
2
|
+
|
3
|
+
require 'msgpack_rpc_client'
|
4
|
+
|
5
|
+
client = MsgpackRpcClient.new('127.0.0.1', 12345)
|
6
|
+
|
7
|
+
response = client.call('Greeter.HelloWorld', name: 'Ruby')
|
8
|
+
# {:greeting=>"Hello, Ruby"}
|
9
|
+
puts response[:greeting]
|
10
|
+
|
11
|
+
puts client.call('Greeter.HelloWorld', {})
|
12
|
+
# {:error=>"Name is required"}
|
13
|
+
|
14
|
+
puts client.call('Greeter.BogusMethod', {})
|
15
|
+
# MsgpackRpcError exception: Server responded with error: rpc: can't find method Greeter.BogusMethod
|
data/examples/server.go
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
// go get github.com/ugorji/go/codec
|
2
|
+
// go run server.go
|
3
|
+
package main
|
4
|
+
|
5
|
+
import (
|
6
|
+
"fmt"
|
7
|
+
"log"
|
8
|
+
"net"
|
9
|
+
"net/http"
|
10
|
+
"net/rpc"
|
11
|
+
|
12
|
+
"github.com/ugorji/go/codec"
|
13
|
+
)
|
14
|
+
|
15
|
+
// The request structure type
|
16
|
+
// JSON tags are reused by go-codec for all encoding formats
|
17
|
+
type HelloWorldRequest struct {
|
18
|
+
Name string `json:"name"`
|
19
|
+
}
|
20
|
+
|
21
|
+
// The response structure type
|
22
|
+
type HelloWorldResponse struct {
|
23
|
+
Greeting string `json:"greeting,omitempty"`
|
24
|
+
Error string `json:"error,omitempty"`
|
25
|
+
}
|
26
|
+
|
27
|
+
// Our empty placeholder server type
|
28
|
+
type HelloWorldServer struct{}
|
29
|
+
|
30
|
+
// The request handler. It follows the usual net/rpc requirements.
|
31
|
+
// Note that the return `error` is akin to a 500 Internal Server Error,
|
32
|
+
// and shouldn't be used for errors directed at the consumer.
|
33
|
+
func (s *HelloWorldServer) HelloWorld(request *HelloWorldRequest, response *HelloWorldResponse) error {
|
34
|
+
if request.Name == "" {
|
35
|
+
response.Error = "Name is required"
|
36
|
+
return nil
|
37
|
+
}
|
38
|
+
response.Greeting = fmt.Sprintf("Hello, %s", request.Name)
|
39
|
+
return nil
|
40
|
+
}
|
41
|
+
|
42
|
+
func main() {
|
43
|
+
// Register the service with RPC
|
44
|
+
var s HelloWorldServer
|
45
|
+
rpc.RegisterName("Greeter", &s)
|
46
|
+
|
47
|
+
go func() {
|
48
|
+
// Using custom codecs with net/rpc is verbose, but isolated to this piece of code
|
49
|
+
var mh codec.MsgpackHandle
|
50
|
+
listener, err := net.Listen("tcp", "127.0.0.1:12345")
|
51
|
+
if err != nil {
|
52
|
+
log.Fatalf("failed to listen on msgpack-rpc address: %v", err)
|
53
|
+
}
|
54
|
+
log.Print("Msgpack listening on port 12345, Ctrl+C to abort")
|
55
|
+
for {
|
56
|
+
conn, err := listener.Accept()
|
57
|
+
if err != nil {
|
58
|
+
log.Fatalf("failed to accept msgpack-rpc connection: %v", err)
|
59
|
+
}
|
60
|
+
rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, &mh)
|
61
|
+
go func() {
|
62
|
+
rpc.ServeCodec(rpcCodec)
|
63
|
+
}()
|
64
|
+
}
|
65
|
+
}()
|
66
|
+
|
67
|
+
// HTTP/JSON API for the benchmarking example
|
68
|
+
h := new(codec.JsonHandle)
|
69
|
+
http.HandleFunc("/hello_world", func(w http.ResponseWriter, r *http.Request) {
|
70
|
+
var request HelloWorldRequest
|
71
|
+
var response HelloWorldResponse
|
72
|
+
|
73
|
+
dec := codec.NewDecoder(r.Body, h)
|
74
|
+
dec.Decode(&request)
|
75
|
+
s.HelloWorld(&request, &response)
|
76
|
+
w.WriteHeader(http.StatusOK)
|
77
|
+
enc := codec.NewEncoder(w, h)
|
78
|
+
enc.Encode(response)
|
79
|
+
})
|
80
|
+
log.Print("HTTP listening on port 12344, Ctrl+C to abort")
|
81
|
+
http.ListenAndServe("127.0.0.1:12344", nil)
|
82
|
+
}
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "msgpack_rpc_client/version"
|
2
|
+
require 'msgpack'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
# MessagePack-RPC client
|
6
|
+
class MsgpackRpcClient
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
|
9
|
+
DEFAULT_MAX_RETRIES = 10
|
10
|
+
DEFAULT_MAX_CONNECT_RETRIES = 5
|
11
|
+
DEFAULT_CONNECT_RETRY_WAIT = 0.1 # seconds
|
12
|
+
MAX_MSGID = 1_000_000_000
|
13
|
+
|
14
|
+
attr_reader :host, :port, :use_ssl, :max_retries, :max_connect_retries, :connect_retry_wait
|
15
|
+
|
16
|
+
# Initialize client and establish connection to server.
|
17
|
+
#
|
18
|
+
# (While it may seem beneficial to not connect in the constructor and wait for
|
19
|
+
# the first RPC call, I believe it's better to fail early.
|
20
|
+
#
|
21
|
+
# * host, port, use_ssl - configure your connection
|
22
|
+
# * logger - logger (default nil, set to Rails.logger in a Rails app)
|
23
|
+
#
|
24
|
+
# Parameters that are better left alone:
|
25
|
+
# * max_retries - number of times to retry sending a request
|
26
|
+
# * max_connect_retries - number of times to retry connecting to the server
|
27
|
+
# * connect_retry_wait - wait between connection retry attempts
|
28
|
+
#
|
29
|
+
# TODO: once we are long past the <2.0.0 legacy, replace with named args
|
30
|
+
def initialize(options={})
|
31
|
+
@host = options.fetch(:host)
|
32
|
+
@port = options.fetch(:port)
|
33
|
+
@use_ssl = options.fetch(:use_ssl, false)
|
34
|
+
@logger = options.fetch(:logger, nil)
|
35
|
+
@max_retries = options.fetch(:max_retries, DEFAULT_MAX_RETRIES)
|
36
|
+
@max_connect_retries = options.fetch(:max_connect_retries, DEFAULT_MAX_CONNECT_RETRIES)
|
37
|
+
@connect_retry_wait = options.fetch(:connect_retry_wait, DEFAULT_CONNECT_RETRY_WAIT)
|
38
|
+
@msgid = 1
|
39
|
+
@call_mutex = Mutex.new
|
40
|
+
init_socket
|
41
|
+
end
|
42
|
+
|
43
|
+
# Call an RPC method. Will reconnect if the server is down. Threadsafe.
|
44
|
+
#
|
45
|
+
# * Params is anything serializable with Messagepack.
|
46
|
+
# * Hashes in response will be deserialized with symbolized keys
|
47
|
+
def call(method_name, *params)
|
48
|
+
request = nil
|
49
|
+
response = nil
|
50
|
+
|
51
|
+
@call_mutex.synchronize do
|
52
|
+
request = [0, @msgid, method_name, params]
|
53
|
+
@msgid = (@msgid % MAX_MSGID) + 1
|
54
|
+
response = make_request_with_retries(request)
|
55
|
+
end
|
56
|
+
|
57
|
+
if response[0] != 1
|
58
|
+
raise MsgpackRpcClient::Error, 'Response does not bear the proper type flag - something is very wrong'
|
59
|
+
end
|
60
|
+
if response[1] != request[1]
|
61
|
+
raise MsgpackRpcClient::Error, 'Response message id does not match request message id - something is very wrong'
|
62
|
+
end
|
63
|
+
if response[2] != nil
|
64
|
+
raise MsgpackRpcClient::Error, "Server responded with error: #{response[2]}"
|
65
|
+
end
|
66
|
+
|
67
|
+
response[3]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Handles socket connectivity details of sending and receiving. Retries on error.
|
73
|
+
def make_request_with_retries(request)
|
74
|
+
retry_count = 0
|
75
|
+
begin
|
76
|
+
@packer.write(request).flush
|
77
|
+
@unpacker.read
|
78
|
+
rescue EOFError, IOError, Errno::EPIPE, Errno::ETIMEDOUT, Errno::ECONNRESET
|
79
|
+
@logger.error("[MSGPACK-RPC] Msgpack-RPC socket interrupted. Re-establishing commmunications.") if @logger
|
80
|
+
retry_count += 1
|
81
|
+
if retry_count == max_retries
|
82
|
+
raise MsgpackRpcClient::Error, "Failed to re-establish communications with server"
|
83
|
+
else
|
84
|
+
init_socket
|
85
|
+
retry
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Opens a socket according to provided configuration. Retries on error.
|
91
|
+
def init_socket
|
92
|
+
@socket.close if @socket
|
93
|
+
retry_count = 0
|
94
|
+
begin
|
95
|
+
@logger.info("[MSGPACK-RPC] Connecting to Msgpack-RPC server...") if @logger
|
96
|
+
@socket = TCPSocket.new(host, port)
|
97
|
+
configure_socket_keepalive
|
98
|
+
init_ssl if use_ssl
|
99
|
+
rescue Errno::ECONNREFUSED
|
100
|
+
@logger.error("[MSGPACK-RPC] Connection refused") if @logger
|
101
|
+
retry_count += 1
|
102
|
+
if retry_count == max_connect_retries
|
103
|
+
raise MsgpackRpcClient::Error, "Could not connect to MsgPack-RPC server"
|
104
|
+
else
|
105
|
+
sleep(connect_retry_wait)
|
106
|
+
# might have a chance with a different instance
|
107
|
+
retry
|
108
|
+
end
|
109
|
+
end
|
110
|
+
# Attach streaming packer/unpacker to the socket
|
111
|
+
@packer = MessagePack::Packer.new(@socket)
|
112
|
+
@unpacker = MessagePack::Unpacker.new(@socket, symbolize_keys: true)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Configure the TCP stack to send keepalive messages, as we want a long-living
|
116
|
+
# connection.
|
117
|
+
#
|
118
|
+
# (Not 100% reliable)
|
119
|
+
def configure_socket_keepalive
|
120
|
+
@socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
121
|
+
if defined?(Socket::TCP_KEEPINTVL) # Not available on JRuby
|
122
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, 10)
|
123
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, 5)
|
124
|
+
end
|
125
|
+
if defined?(Socket::TCP_KEEPIDLE) # Not available on BSD / OSX
|
126
|
+
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, 50)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Open an SSL socket on top of the TCP socket.
|
131
|
+
#
|
132
|
+
# VERIFY_PEER is mandatory; if you have problems with it, just don't use SSL -
|
133
|
+
# without verification it gives no security benefits but only increases cpu load.
|
134
|
+
def init_ssl
|
135
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
136
|
+
ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
|
137
|
+
# We are overwriting the TCP socket with the SSL socket here.
|
138
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@socket, ctx)
|
139
|
+
@socket.sync_close = true
|
140
|
+
@socket.connect
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'msgpack_rpc_client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "msgpack_rpc_client"
|
8
|
+
spec.version = MsgpackRpcClient::VERSION
|
9
|
+
spec.authors = ["BrightBytes", "Leonid Shevtsov"]
|
10
|
+
spec.email = ["leonid@shevtsov.me"]
|
11
|
+
|
12
|
+
spec.summary = %q{A lean Ruby client for the MessagePack-RPC protocol.}
|
13
|
+
spec.homepage = "https://github.com/brightbytes/msgpack_rpc_client"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "msgpack"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: msgpack_rpc_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- BrightBytes
|
8
|
+
- Leonid Shevtsov
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-08-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: msgpack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.10'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.10'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description:
|
71
|
+
email:
|
72
|
+
- leonid@shevtsov.me
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- circle.yml
|
84
|
+
- examples/benchmark.rb
|
85
|
+
- examples/client.rb
|
86
|
+
- examples/server.go
|
87
|
+
- lib/msgpack_rpc_client.rb
|
88
|
+
- lib/msgpack_rpc_client/version.rb
|
89
|
+
- msgpack_rpc_client.gemspec
|
90
|
+
homepage: https://github.com/brightbytes/msgpack_rpc_client
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.5.2
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: A lean Ruby client for the MessagePack-RPC protocol.
|
114
|
+
test_files: []
|
115
|
+
has_rdoc:
|