grpc 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grpc might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +0 -0
- data/Rakefile +27 -26
- data/bin/interop/README.md +0 -0
- data/bin/interop/interop_client.rb +1 -1
- data/bin/interop/interop_server.rb +2 -3
- data/bin/math_server.rb +2 -3
- data/bin/noproto_server.rb +2 -3
- data/ext/grpc/extconf.rb +16 -1
- data/ext/grpc/rb_grpc.c +3 -3
- data/ext/grpc/rb_server.c +14 -27
- data/grpc.gemspec +0 -4
- data/lib/grpc/generic/active_call.rb +2 -2
- data/lib/grpc/generic/client_stub.rb +26 -35
- data/lib/grpc/generic/rpc_desc.rb +1 -0
- data/lib/grpc/generic/rpc_server.rb +1 -8
- data/lib/grpc/generic/service.rb +11 -10
- data/lib/grpc/version.rb +1 -1
- data/spec/client_server_spec.rb +8 -10
- data/spec/generic/active_call_spec.rb +4 -4
- data/spec/generic/client_stub_spec.rb +6 -12
- data/spec/generic/rpc_desc_spec.rb +4 -0
- data/spec/generic/rpc_server_spec.rb +74 -18
- data/spec/server_spec.rb +4 -11
- data/spec/testdata/README +0 -0
- metadata +2 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea942edb7ba32af1cbb7114b26097b3c08c70b90
|
4
|
+
data.tar.gz: 434745623715052a46b4bcbe16166cc8dcc9d490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7eccf1a3db3deadf530258d2c14dd541886dbe6f4ef7ab29e46919cac883f85b5ce223b23665ab2ebd576d0b4316c28e5991303176b81aa9090a2b03f7e6347a
|
7
|
+
data.tar.gz: 69d7a8a23039907725c62aed351208b2d45d3fc1b14b07971d3ff26c53854f0ee6417481547ed014dd07010397f4c4a46a90cd1b9880d87ada51edfaff0713f2
|
data/README.md
CHANGED
File without changes
|
data/Rakefile
CHANGED
@@ -2,14 +2,17 @@
|
|
2
2
|
require 'rake/extensiontask'
|
3
3
|
require 'rspec/core/rake_task'
|
4
4
|
require 'rubocop/rake_task'
|
5
|
+
require 'bundler/gem_tasks'
|
5
6
|
|
6
|
-
|
7
|
+
# Add rubocop style checking tasks
|
7
8
|
RuboCop::RakeTask.new
|
8
9
|
|
10
|
+
# Add the extension compiler task
|
9
11
|
Rake::ExtensionTask.new 'grpc' do |ext|
|
10
12
|
ext.lib_dir = File.join('lib', 'grpc')
|
11
13
|
end
|
12
14
|
|
15
|
+
# Define the test suites
|
13
16
|
SPEC_SUITES = [
|
14
17
|
{ id: :wrapper, title: 'wrapper layer', files: %w(spec/*.rb) },
|
15
18
|
{ id: :idiomatic, title: 'idiomatic layer', dir: %w(spec/generic),
|
@@ -19,36 +22,34 @@ SPEC_SUITES = [
|
|
19
22
|
{ id: :server, title: 'rpc server thread tests', dir: %w(spec/generic),
|
20
23
|
tag: 'server' }
|
21
24
|
]
|
25
|
+
namespace :suite do
|
26
|
+
SPEC_SUITES.each do |suite|
|
27
|
+
desc "Run all specs in the #{suite[:title]} spec suite"
|
28
|
+
RSpec::Core::RakeTask.new(suite[:id]) do |t|
|
29
|
+
spec_files = []
|
30
|
+
suite[:files].each { |f| spec_files += Dir[f] } if suite[:files]
|
31
|
+
|
32
|
+
if suite[:dir]
|
33
|
+
suite[:dir].each { |f| spec_files += Dir["#{f}/**/*_spec.rb"] }
|
34
|
+
end
|
35
|
+
helper = 'spec/spec_helper.rb'
|
36
|
+
spec_files << helper unless spec_files.include?(helper)
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
desc "Run all specs in #{suite[:title]} spec suite"
|
28
|
-
RSpec::Core::RakeTask.new(suite[:id]) do |t|
|
29
|
-
spec_files = []
|
30
|
-
suite[:files].each { |f| spec_files += Dir[f] } if suite[:files]
|
31
|
-
|
32
|
-
if suite[:dirs]
|
33
|
-
suite[:dirs].each { |f| spec_files += Dir["#{f}/**/*_spec.rb"] }
|
34
|
-
end
|
35
|
-
|
36
|
-
t.pattern = spec_files
|
37
|
-
t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag]
|
38
|
-
if suite[:tags]
|
39
|
-
t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ')
|
40
|
-
end
|
38
|
+
t.pattern = spec_files
|
39
|
+
t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag]
|
40
|
+
if suite[:tags]
|
41
|
+
t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ')
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
46
|
-
|
47
|
-
task :
|
47
|
+
# Define dependencies between the suites.
|
48
|
+
task 'suite:wrapper' => [:compile, :rubocop]
|
49
|
+
task 'suite:idiomatic' => 'suite:wrapper'
|
50
|
+
task 'suite:bidi' => 'suite:wrapper'
|
51
|
+
task 'suite:server' => 'suite:wrapper'
|
48
52
|
|
53
|
+
desc 'Compiles the gRPC extension then runs all the tests'
|
54
|
+
task all: ['suite:idiomatic', 'suite:bidi', 'suite:server']
|
49
55
|
task default: :all
|
50
|
-
task 'spec:suite:wrapper' => [:compile, :rubocop]
|
51
|
-
task 'spec:suite:idiomatic' => 'spec:suite:wrapper'
|
52
|
-
task 'spec:suite:bidi' => 'spec:suite:wrapper'
|
53
|
-
task 'spec:suite:server' => 'spec:suite:wrapper'
|
54
|
-
task all: ['spec:suite:idiomatic', 'spec:suite:bidi', 'spec:suite:server']
|
data/bin/interop/README.md
CHANGED
File without changes
|
@@ -57,7 +57,7 @@ require 'test/cpp/interop/empty'
|
|
57
57
|
|
58
58
|
require 'signet/ssl_config'
|
59
59
|
|
60
|
-
AUTH_ENV = Google::Auth::
|
60
|
+
AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR
|
61
61
|
|
62
62
|
# loads the certificates used to access the test server securely.
|
63
63
|
def load_test_certs
|
@@ -176,12 +176,11 @@ end
|
|
176
176
|
def main
|
177
177
|
opts = parse_options
|
178
178
|
host = "0.0.0.0:#{opts['port']}"
|
179
|
+
s = GRPC::RpcServer.new
|
179
180
|
if opts['secure']
|
180
|
-
s
|
181
|
-
s.add_http2_port(host, true)
|
181
|
+
s.add_http2_port(host, test_server_creds)
|
182
182
|
logger.info("... running securely on #{host}")
|
183
183
|
else
|
184
|
-
s = GRPC::RpcServer.new
|
185
184
|
s.add_http2_port(host)
|
186
185
|
logger.info("... running insecurely on #{host}")
|
187
186
|
end
|
data/bin/math_server.rb
CHANGED
@@ -173,12 +173,11 @@ def main
|
|
173
173
|
end
|
174
174
|
end.parse!
|
175
175
|
|
176
|
+
s = GRPC::RpcServer.new
|
176
177
|
if options['secure']
|
177
|
-
s
|
178
|
-
s.add_http2_port(options['host'], true)
|
178
|
+
s.add_http2_port(options['host'], test_server_creds)
|
179
179
|
logger.info("... running securely on #{options['host']}")
|
180
180
|
else
|
181
|
-
s = GRPC::RpcServer.new
|
182
181
|
s.add_http2_port(options['host'])
|
183
182
|
logger.info("... running insecurely on #{options['host']}")
|
184
183
|
end
|
data/bin/noproto_server.rb
CHANGED
@@ -95,12 +95,11 @@ def main
|
|
95
95
|
end
|
96
96
|
end.parse!
|
97
97
|
|
98
|
+
s = GRPC::RpcServer.new
|
98
99
|
if options['secure']
|
99
|
-
s
|
100
|
-
s.add_http2_port(options['host'], true)
|
100
|
+
s.add_http2_port(options['host'], test_server_creds)
|
101
101
|
logger.info("... running securely on #{options['host']}")
|
102
102
|
else
|
103
|
-
s = GRPC::RpcServer.new
|
104
103
|
s.add_http2_port(options['host'])
|
105
104
|
logger.info("... running insecurely on #{options['host']}")
|
106
105
|
end
|
data/ext/grpc/extconf.rb
CHANGED
@@ -32,6 +32,17 @@ require 'mkmf'
|
|
32
32
|
LIBDIR = RbConfig::CONFIG['libdir']
|
33
33
|
INCLUDEDIR = RbConfig::CONFIG['includedir']
|
34
34
|
|
35
|
+
if ENV.key? 'GRPC_ROOT'
|
36
|
+
GRPC_ROOT = ENV['GRPC_ROOT']
|
37
|
+
if ENV.key? 'GRPC_LIB_DIR'
|
38
|
+
GRPC_LIB_DIR = ENV['GRPC_LIB_DIR']
|
39
|
+
else
|
40
|
+
GRPC_LIB_DIR = 'libs/opt'
|
41
|
+
end
|
42
|
+
else
|
43
|
+
GRPC_ROOT = nil
|
44
|
+
end
|
45
|
+
|
35
46
|
HEADER_DIRS = [
|
36
47
|
# Search /opt/local (Mac source install)
|
37
48
|
'/opt/local/include',
|
@@ -54,6 +65,11 @@ LIB_DIRS = [
|
|
54
65
|
LIBDIR
|
55
66
|
]
|
56
67
|
|
68
|
+
unless GRPC_ROOT.nil?
|
69
|
+
HEADER_DIRS.unshift File.join(GRPC_ROOT, 'include')
|
70
|
+
LIB_DIRS.unshift File.join(GRPC_ROOT, GRPC_LIB_DIR)
|
71
|
+
end
|
72
|
+
|
57
73
|
def crash(msg)
|
58
74
|
print(" extconf failure: #{msg}\n")
|
59
75
|
exit 1
|
@@ -61,7 +77,6 @@ end
|
|
61
77
|
|
62
78
|
dir_config('grpc', HEADER_DIRS, LIB_DIRS)
|
63
79
|
|
64
|
-
$CFLAGS << ' -std=c89 '
|
65
80
|
$CFLAGS << ' -Wno-implicit-function-declaration '
|
66
81
|
$CFLAGS << ' -Wno-pointer-sign '
|
67
82
|
$CFLAGS << ' -Wno-return-type '
|
data/ext/grpc/rb_grpc.c
CHANGED
@@ -119,12 +119,12 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
|
|
119
119
|
break;
|
120
120
|
|
121
121
|
case T_FLOAT:
|
122
|
-
if (interval &&
|
122
|
+
if (interval && RFLOAT_VALUE(time) < 0.0)
|
123
123
|
rb_raise(rb_eArgError, "%s must be positive", tstr);
|
124
124
|
else {
|
125
125
|
double f, d;
|
126
126
|
|
127
|
-
d = modf(
|
127
|
+
d = modf(RFLOAT_VALUE(time), &f);
|
128
128
|
if (d < 0) {
|
129
129
|
d += 1;
|
130
130
|
f -= 1;
|
@@ -132,7 +132,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
|
|
132
132
|
t.tv_sec = (time_t)f;
|
133
133
|
if (f != t.tv_sec) {
|
134
134
|
rb_raise(rb_eRangeError, "%f out of Time range",
|
135
|
-
|
135
|
+
RFLOAT_VALUE(time));
|
136
136
|
}
|
137
137
|
t.tv_nsec = (time_t)(d * 1e9 + 0.5);
|
138
138
|
}
|
data/ext/grpc/rb_server.c
CHANGED
@@ -97,35 +97,19 @@ static VALUE grpc_rb_server_alloc(VALUE cls) {
|
|
97
97
|
/*
|
98
98
|
call-seq:
|
99
99
|
cq = CompletionQueue.new
|
100
|
-
|
101
|
-
server_creds = ...
|
102
|
-
secure_server = Server.new(cq, {'arg1': 'value1'}, server_creds)
|
100
|
+
server = Server.new(cq, {'arg1': 'value1'})
|
103
101
|
|
104
102
|
Initializes server instances. */
|
105
|
-
static VALUE grpc_rb_server_init(
|
106
|
-
VALUE cqueue = Qnil;
|
107
|
-
VALUE credentials = Qnil;
|
108
|
-
VALUE channel_args = Qnil;
|
103
|
+
static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
|
109
104
|
grpc_completion_queue *cq = NULL;
|
110
|
-
grpc_server_credentials *creds = NULL;
|
111
105
|
grpc_rb_server *wrapper = NULL;
|
112
106
|
grpc_server *srv = NULL;
|
113
107
|
grpc_channel_args args;
|
114
108
|
MEMZERO(&args, grpc_channel_args, 1);
|
115
|
-
|
116
|
-
/* "21" == 2 mandatory args, 1 (credentials) is optional */
|
117
|
-
rb_scan_args(argc, argv, "21", &cqueue, &channel_args, &credentials);
|
118
109
|
cq = grpc_rb_get_wrapped_completion_queue(cqueue);
|
119
|
-
|
120
110
|
Data_Get_Struct(self, grpc_rb_server, wrapper);
|
121
111
|
grpc_rb_hash_convert_to_channel_args(channel_args, &args);
|
122
112
|
srv = grpc_server_create(cq, &args);
|
123
|
-
if (credentials == Qnil) {
|
124
|
-
srv = grpc_server_create(cq, &args);
|
125
|
-
} else {
|
126
|
-
creds = grpc_rb_get_wrapped_server_credentials(credentials);
|
127
|
-
srv = grpc_secure_server_create(creds, cq, &args);
|
128
|
-
}
|
129
113
|
|
130
114
|
if (args.args != NULL) {
|
131
115
|
xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
|
@@ -215,33 +199,36 @@ static VALUE grpc_rb_server_destroy(VALUE self) {
|
|
215
199
|
|
216
200
|
// secure port
|
217
201
|
server_creds = ...
|
218
|
-
secure_server = Server.new(cq, {'arg1': 'value1'}
|
219
|
-
secure_server.add_http_port('mydomain:7575',
|
202
|
+
secure_server = Server.new(cq, {'arg1': 'value1'})
|
203
|
+
secure_server.add_http_port('mydomain:7575', server_creds)
|
220
204
|
|
221
205
|
Adds a http2 port to server */
|
222
206
|
static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
|
223
207
|
VALUE port = Qnil;
|
224
|
-
VALUE
|
208
|
+
VALUE rb_creds = Qnil;
|
225
209
|
grpc_rb_server *s = NULL;
|
210
|
+
grpc_server_credentials *creds = NULL;
|
226
211
|
int recvd_port = 0;
|
227
212
|
|
228
|
-
/* "11" == 1 mandatory args, 1 (
|
229
|
-
rb_scan_args(argc, argv, "11", &port, &
|
213
|
+
/* "11" == 1 mandatory args, 1 (rb_creds) is optional */
|
214
|
+
rb_scan_args(argc, argv, "11", &port, &rb_creds);
|
230
215
|
|
231
216
|
Data_Get_Struct(self, grpc_rb_server, s);
|
232
217
|
if (s->wrapped == NULL) {
|
233
218
|
rb_raise(rb_eRuntimeError, "closed!");
|
234
219
|
return Qnil;
|
235
|
-
} else if (
|
220
|
+
} else if (rb_creds == Qnil) {
|
236
221
|
recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
|
237
222
|
if (recvd_port == 0) {
|
238
223
|
rb_raise(rb_eRuntimeError,
|
239
224
|
"could not add port %s to server, not sure why",
|
240
225
|
StringValueCStr(port));
|
241
226
|
}
|
242
|
-
} else
|
227
|
+
} else {
|
228
|
+
creds = grpc_rb_get_wrapped_server_credentials(rb_creds);
|
243
229
|
recvd_port =
|
244
|
-
grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port)
|
230
|
+
grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port),
|
231
|
+
creds);
|
245
232
|
if (recvd_port == 0) {
|
246
233
|
rb_raise(rb_eRuntimeError,
|
247
234
|
"could not add secure port %s to server, not sure why",
|
@@ -258,7 +245,7 @@ void Init_grpc_server() {
|
|
258
245
|
rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
|
259
246
|
|
260
247
|
/* Provides a ruby constructor and support for dup/clone. */
|
261
|
-
rb_define_method(rb_cServer, "initialize", grpc_rb_server_init,
|
248
|
+
rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
|
262
249
|
rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
|
263
250
|
|
264
251
|
/* Add the server methods. */
|
data/grpc.gemspec
CHANGED
@@ -21,14 +21,10 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.require_paths = ['lib']
|
22
22
|
s.platform = Gem::Platform::RUBY
|
23
23
|
|
24
|
-
s.add_dependency 'faraday', '~> 0.9'
|
25
24
|
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
|
26
25
|
s.add_dependency 'googleauth', '~> 0.1'
|
27
26
|
s.add_dependency 'logging', '~> 1.8'
|
28
|
-
s.add_dependency 'jwt', '~> 1.2.1'
|
29
27
|
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
|
30
|
-
s.add_dependency 'multi_json', '1.10.1'
|
31
|
-
s.add_dependency 'signet', '~> 0.6.0'
|
32
28
|
s.add_dependency 'xray', '~> 1.1'
|
33
29
|
|
34
30
|
s.add_development_dependency 'bundler', '~> 1.7'
|
@@ -505,12 +505,12 @@ module GRPC
|
|
505
505
|
|
506
506
|
# SingleReqView limits access to an ActiveCall's methods for use in server
|
507
507
|
# handlers that receive just one request.
|
508
|
-
SingleReqView = view_class(:cancelled, :deadline)
|
508
|
+
SingleReqView = view_class(:cancelled, :deadline, :metadata)
|
509
509
|
|
510
510
|
# MultiReqView limits access to an ActiveCall's methods for use in
|
511
511
|
# server client_streamer handlers.
|
512
512
|
MultiReqView = view_class(:cancelled, :deadline, :each_queued_msg,
|
513
|
-
:each_remote_read)
|
513
|
+
:each_remote_read, :metadata)
|
514
514
|
|
515
515
|
# Operation limits access to an ActiveCall's methods for use as
|
516
516
|
# a Operation on the client.
|
@@ -39,6 +39,25 @@ module GRPC
|
|
39
39
|
# Default deadline is 5 seconds.
|
40
40
|
DEFAULT_DEADLINE = 5
|
41
41
|
|
42
|
+
# setup_channel is used by #initialize to constuct a channel from its
|
43
|
+
# arguments.
|
44
|
+
def self.setup_channel(alt_chan, host, creds, **kw)
|
45
|
+
unless alt_chan.nil?
|
46
|
+
fail(TypeError, '!Channel') unless alt_chan.is_a?(Core::Channel)
|
47
|
+
return alt_chan
|
48
|
+
end
|
49
|
+
return Core::Channel.new(host, kw) if creds.nil?
|
50
|
+
fail(TypeError, '!Credentials') unless creds.is_a?(Core::Credentials)
|
51
|
+
Core::Channel.new(host, kw, creds)
|
52
|
+
end
|
53
|
+
|
54
|
+
# check_update_metadata is used by #initialize verify that it's a Proc.
|
55
|
+
def self.check_update_metadata(update_metadata)
|
56
|
+
return update_metadata if update_metadata.nil?
|
57
|
+
fail(TypeError, '!is_a?Proc') unless update_metadata.is_a?(Proc)
|
58
|
+
update_metadata
|
59
|
+
end
|
60
|
+
|
42
61
|
# Creates a new ClientStub.
|
43
62
|
#
|
44
63
|
# Minimally, a stub is created with the just the host of the gRPC service
|
@@ -73,40 +92,17 @@ module GRPC
|
|
73
92
|
# @param update_metadata a func that updates metadata as described above
|
74
93
|
# @param kw [KeywordArgs]the channel arguments
|
75
94
|
def initialize(host, q,
|
76
|
-
channel_override:nil,
|
95
|
+
channel_override: nil,
|
77
96
|
deadline: DEFAULT_DEADLINE,
|
78
97
|
creds: nil,
|
79
98
|
update_metadata: nil,
|
80
99
|
**kw)
|
81
|
-
unless q.is_a?
|
82
|
-
fail(ArgumentError, 'not a CompletionQueue')
|
83
|
-
end
|
100
|
+
fail(TypeError, '!CompletionQueue') unless q.is_a?(Core::CompletionQueue)
|
84
101
|
@queue = q
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
fail(ArgumentError, 'not a Channel') unless ch.is_a? Core::Channel
|
90
|
-
else
|
91
|
-
if creds.nil?
|
92
|
-
ch = Core::Channel.new(host, kw)
|
93
|
-
elsif !creds.is_a?(Core::Credentials)
|
94
|
-
fail(ArgumentError, 'not a Credentials')
|
95
|
-
else
|
96
|
-
ch = Core::Channel.new(host, kw, creds)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
@ch = ch
|
100
|
-
|
101
|
-
@update_metadata = nil
|
102
|
-
unless update_metadata.nil?
|
103
|
-
unless update_metadata.is_a? Proc
|
104
|
-
fail(ArgumentError, 'update_metadata is not a Proc')
|
105
|
-
end
|
106
|
-
@update_metadata = update_metadata
|
107
|
-
end
|
108
|
-
|
109
|
-
@host = host
|
102
|
+
@ch = ClientStub.setup_channel(channel_override, host, creds, **kw)
|
103
|
+
@update_metadata = ClientStub.check_update_metadata(update_metadata)
|
104
|
+
alt_host = kw[Core::Channel::SSL_TARGET]
|
105
|
+
@host = alt_host.nil? ? host : alt_host
|
110
106
|
@deadline = deadline
|
111
107
|
end
|
112
108
|
|
@@ -400,12 +396,7 @@ module GRPC
|
|
400
396
|
# @param deadline [TimeConst]
|
401
397
|
def new_active_call(ch, marshal, unmarshal, deadline = nil)
|
402
398
|
absolute_deadline = Core::TimeConsts.from_relative_time(deadline)
|
403
|
-
|
404
|
-
# the moment this fails a security check. This will be corrected.
|
405
|
-
#
|
406
|
-
# TODO: # remove this after create_call is updated
|
407
|
-
host = @host.split(':')[0]
|
408
|
-
call = @ch.create_call(ch, host, absolute_deadline)
|
399
|
+
call = @ch.create_call(ch, @host, absolute_deadline)
|
409
400
|
ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline,
|
410
401
|
started: false)
|
411
402
|
end
|
@@ -81,7 +81,6 @@ module GRPC
|
|
81
81
|
max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
|
82
82
|
poll_period:INFINITE_FUTURE,
|
83
83
|
completion_queue_override:nil,
|
84
|
-
creds:nil,
|
85
84
|
server_override:nil,
|
86
85
|
**kw)
|
87
86
|
if completion_queue_override.nil?
|
@@ -95,13 +94,7 @@ module GRPC
|
|
95
94
|
@cq = cq
|
96
95
|
|
97
96
|
if server_override.nil?
|
98
|
-
|
99
|
-
srv = Core::Server.new(@cq, kw)
|
100
|
-
elsif !creds.is_a? Core::ServerCredentials
|
101
|
-
fail(ArgumentError, 'not a ServerCredentials')
|
102
|
-
else
|
103
|
-
srv = Core::Server.new(@cq, kw, creds)
|
104
|
-
end
|
97
|
+
srv = Core::Server.new(@cq, kw)
|
105
98
|
else
|
106
99
|
srv = server_override
|
107
100
|
fail(ArgumentError, 'not a Server') unless srv.is_a? Core::Server
|
data/lib/grpc/generic/service.rb
CHANGED
@@ -176,25 +176,26 @@ module GRPC
|
|
176
176
|
unmarshal = desc.unmarshal_proc(:output)
|
177
177
|
route = "/#{route_prefix}/#{name}"
|
178
178
|
if desc.request_response?
|
179
|
-
define_method(mth_name) do |req, deadline = nil|
|
179
|
+
define_method(mth_name) do |req, deadline = nil, **kw|
|
180
180
|
logger.debug("calling #{@host}:#{route}")
|
181
|
-
request_response(route, req, marshal, unmarshal, deadline)
|
181
|
+
request_response(route, req, marshal, unmarshal, deadline, **kw)
|
182
182
|
end
|
183
183
|
elsif desc.client_streamer?
|
184
|
-
define_method(mth_name) do |reqs, deadline = nil|
|
184
|
+
define_method(mth_name) do |reqs, deadline = nil, **kw|
|
185
185
|
logger.debug("calling #{@host}:#{route}")
|
186
|
-
client_streamer(route, reqs, marshal, unmarshal, deadline)
|
186
|
+
client_streamer(route, reqs, marshal, unmarshal, deadline, **kw)
|
187
187
|
end
|
188
188
|
elsif desc.server_streamer?
|
189
|
-
define_method(mth_name) do |req, deadline = nil, &blk|
|
189
|
+
define_method(mth_name) do |req, deadline = nil, **kw, &blk|
|
190
190
|
logger.debug("calling #{@host}:#{route}")
|
191
|
-
server_streamer(route, req, marshal, unmarshal, deadline,
|
191
|
+
server_streamer(route, req, marshal, unmarshal, deadline, **kw,
|
192
192
|
&blk)
|
193
193
|
end
|
194
194
|
else # is a bidi_stream
|
195
|
-
define_method(mth_name) do |reqs, deadline = nil, &blk|
|
195
|
+
define_method(mth_name) do |reqs, deadline = nil, **kw, &blk|
|
196
196
|
logger.debug("calling #{@host}:#{route}")
|
197
|
-
bidi_streamer(route, reqs, marshal, unmarshal, deadline,
|
197
|
+
bidi_streamer(route, reqs, marshal, unmarshal, deadline, **kw,
|
198
|
+
&blk)
|
198
199
|
end
|
199
200
|
end
|
200
201
|
end
|
@@ -217,8 +218,8 @@ module GRPC
|
|
217
218
|
|
218
219
|
def self.included(o)
|
219
220
|
o.extend(Dsl)
|
220
|
-
# Update to the use the service name including module.
|
221
|
-
# that can be nil e
|
221
|
+
# Update to the use the service name including module. Provide a default
|
222
|
+
# that can be nil e.g. when modules are declared dynamically.
|
222
223
|
return unless o.service_name.nil?
|
223
224
|
if o.name.nil?
|
224
225
|
o.service_name = 'GenericService'
|
data/lib/grpc/version.rb
CHANGED
data/spec/client_server_spec.rb
CHANGED
@@ -95,7 +95,7 @@ shared_context 'setup: tags' do
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def new_client_call
|
98
|
-
@ch.create_call('/method', '
|
98
|
+
@ch.create_call('/method', 'foo.test.google.fr', deadline)
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
@@ -346,12 +346,12 @@ end
|
|
346
346
|
describe 'the secure http client/server' do
|
347
347
|
before(:example) do
|
348
348
|
certs = load_test_certs
|
349
|
-
server_host = '
|
349
|
+
server_host = '0.0.0.0:0'
|
350
350
|
@client_queue = GRPC::Core::CompletionQueue.new
|
351
351
|
@server_queue = GRPC::Core::CompletionQueue.new
|
352
352
|
server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
|
353
|
-
@server = GRPC::Core::Server.new(@server_queue, nil
|
354
|
-
server_port = @server.add_http2_port(server_host,
|
353
|
+
@server = GRPC::Core::Server.new(@server_queue, nil)
|
354
|
+
server_port = @server.add_http2_port(server_host, server_creds)
|
355
355
|
@server.start
|
356
356
|
args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
|
357
357
|
@ch = Channel.new("0.0.0.0:#{server_port}", args,
|
@@ -362,11 +362,9 @@ describe 'the secure http client/server' do
|
|
362
362
|
@server.close
|
363
363
|
end
|
364
364
|
|
365
|
-
|
366
|
-
|
367
|
-
# end
|
365
|
+
it_behaves_like 'basic GRPC message delivery is OK' do
|
366
|
+
end
|
368
367
|
|
369
|
-
|
370
|
-
|
371
|
-
# end
|
368
|
+
it_behaves_like 'GRPC metadata delivery works OK' do
|
369
|
+
end
|
372
370
|
end
|
@@ -67,8 +67,8 @@ describe GRPC::ActiveCall do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
describe '#multi_req_view' do
|
70
|
-
|
71
|
-
want = %w(cancelled, deadline, each_remote_read, shutdown)
|
70
|
+
it 'exposes a fixed subset of the ActiveCall methods' do
|
71
|
+
want = %w(cancelled, deadline, each_remote_read, metadata, shutdown)
|
72
72
|
v = @client_call.multi_req_view
|
73
73
|
want.each do |w|
|
74
74
|
expect(v.methods.include?(w))
|
@@ -77,8 +77,8 @@ describe GRPC::ActiveCall do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
describe '#single_req_view' do
|
80
|
-
|
81
|
-
want = %w(cancelled, deadline, shutdown)
|
80
|
+
it 'exposes a fixed subset of the ActiveCall methods' do
|
81
|
+
want = %w(cancelled, deadline, metadata, shutdown)
|
82
82
|
v = @client_call.single_req_view
|
83
83
|
want.each do |w|
|
84
84
|
expect(v.methods.include?(w))
|
@@ -384,13 +384,7 @@ describe 'ClientStub' do
|
|
384
384
|
th.join
|
385
385
|
end
|
386
386
|
|
387
|
-
|
388
|
-
#
|
389
|
-
# - servers should be able initiate messaging, however, as it stand
|
390
|
-
# servers don't know if all the client metadata has been sent until
|
391
|
-
# they receive a message from the client. Without receiving all the
|
392
|
-
# metadata, the server does not accept the call, so this test hangs.
|
393
|
-
xit 'supports a server-initiated ping pong', bidi: true do
|
387
|
+
it 'supports a server-initiated ping pong', bidi: true do
|
394
388
|
server_port = create_test_server
|
395
389
|
host = "localhost:#{server_port}"
|
396
390
|
th = run_bidi_streamer_echo_ping_pong(@sent_msgs, @pass, false)
|
@@ -434,7 +428,7 @@ describe 'ClientStub' do
|
|
434
428
|
end
|
435
429
|
expect(c.remote_read).to eq(expected_input)
|
436
430
|
replys.each { |r| c.remote_send(r) }
|
437
|
-
c.send_status(status, status == @pass ? 'OK' : 'NOK')
|
431
|
+
c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
|
438
432
|
end
|
439
433
|
end
|
440
434
|
|
@@ -444,7 +438,7 @@ describe 'ClientStub' do
|
|
444
438
|
c = expect_server_to_be_invoked(mtx, cnd)
|
445
439
|
expected_inputs.each { |i| expect(c.remote_read).to eq(i) }
|
446
440
|
replys.each { |r| c.remote_send(r) }
|
447
|
-
c.send_status(status, status == @pass ? 'OK' : 'NOK')
|
441
|
+
c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
|
448
442
|
end
|
449
443
|
end
|
450
444
|
|
@@ -460,7 +454,7 @@ describe 'ClientStub' do
|
|
460
454
|
expect(c.remote_read).to eq(i)
|
461
455
|
end
|
462
456
|
end
|
463
|
-
c.send_status(status, status == @pass ? 'OK' : 'NOK')
|
457
|
+
c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
|
464
458
|
end
|
465
459
|
end
|
466
460
|
|
@@ -473,7 +467,7 @@ describe 'ClientStub' do
|
|
473
467
|
expect(c.metadata[k.to_s]).to eq(v)
|
474
468
|
end
|
475
469
|
c.remote_send(resp)
|
476
|
-
c.send_status(status, status == @pass ? 'OK' : 'NOK')
|
470
|
+
c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
|
477
471
|
end
|
478
472
|
end
|
479
473
|
|
@@ -486,7 +480,7 @@ describe 'ClientStub' do
|
|
486
480
|
expect(c.metadata[k.to_s]).to eq(v)
|
487
481
|
end
|
488
482
|
c.remote_send(resp)
|
489
|
-
c.send_status(status, status == @pass ? 'OK' : 'NOK')
|
483
|
+
c.send_status(status, status == @pass ? 'OK' : 'NOK', true)
|
490
484
|
end
|
491
485
|
end
|
492
486
|
|
@@ -94,6 +94,7 @@ describe GRPC::RpcDesc do
|
|
94
94
|
expect(@call).to receive(:remote_read).once.and_return(req)
|
95
95
|
expect(@call).to receive(:remote_send).once.with(@ok_response)
|
96
96
|
expect(@call).to receive(:send_status).once.with(OK, 'OK')
|
97
|
+
expect(@call).to receive(:finished).once
|
97
98
|
@request_response.run_server_method(@call, method(:fake_reqresp))
|
98
99
|
end
|
99
100
|
end
|
@@ -134,6 +135,7 @@ describe GRPC::RpcDesc do
|
|
134
135
|
it 'sends a response and closes the stream if there no errors' do
|
135
136
|
expect(@call).to receive(:remote_send).once.with(@ok_response)
|
136
137
|
expect(@call).to receive(:send_status).once.with(OK, 'OK')
|
138
|
+
expect(@call).to receive(:finished).once
|
137
139
|
@client_streamer.run_server_method(@call, method(:fake_clstream))
|
138
140
|
end
|
139
141
|
end
|
@@ -178,6 +180,7 @@ describe GRPC::RpcDesc do
|
|
178
180
|
expect(@call).to receive(:remote_read).once.and_return(req)
|
179
181
|
expect(@call).to receive(:remote_send).twice.with(@ok_response)
|
180
182
|
expect(@call).to receive(:send_status).once.with(OK, 'OK')
|
183
|
+
expect(@call).to receive(:finished).once
|
181
184
|
@server_streamer.run_server_method(@call, method(:fake_svstream))
|
182
185
|
end
|
183
186
|
end
|
@@ -207,6 +210,7 @@ describe GRPC::RpcDesc do
|
|
207
210
|
it 'closes the stream if there no errors' do
|
208
211
|
expect(@call).to receive(:run_server_bidi)
|
209
212
|
expect(@call).to receive(:send_status).once.with(OK, 'OK')
|
213
|
+
expect(@call).to receive(:finished).once
|
210
214
|
@bidi_streamer.run_server_method(@call, method(:fake_bidistream))
|
211
215
|
end
|
212
216
|
end
|
@@ -62,12 +62,15 @@ end
|
|
62
62
|
class EchoService
|
63
63
|
include GRPC::GenericService
|
64
64
|
rpc :an_rpc, EchoMsg, EchoMsg
|
65
|
+
attr_reader :received_md
|
65
66
|
|
66
67
|
def initialize(_default_var = 'ignored')
|
68
|
+
@received_md = []
|
67
69
|
end
|
68
70
|
|
69
|
-
def an_rpc(req,
|
71
|
+
def an_rpc(req, call)
|
70
72
|
logger.info('echo service received a request')
|
73
|
+
@received_md << call.metadata unless call.metadata.nil?
|
71
74
|
req
|
72
75
|
end
|
73
76
|
end
|
@@ -78,14 +81,17 @@ EchoStub = EchoService.rpc_stub_class
|
|
78
81
|
class SlowService
|
79
82
|
include GRPC::GenericService
|
80
83
|
rpc :an_rpc, EchoMsg, EchoMsg
|
84
|
+
attr_reader :received_md, :delay
|
81
85
|
|
82
86
|
def initialize(_default_var = 'ignored')
|
87
|
+
@delay = 0.25
|
88
|
+
@received_md = []
|
83
89
|
end
|
84
90
|
|
85
|
-
def an_rpc(req,
|
86
|
-
delay
|
87
|
-
|
88
|
-
|
91
|
+
def an_rpc(req, call)
|
92
|
+
logger.info("starting a slow #{@delay} rpc")
|
93
|
+
sleep @delay
|
94
|
+
@received_md << call.metadata unless call.metadata.nil?
|
89
95
|
req # send back the req as the response
|
90
96
|
end
|
91
97
|
end
|
@@ -164,19 +170,6 @@ describe GRPC::RpcServer do
|
|
164
170
|
expect(&blk).to raise_error
|
165
171
|
end
|
166
172
|
|
167
|
-
it 'can be created with the creds as valid ServerCedentials' do
|
168
|
-
certs = load_test_certs
|
169
|
-
server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
|
170
|
-
blk = proc do
|
171
|
-
opts = {
|
172
|
-
a_channel_arg: 'an_arg',
|
173
|
-
creds: server_creds
|
174
|
-
}
|
175
|
-
RpcServer.new(**opts)
|
176
|
-
end
|
177
|
-
expect(&blk).to_not raise_error
|
178
|
-
end
|
179
|
-
|
180
173
|
it 'can be created with a server override' do
|
181
174
|
opts = { a_channel_arg: 'an_arg', server_override: @server }
|
182
175
|
blk = proc do
|
@@ -350,6 +343,69 @@ describe GRPC::RpcServer do
|
|
350
343
|
t.join
|
351
344
|
end
|
352
345
|
|
346
|
+
it 'should receive metadata sent as rpc keyword args', server: true do
|
347
|
+
service = EchoService.new
|
348
|
+
@srv.handle(service)
|
349
|
+
t = Thread.new { @srv.run }
|
350
|
+
@srv.wait_till_running
|
351
|
+
req = EchoMsg.new
|
352
|
+
stub = EchoStub.new(@host, **@client_opts)
|
353
|
+
expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
|
354
|
+
wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
|
355
|
+
expect(service.received_md).to eq(wanted_md)
|
356
|
+
@srv.stop
|
357
|
+
t.join
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should receive metadata when a deadline is specified', server: true do
|
361
|
+
service = SlowService.new
|
362
|
+
@srv.handle(service)
|
363
|
+
t = Thread.new { @srv.run }
|
364
|
+
@srv.wait_till_running
|
365
|
+
req = EchoMsg.new
|
366
|
+
stub = SlowStub.new(@host, **@client_opts)
|
367
|
+
deadline = service.delay + 0.5 # wait for long enough
|
368
|
+
expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
|
369
|
+
wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
|
370
|
+
expect(service.received_md).to eq(wanted_md)
|
371
|
+
@srv.stop
|
372
|
+
t.join
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'should not receive metadata if the client times out', server: true do
|
376
|
+
service = SlowService.new
|
377
|
+
@srv.handle(service)
|
378
|
+
t = Thread.new { @srv.run }
|
379
|
+
@srv.wait_till_running
|
380
|
+
req = EchoMsg.new
|
381
|
+
stub = SlowStub.new(@host, **@client_opts)
|
382
|
+
deadline = 0.1 # too short for SlowService to respond
|
383
|
+
blk = proc { stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2') }
|
384
|
+
expect(&blk).to raise_error GRPC::BadStatus
|
385
|
+
wanted_md = []
|
386
|
+
expect(service.received_md).to eq(wanted_md)
|
387
|
+
@srv.stop
|
388
|
+
t.join
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'should receive updated metadata', server: true do
|
392
|
+
service = EchoService.new
|
393
|
+
@srv.handle(service)
|
394
|
+
t = Thread.new { @srv.run }
|
395
|
+
@srv.wait_till_running
|
396
|
+
req = EchoMsg.new
|
397
|
+
@client_opts[:update_metadata] = proc do |md|
|
398
|
+
md[:k1] = 'updated-v1'
|
399
|
+
md
|
400
|
+
end
|
401
|
+
stub = EchoStub.new(@host, **@client_opts)
|
402
|
+
expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
|
403
|
+
wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2' }]
|
404
|
+
expect(service.received_md).to eq(wanted_md)
|
405
|
+
@srv.stop
|
406
|
+
t.join
|
407
|
+
end
|
408
|
+
|
353
409
|
it 'should handle multiple parallel requests', server: true do
|
354
410
|
@srv.handle(EchoService)
|
355
411
|
Thread.new { @srv.run }
|
data/spec/server_spec.rb
CHANGED
@@ -118,10 +118,11 @@ describe Server do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
describe 'for secure servers' do
|
121
|
+
let(:cert) { create_test_cert }
|
121
122
|
it 'runs without failing' do
|
122
123
|
blk = proc do
|
123
124
|
s = Server.new(@cq, nil)
|
124
|
-
s.add_http2_port('localhost:0',
|
125
|
+
s.add_http2_port('localhost:0', cert)
|
125
126
|
s.close
|
126
127
|
end
|
127
128
|
expect(&blk).to_not raise_error
|
@@ -130,7 +131,7 @@ describe Server do
|
|
130
131
|
it 'fails if the server is closed' do
|
131
132
|
s = Server.new(@cq, nil)
|
132
133
|
s.close
|
133
|
-
blk = proc { s.add_http2_port('localhost:0',
|
134
|
+
blk = proc { s.add_http2_port('localhost:0', cert) }
|
134
135
|
expect(&blk).to raise_error(RuntimeError)
|
135
136
|
end
|
136
137
|
end
|
@@ -138,7 +139,7 @@ describe Server do
|
|
138
139
|
|
139
140
|
shared_examples '#new' do
|
140
141
|
it 'takes a completion queue with nil channel args' do
|
141
|
-
expect { Server.new(@cq, nil
|
142
|
+
expect { Server.new(@cq, nil) }.to_not raise_error
|
142
143
|
end
|
143
144
|
|
144
145
|
it 'does not take a hash with bad keys as channel args' do
|
@@ -195,14 +196,6 @@ describe Server do
|
|
195
196
|
it_behaves_like '#new'
|
196
197
|
end
|
197
198
|
|
198
|
-
describe '#new with a secure channel' do
|
199
|
-
def construct_with_args(a)
|
200
|
-
proc { Server.new(@cq, a, create_test_cert) }
|
201
|
-
end
|
202
|
-
|
203
|
-
it_behaves_like '#new'
|
204
|
-
end
|
205
|
-
|
206
199
|
def start_a_server
|
207
200
|
s = Server.new(@cq, nil)
|
208
201
|
s.add_http2_port('0.0.0.0:0')
|
data/spec/testdata/README
CHANGED
File without changes
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gRPC Authors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: faraday
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.9'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0.9'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: google-protobuf
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +52,6 @@ dependencies:
|
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '1.8'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: jwt
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 1.2.1
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.2.1
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: minitest
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,34 +66,6 @@ dependencies:
|
|
94
66
|
- - "~>"
|
95
67
|
- !ruby/object:Gem::Version
|
96
68
|
version: '5.4'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: multi_json
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - '='
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: 1.10.1
|
104
|
-
type: :runtime
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - '='
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: 1.10.1
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: signet
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 0.6.0
|
118
|
-
type: :runtime
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 0.6.0
|
125
69
|
- !ruby/object:Gem::Dependency
|
126
70
|
name: xray
|
127
71
|
requirement: !ruby/object:Gem::Requirement
|