ovirt-engine-sdk 4.2.0 → 4.2.1
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 +4 -4
- data/CHANGES.adoc +10 -0
- data/ext/ovirtsdk4c/ov_http_client.c +132 -10
- data/ext/ovirtsdk4c/ov_http_client.h +8 -1
- data/lib/ovirtsdk4/connection.rb +5 -7
- data/lib/ovirtsdk4/service.rb +5 -7
- data/lib/ovirtsdk4/version.rb +1 -1
- data/lib/ovirtsdk4/writer.rb +1 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 573f19600decb51717ca7b46538158f0bfe145c9
|
4
|
+
data.tar.gz: a42480a7c2627d263106163ecd835c80f0ef2b53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a8a06fe3746bf4c82c9d18c5d9dce0920f6202857444e830c4e917e2d8bacfc2ac9bdd802402a9049b7df029105837c41f1c0c94e3cd7ffc50750abefd66547
|
7
|
+
data.tar.gz: 97c010a0aee2fa5e496df698790a65be800522c4271db84aba09cf3fe6be1794aec263a0f7607edc22eb8aa48fda629a018e182d9e1d872c716b68626deb1219
|
data/CHANGES.adoc
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
This document describes the relevant changes between releases of the SDK.
|
4
4
|
|
5
|
+
== 4.2.1 / Dec 13 2017
|
6
|
+
|
7
|
+
Bug fixes:
|
8
|
+
|
9
|
+
* Fix handling of the `all_content` parameter
|
10
|
+
https://bugzilla.redhat.com/1525555[#1525555].
|
11
|
+
|
12
|
+
* Limit the number of requests sent to `libcurl`
|
13
|
+
https://bugzilla.redhat.com/1525302[#1525302].
|
14
|
+
|
5
15
|
== 4.2.0 / Dec 4 2017
|
6
16
|
|
7
17
|
No chages, version `4.2.0` is identical to `4.2.0-beta2`, only the
|
@@ -64,16 +64,21 @@ static ID READ_ID;
|
|
64
64
|
static ID STRING_ID;
|
65
65
|
static ID STRING_IO_ID;
|
66
66
|
static ID URI_ID;
|
67
|
+
static ID WARN_ID;
|
68
|
+
static ID WARN_Q_ID;
|
67
69
|
static ID WRITE_ID;
|
68
70
|
|
69
71
|
/* References to classes: */
|
70
|
-
static VALUE URI_CLASS;
|
71
72
|
static VALUE STRING_IO_CLASS;
|
73
|
+
static VALUE URI_CLASS;
|
72
74
|
|
73
75
|
/* Constants: */
|
74
76
|
const char CR = '\x0D';
|
75
77
|
const char LF = '\x0A';
|
76
78
|
|
79
|
+
/* Version of libcurl: */
|
80
|
+
static curl_version_info_data* libcurl_version;
|
81
|
+
|
77
82
|
/* Before version 7.38.0 of libcurl the NEGOTIATE authentication method was named GSSNEGOTIATE: */
|
78
83
|
#ifndef CURLAUTH_NEGOTIATE
|
79
84
|
#define CURLAUTH_NEGOTIATE CURLAUTH_GSSNEGOTIATE
|
@@ -85,6 +90,21 @@ const char LF = '\x0A';
|
|
85
90
|
#define CURLPIPE_HTTP1 1
|
86
91
|
#endif
|
87
92
|
|
93
|
+
/* Define options that may not be available in some versions of libcurl: */
|
94
|
+
#if LIBCURL_VERSION_NUM < 0x071e00 /* 7.30.0 */
|
95
|
+
#define CURLMOPT_MAX_HOST_CONNECTIONS 7
|
96
|
+
#define CURLMOPT_MAX_PIPELINE_LENGTH 8
|
97
|
+
#define CURLMOPT_MAX_TOTAL_CONNECTIONS 13
|
98
|
+
#endif
|
99
|
+
|
100
|
+
#if LIBCURL_VERSION_NUM < 0x070f03 /* 7.16.3 */
|
101
|
+
#define CURLMOPT_MAXCONNECTS 6
|
102
|
+
#endif
|
103
|
+
|
104
|
+
#if LIBCURL_VERSION_NUM < 0x070f00 /* 7.16.0 */
|
105
|
+
#define CURLMOPT_PIPELINING 3
|
106
|
+
#endif
|
107
|
+
|
88
108
|
typedef struct {
|
89
109
|
VALUE io; /* IO */
|
90
110
|
char* ptr;
|
@@ -131,6 +151,22 @@ static void ov_http_client_log_info(VALUE log, const char* format, ...) {
|
|
131
151
|
}
|
132
152
|
}
|
133
153
|
|
154
|
+
static void ov_http_client_log_warn(VALUE log, const char* format, ...) {
|
155
|
+
VALUE enabled;
|
156
|
+
VALUE message;
|
157
|
+
va_list args;
|
158
|
+
|
159
|
+
if (!NIL_P(log)) {
|
160
|
+
enabled = rb_funcall(log, WARN_Q_ID, 0);
|
161
|
+
if (RTEST(enabled)) {
|
162
|
+
va_start(args, format);
|
163
|
+
message = rb_vsprintf(format, args);
|
164
|
+
rb_funcall(log, WARN_ID, 1, message);
|
165
|
+
va_end(args);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
134
170
|
static void ov_http_client_check_closed(ov_http_client_object* object) {
|
135
171
|
if (object->handle == NULL) {
|
136
172
|
rb_raise(ov_error_class, "The client is already closed");
|
@@ -142,6 +178,7 @@ static void ov_http_client_mark(void* vptr) {
|
|
142
178
|
|
143
179
|
ptr = vptr;
|
144
180
|
rb_gc_mark(ptr->log);
|
181
|
+
rb_gc_mark(ptr->queue);
|
145
182
|
rb_gc_mark(ptr->pending);
|
146
183
|
rb_gc_mark(ptr->completed);
|
147
184
|
}
|
@@ -192,6 +229,8 @@ static VALUE ov_http_client_alloc(VALUE klass) {
|
|
192
229
|
ptr->handle = NULL;
|
193
230
|
ptr->share = NULL;
|
194
231
|
ptr->log = Qnil;
|
232
|
+
ptr->limit = 0;
|
233
|
+
ptr->queue = Qnil;
|
195
234
|
ptr->pending = Qnil;
|
196
235
|
ptr->completed = Qnil;
|
197
236
|
ptr->compress = false;
|
@@ -567,16 +606,22 @@ static VALUE ov_http_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
567
606
|
Check_Type(opt, T_FIXNUM);
|
568
607
|
pipeline = NUM2LONG(opt);
|
569
608
|
}
|
609
|
+
if (pipeline < 0) {
|
610
|
+
rb_raise(rb_eArgError, "The maximum pipeline length can't be %ld, minimum is 0.", pipeline);
|
611
|
+
}
|
570
612
|
|
571
613
|
/* Get the value of the 'connections' parameter: */
|
572
614
|
opt = rb_hash_aref(opts, CONNECTIONS_SYMBOL);
|
573
615
|
if (NIL_P(opt)) {
|
574
|
-
connections =
|
616
|
+
connections = 1;
|
575
617
|
}
|
576
618
|
else {
|
577
619
|
Check_Type(opt, T_FIXNUM);
|
578
620
|
connections = NUM2LONG(opt);
|
579
621
|
}
|
622
|
+
if (connections < 1) {
|
623
|
+
rb_raise(rb_eArgError, "The maximum number of connections can't be %ld, minimum is 1.", connections);
|
624
|
+
}
|
580
625
|
|
581
626
|
/* Get the value of the 'cookies' parameter. If it is a string it will be used as the path of the file where the
|
582
627
|
cookies will be stored. If it is any other thing it will be treated as a boolean flag indicating if cookies
|
@@ -592,11 +637,22 @@ static VALUE ov_http_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
592
637
|
ptr->cookies = NULL;
|
593
638
|
}
|
594
639
|
|
640
|
+
/* Create the queue that contains requests that haven't been sent to libcurl yet: */
|
641
|
+
ptr->queue = rb_ary_new();
|
642
|
+
|
595
643
|
/* Create the hash that contains the transfers are pending an completed. Both use the identity of the request
|
596
644
|
as key. */
|
597
645
|
ptr->completed = rb_funcall(rb_hash_new(), COMPARE_BY_IDENTITY_ID, 0);
|
598
646
|
ptr->pending = rb_funcall(rb_hash_new(), COMPARE_BY_IDENTITY_ID, 0);
|
599
647
|
|
648
|
+
/* Calculate the max number of requests that can be handled by libcurl simultaneously. For versions of libcurl
|
649
|
+
newer than 7.30.0 the limit can be increased when using pipelining. For older versions it can't be increased
|
650
|
+
because libcurl would create additional connections for the requests that can't be pipelined. */
|
651
|
+
ptr->limit = connections;
|
652
|
+
if (pipeline > 0 && libcurl_version->version_num >= 0x071e00 /* 7.30.0 */) {
|
653
|
+
ptr->limit *= pipeline;
|
654
|
+
}
|
655
|
+
|
600
656
|
/* Create the libcurl multi handle: */
|
601
657
|
ptr->handle = curl_multi_init();
|
602
658
|
if (ptr->handle == NULL) {
|
@@ -615,14 +671,47 @@ static VALUE ov_http_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
615
671
|
/* Enable pipelining: */
|
616
672
|
if (pipeline > 0) {
|
617
673
|
curl_multi_setopt(ptr->handle, CURLMOPT_PIPELINING, CURLPIPE_HTTP1);
|
618
|
-
|
619
|
-
|
620
|
-
|
674
|
+
if (libcurl_version->version_num >= 0x071e00 /* 7.30.0 */) {
|
675
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAX_PIPELINE_LENGTH, pipeline);
|
676
|
+
}
|
677
|
+
else {
|
678
|
+
ov_http_client_log_warn(
|
679
|
+
ptr->log,
|
680
|
+
"Can't set maximum pipeline length to %d, it isn't supported by libcurl %s. Upgrade to 7.30.0 or "
|
681
|
+
"newer to avoid this issue.",
|
682
|
+
pipeline,
|
683
|
+
libcurl_version->version
|
684
|
+
);
|
685
|
+
}
|
621
686
|
}
|
687
|
+
|
688
|
+
/* Set the max number of connections: */
|
622
689
|
if (connections > 0) {
|
623
|
-
|
624
|
-
|
625
|
-
|
690
|
+
if (libcurl_version->version_num >= 0x071e00 /* 7.30.0 */) {
|
691
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAX_HOST_CONNECTIONS, connections);
|
692
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, connections);
|
693
|
+
}
|
694
|
+
else {
|
695
|
+
ov_http_client_log_warn(
|
696
|
+
ptr->log,
|
697
|
+
"Can't set maximum number of connections to %d, it isn't supported by libcurl %s. Upgrade to 7.30.0 "
|
698
|
+
"or newer to avoid this issue.",
|
699
|
+
connections,
|
700
|
+
libcurl_version->version
|
701
|
+
);
|
702
|
+
}
|
703
|
+
if (libcurl_version->version_num >= 0x070f03 /* 7.16.3 */) {
|
704
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAXCONNECTS, connections);
|
705
|
+
}
|
706
|
+
else {
|
707
|
+
ov_http_client_log_warn(
|
708
|
+
ptr->log,
|
709
|
+
"Can't set total maximum connection cache size to %d, it isn't supported by libcurl %s. Upgrade to "
|
710
|
+
"7.16.3 or newer to avoid this issue.",
|
711
|
+
connections,
|
712
|
+
libcurl_version->version
|
713
|
+
);
|
714
|
+
}
|
626
715
|
}
|
627
716
|
|
628
717
|
return self;
|
@@ -922,7 +1011,7 @@ static void ov_http_client_prepare_handle(ov_http_client_object* client_ptr, ov_
|
|
922
1011
|
);
|
923
1012
|
}
|
924
1013
|
|
925
|
-
static VALUE
|
1014
|
+
static VALUE ov_http_client_submit(VALUE self, VALUE request) {
|
926
1015
|
CURL* handle;
|
927
1016
|
VALUE response;
|
928
1017
|
VALUE transfer;
|
@@ -992,7 +1081,26 @@ static VALUE ov_http_client_send(VALUE self, VALUE request) {
|
|
992
1081
|
return Qnil;
|
993
1082
|
}
|
994
1083
|
|
1084
|
+
static VALUE ov_http_client_send(VALUE self, VALUE request) {
|
1085
|
+
ov_http_client_object* ptr;
|
1086
|
+
|
1087
|
+
/* Get the pointer to the native object and check that it isn't closed: */
|
1088
|
+
ov_http_client_ptr(self, ptr);
|
1089
|
+
ov_http_client_check_closed(ptr);
|
1090
|
+
|
1091
|
+
/* If the limit hasn't been reached then submit the request directly to libcurl, otherwise put it in the queue: */
|
1092
|
+
if (RHASH_SIZE(ptr->pending) < ptr->limit) {
|
1093
|
+
ov_http_client_submit(self, request);
|
1094
|
+
}
|
1095
|
+
else {
|
1096
|
+
rb_ary_push(ptr->queue, request);
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
return Qnil;
|
1100
|
+
}
|
1101
|
+
|
995
1102
|
static VALUE ov_http_client_wait(VALUE self, VALUE request) {
|
1103
|
+
VALUE next;
|
996
1104
|
VALUE result;
|
997
1105
|
ov_http_client_object* ptr;
|
998
1106
|
ov_http_client_wait_context context;
|
@@ -1001,15 +1109,24 @@ static VALUE ov_http_client_wait(VALUE self, VALUE request) {
|
|
1001
1109
|
ov_http_client_ptr(self, ptr);
|
1002
1110
|
ov_http_client_check_closed(ptr);
|
1003
1111
|
|
1004
|
-
/* Work till the transfer has been completed
|
1112
|
+
/* Work till the transfer has been completed. */
|
1005
1113
|
context.handle = ptr->handle;
|
1006
1114
|
context.code = CURLE_OK;
|
1007
1115
|
context.cancel = false;
|
1008
1116
|
for (;;) {
|
1117
|
+
/* Move requests from the queue to libcurl: */
|
1118
|
+
while (RARRAY_LEN(ptr->queue) > 0 && RHASH_SIZE(ptr->pending) < ptr->limit) {
|
1119
|
+
next = rb_ary_shift(ptr->queue);
|
1120
|
+
ov_http_client_submit(self, next);
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
/* Check if the response is already available, if so then return it: */
|
1009
1124
|
result = rb_hash_delete(ptr->completed, request);
|
1010
1125
|
if (!NIL_P(result)) {
|
1011
1126
|
return result;
|
1012
1127
|
}
|
1128
|
+
|
1129
|
+
/* If the response isn't available yet, then do some real work: */
|
1013
1130
|
rb_thread_call_without_gvl(
|
1014
1131
|
ov_http_client_wait_task,
|
1015
1132
|
&context,
|
@@ -1082,6 +1199,8 @@ void ov_http_client_define(void) {
|
|
1082
1199
|
STRING_ID = rb_intern("string");
|
1083
1200
|
STRING_IO_ID = rb_intern("StringIO");
|
1084
1201
|
URI_ID = rb_intern("URI");
|
1202
|
+
WARN_ID = rb_intern("warn");
|
1203
|
+
WARN_Q_ID = rb_intern("warn?");
|
1085
1204
|
WRITE_ID = rb_intern("write");
|
1086
1205
|
|
1087
1206
|
/* Locate classes: */
|
@@ -1093,4 +1212,7 @@ void ov_http_client_define(void) {
|
|
1093
1212
|
if (code != CURLE_OK) {
|
1094
1213
|
rb_raise(ov_error_class, "Can't initialize libcurl: %s", curl_easy_strerror(code));
|
1095
1214
|
}
|
1215
|
+
|
1216
|
+
/* Get the libcurl version: */
|
1217
|
+
libcurl_version = curl_version_info(CURLVERSION_NOW);
|
1096
1218
|
}
|
@@ -37,7 +37,14 @@ typedef struct {
|
|
37
37
|
/* The logger: */
|
38
38
|
VALUE log;
|
39
39
|
|
40
|
-
/*
|
40
|
+
/* The max number of requests that can be processed simultaneously by libcurl. Will be calculated multiplying the
|
41
|
+
max number of connections by the pipeline length: */
|
42
|
+
int limit;
|
43
|
+
|
44
|
+
/* This queue contains the requests that have not yet been sent to libcurl for processing: */
|
45
|
+
VALUE queue;
|
46
|
+
|
47
|
+
/* This hash stores the transfers that are pending. The key of the hash is the request that initiated the transfer,
|
41
48
|
and the value is the transfer itself. */
|
42
49
|
VALUE pending;
|
43
50
|
|
data/lib/ovirtsdk4/connection.rb
CHANGED
@@ -101,8 +101,8 @@ module OvirtSDK4
|
|
101
101
|
# as the names of the headers. If the same header is provided here and in the `headers` parameter of a specific
|
102
102
|
# method call, then the `headers` parameter of the specific method call will have precedence.
|
103
103
|
#
|
104
|
-
# @option opts [Integer] :connections (
|
105
|
-
#
|
104
|
+
# @option opts [Integer] :connections (1) The maximum number of connections to open to the host. The value must
|
105
|
+
# be greater than 0.
|
106
106
|
#
|
107
107
|
# @option opts [Integer] :pipeline (0) The maximum number of request to put in an HTTP pipeline without waiting for
|
108
108
|
# the response. If the value is `0` (the default) then pipelining is disabled.
|
@@ -126,7 +126,7 @@ module OvirtSDK4
|
|
126
126
|
@proxy_username = opts[:proxy_username]
|
127
127
|
@proxy_password = opts[:proxy_password]
|
128
128
|
@headers = opts[:headers]
|
129
|
-
@connections = opts[:connections] ||
|
129
|
+
@connections = opts[:connections] || 1
|
130
130
|
@pipeline = opts[:pipeline] || 0
|
131
131
|
|
132
132
|
# Check that the URL has been provided:
|
@@ -275,9 +275,7 @@ module OvirtSDK4
|
|
275
275
|
def follow_link(object)
|
276
276
|
# Check that the "href" has a value, as it is needed in order to retrieve the representation of the object:
|
277
277
|
href = object.href
|
278
|
-
if href.nil?
|
279
|
-
raise Error, "Can't follow link because the 'href' attribute doesn't have a value"
|
280
|
-
end
|
278
|
+
raise Error, "Can't follow link because the 'href' attribute doesn't have a value" if href.nil?
|
281
279
|
|
282
280
|
# Check that the value of the "href" attribute is compatible with the base URL of the connection:
|
283
281
|
prefix = URI(@url).path
|
@@ -634,7 +632,7 @@ module OvirtSDK4
|
|
634
632
|
# parameter. In order to better support those older versions of the engine we need to check if this parameter is
|
635
633
|
# included in the request, and add the corresponding header.
|
636
634
|
unless request.query.nil?
|
637
|
-
all_content = request.query[
|
635
|
+
all_content = request.query[:all_content]
|
638
636
|
request.headers['All-Content'] = all_content unless all_content.nil?
|
639
637
|
end
|
640
638
|
|
data/lib/ovirtsdk4/service.rb
CHANGED
@@ -175,7 +175,7 @@ module OvirtSDK4
|
|
175
175
|
# Get the values of the options specific to this operation:
|
176
176
|
specs.each do |name, kind|
|
177
177
|
value = opts.delete(name)
|
178
|
-
query[name] = Writer.render(value, kind)
|
178
|
+
query[name] = Writer.render(value, kind) unless value.nil?
|
179
179
|
end
|
180
180
|
|
181
181
|
# Check the remaining options:
|
@@ -224,7 +224,7 @@ module OvirtSDK4
|
|
224
224
|
# Get the values of the options specific to this operation:
|
225
225
|
specs.each do |name, kind|
|
226
226
|
value = opts.delete(name)
|
227
|
-
query[name] = Writer.render(value, kind)
|
227
|
+
query[name] = Writer.render(value, kind) unless value.nil?
|
228
228
|
end
|
229
229
|
|
230
230
|
# Check the remaining options:
|
@@ -274,7 +274,7 @@ module OvirtSDK4
|
|
274
274
|
# Get the values of the options specific to this operation:
|
275
275
|
specs.each do |name, kind|
|
276
276
|
value = opts.delete(name)
|
277
|
-
query[name] = Writer.render(value, kind)
|
277
|
+
query[name] = Writer.render(value, kind) unless value.nil?
|
278
278
|
end
|
279
279
|
|
280
280
|
# Check the remaining options:
|
@@ -321,7 +321,7 @@ module OvirtSDK4
|
|
321
321
|
# Get the values of the options specific to this operation:
|
322
322
|
specs.each do |name, kind|
|
323
323
|
value = opts.delete(name)
|
324
|
-
query[name] = Writer.render(value, kind)
|
324
|
+
query[name] = Writer.render(value, kind) unless value.nil?
|
325
325
|
end
|
326
326
|
|
327
327
|
# Check the remaining options:
|
@@ -407,9 +407,7 @@ module OvirtSDK4
|
|
407
407
|
def internal_read_body(response)
|
408
408
|
# First check if the response body is empty, as it makes no sense to check the content type if there is
|
409
409
|
# no body:
|
410
|
-
if response.body.nil? || response.body.length.zero?
|
411
|
-
connection.raise_error(response, 'The response body is empty')
|
412
|
-
end
|
410
|
+
connection.raise_error(response, 'The response body is empty') if response.body.nil? || response.body.length.zero?
|
413
411
|
|
414
412
|
# Check the content type, as otherwise the parsing will fail, and the resulting error message won't be explicit
|
415
413
|
# about the cause of the problem:
|
data/lib/ovirtsdk4/version.rb
CHANGED
data/lib/ovirtsdk4/writer.rb
CHANGED
@@ -195,9 +195,7 @@ module OvirtSDK4
|
|
195
195
|
begin
|
196
196
|
if object.is_a?(Array)
|
197
197
|
# For arrays we can't decide which tag to use, so the 'root' parameter is mandatory in this case:
|
198
|
-
if root.nil?
|
199
|
-
raise Error, "The 'root' option is mandatory when writing arrays"
|
200
|
-
end
|
198
|
+
raise Error, "The 'root' option is mandatory when writing arrays" if root.nil?
|
201
199
|
|
202
200
|
# Write the root tag, and then recursively call the method to write each of the items of the array:
|
203
201
|
cursor.write_start(root)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ovirt-engine-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.2.
|
4
|
+
version: 4.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Hernandez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -151,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
151
|
requirements:
|
152
152
|
- - ">="
|
153
153
|
- !ruby/object:Gem::Version
|
154
|
-
version: '2.
|
154
|
+
version: '2.1'
|
155
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|