devcycle-ruby-server-sdk 2.0.3 → 2.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 +4 -4
- data/lib/devcycle-ruby-server-sdk/api/devcycle_api.rb +53 -58
- data/lib/devcycle-ruby-server-sdk/localbucketing/bucketing-lib.release.wasm +0 -0
- data/lib/devcycle-ruby-server-sdk/localbucketing/local_bucketing.rb +132 -87
- data/lib/devcycle-ruby-server-sdk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66277b1d5bea79db7794168b2f9be005a3c37ef6ea0dc1d9b0cbf876c5fb1c17
|
4
|
+
data.tar.gz: 94381d579783bffc333ac56391ea530466c796a44ce700c25dc99267409412ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b958f804a26259dadd8f33d46a0326d9c4ca5a8aad5c0a230ffdbb54d0adc7d82714677add429dc5161c855d31a0e1a19fdc0461db70a0945436285160996b2
|
7
|
+
data.tar.gz: 731696f8b1129e8151031d8ede467f646f0c910f747a36dfc9ab54b127db6097eddfcbb35afc2cf1d49da2d53a8c22e20e0d908f9a6371845a7f67e63a84b701
|
@@ -32,8 +32,8 @@ module DevCycle
|
|
32
32
|
@api_client.config.enable_edge_db = @dvc_options.enable_edge_db
|
33
33
|
@api_client.config.logger = @logger
|
34
34
|
else
|
35
|
-
@
|
36
|
-
@event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @
|
35
|
+
@local_bucketing = LocalBucketing.new(@sdkKey, dvc_options, wait_for_init)
|
36
|
+
@event_queue = EventQueue.new(@sdkKey, dvc_options.event_queue_options, @local_bucketing)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -42,15 +42,15 @@ module DevCycle
|
|
42
42
|
@logger.info("Cloud Bucketing does not require closing.")
|
43
43
|
return
|
44
44
|
end
|
45
|
-
if @
|
46
|
-
if !@
|
45
|
+
if @local_bucketing != nil
|
46
|
+
if !@local_bucketing.initialized
|
47
47
|
@logger.info("Awaiting client initialization before closing")
|
48
|
-
while !@
|
48
|
+
while !@local_bucketing.initialized
|
49
49
|
sleep(0.5)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
@
|
53
|
-
@
|
52
|
+
@local_bucketing.close
|
53
|
+
@local_bucketing = nil
|
54
54
|
@logger.info("Closed DevCycle Local Bucketing Engine.")
|
55
55
|
end
|
56
56
|
|
@@ -65,7 +65,7 @@ module DevCycle
|
|
65
65
|
end
|
66
66
|
|
67
67
|
if local_bucketing_initialized?
|
68
|
-
@
|
68
|
+
@local_bucketing.set_client_custom_data(customdata)
|
69
69
|
else
|
70
70
|
@logger.warn("Local bucketing not initialized. Unable to set client custom data.")
|
71
71
|
end
|
@@ -93,8 +93,8 @@ module DevCycle
|
|
93
93
|
return data
|
94
94
|
end
|
95
95
|
|
96
|
-
if local_bucketing_initialized? && @
|
97
|
-
bucketed_config = @
|
96
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
97
|
+
bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
|
98
98
|
bucketed_config.features
|
99
99
|
else
|
100
100
|
{}
|
@@ -176,60 +176,40 @@ module DevCycle
|
|
176
176
|
return data
|
177
177
|
end
|
178
178
|
|
179
|
-
|
180
|
-
|
181
|
-
|
179
|
+
value = default
|
180
|
+
type = determine_variable_type(default)
|
181
|
+
defaulted = true
|
182
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
183
|
+
type_code = variable_type_code_from_type(type)
|
184
|
+
variable_json = variable_for_user(user_data, key, type_code)
|
182
185
|
if variable_json == nil
|
183
186
|
@logger.warn("No variable found for key #{key}, returning default value")
|
184
|
-
|
185
|
-
@event_queue.queue_aggregate_event(variable_event, bucketed_config)
|
186
|
-
|
187
|
-
return Variable.new({
|
188
|
-
key: key,
|
189
|
-
type: determine_variable_type(default),
|
190
|
-
value: default,
|
191
|
-
defaultValue: default,
|
192
|
-
isDefaulted: true
|
193
|
-
})
|
194
|
-
end
|
195
|
-
default_type = determine_variable_type(default)
|
196
|
-
variable_type = variable_json['type']
|
197
|
-
if default_type != variable_type
|
187
|
+
elsif type != variable_json['type']
|
198
188
|
@logger.warn("Type mismatch for variable #{key}, returning default value")
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
return Variable.new({
|
203
|
-
key: key,
|
204
|
-
type: default_type,
|
205
|
-
value: default,
|
206
|
-
defaultValue: default,
|
207
|
-
isDefaulted: true
|
208
|
-
})
|
189
|
+
else
|
190
|
+
value = variable_json['value']
|
191
|
+
defaulted = false
|
209
192
|
end
|
210
|
-
variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_evaluated], target: key })
|
211
|
-
@event_queue.queue_aggregate_event(variable_event, bucketed_config)
|
212
|
-
|
213
|
-
Variable.new({
|
214
|
-
key: key,
|
215
|
-
type: variable_type,
|
216
|
-
value: variable_json['value'],
|
217
|
-
defaultValue: default,
|
218
|
-
isDefaulted: false
|
219
|
-
})
|
220
193
|
else
|
221
194
|
@logger.warn("Local bucketing not initialized, returning default value for variable #{key}")
|
222
195
|
variable_event = Event.new({ type: DevCycle::EventTypes[:agg_variable_defaulted], target: key })
|
196
|
+
bucketed_config = BucketedUserConfig.new({}, {}, {}, {}, {}, {}, [])
|
223
197
|
@event_queue.queue_aggregate_event(variable_event, bucketed_config)
|
224
|
-
|
225
|
-
Variable.new({
|
226
|
-
key: key,
|
227
|
-
type: determine_variable_type(default),
|
228
|
-
value: default,
|
229
|
-
defaultValue: default,
|
230
|
-
isDefaulted: true
|
231
|
-
})
|
232
198
|
end
|
199
|
+
|
200
|
+
Variable.new({
|
201
|
+
key: key,
|
202
|
+
value: value,
|
203
|
+
type: type,
|
204
|
+
defaultValue: default,
|
205
|
+
isDefaulted: defaulted
|
206
|
+
})
|
207
|
+
end
|
208
|
+
|
209
|
+
def variable_for_user(user, key, variable_type_code)
|
210
|
+
json_str = @local_bucketing.variable_for_user(user, key, variable_type_code)
|
211
|
+
return nil if json_str.nil?
|
212
|
+
JSON.parse(json_str)
|
233
213
|
end
|
234
214
|
|
235
215
|
# Get variable by key for user data
|
@@ -319,8 +299,8 @@ module DevCycle
|
|
319
299
|
return data
|
320
300
|
end
|
321
301
|
|
322
|
-
if local_bucketing_initialized? && @
|
323
|
-
bucketed_config = @
|
302
|
+
if local_bucketing_initialized? && @local_bucketing.has_config
|
303
|
+
bucketed_config = @local_bucketing.generate_bucketed_config(user_data)
|
324
304
|
bucketed_config.variables
|
325
305
|
else
|
326
306
|
{}
|
@@ -486,7 +466,7 @@ module DevCycle
|
|
486
466
|
end
|
487
467
|
|
488
468
|
def local_bucketing_initialized?
|
489
|
-
!@
|
469
|
+
!@local_bucketing.nil? && @local_bucketing.initialized
|
490
470
|
end
|
491
471
|
|
492
472
|
def determine_variable_type(variable_value)
|
@@ -502,5 +482,20 @@ module DevCycle
|
|
502
482
|
raise ArgumentError, "Invalid type for variable: #{variable_value}"
|
503
483
|
end
|
504
484
|
end
|
485
|
+
|
486
|
+
def variable_type_code_from_type(type)
|
487
|
+
case type
|
488
|
+
when 'String'
|
489
|
+
@local_bucketing.variable_type_codes[:string]
|
490
|
+
when 'Boolean'
|
491
|
+
@local_bucketing.variable_type_codes[:boolean]
|
492
|
+
when 'Number'
|
493
|
+
@local_bucketing.variable_type_codes[:number]
|
494
|
+
when 'JSON'
|
495
|
+
@local_bucketing.variable_type_codes[:json]
|
496
|
+
else
|
497
|
+
raise ArgumentError.new("Invalid type for variable: #{type}")
|
498
|
+
end
|
499
|
+
end
|
505
500
|
end
|
506
501
|
end
|
Binary file
|
@@ -13,6 +13,7 @@ module DevCycle
|
|
13
13
|
extend T::Sig
|
14
14
|
|
15
15
|
attr_reader :options
|
16
|
+
attr_reader :variable_type_codes
|
16
17
|
attr_accessor :initialized
|
17
18
|
attr_accessor :has_config
|
18
19
|
|
@@ -48,6 +49,8 @@ module DevCycle
|
|
48
49
|
result
|
49
50
|
}
|
50
51
|
|
52
|
+
@@stack_tracer_raise = lambda { |message| raise message }
|
53
|
+
# each method reassigns stack_tracer so the call stack is properly displayed
|
51
54
|
@@stack_tracer = lambda {}
|
52
55
|
|
53
56
|
@@linker.func_new("env", "abort", [:i32, :i32, :i32, :i32], []) do |_caller, messagePtr, filenamePtr, lineNum, colNum|
|
@@ -91,6 +94,13 @@ module DevCycle
|
|
91
94
|
@sdkkey = sdkkey
|
92
95
|
@options = options
|
93
96
|
@logger = options.logger
|
97
|
+
@wasm_mutex = Mutex.new
|
98
|
+
@variable_type_codes = {
|
99
|
+
boolean: @@instance.export("VariableType.Boolean").to_global.get.to_i,
|
100
|
+
string: @@instance.export("VariableType.String").to_global.get.to_i,
|
101
|
+
number: @@instance.export("VariableType.Number").to_global.get.to_i,
|
102
|
+
json: @@instance.export("VariableType.JSON").to_global.get.to_i
|
103
|
+
}
|
94
104
|
set_sdk_key_internal(sdkkey)
|
95
105
|
platform_data = PlatformData.new('server', VERSION, RUBY_VERSION, nil, 'Ruby', Socket.gethostname)
|
96
106
|
set_platform_data(platform_data)
|
@@ -104,123 +114,158 @@ module DevCycle
|
|
104
114
|
|
105
115
|
sig { params(user: UserData).returns(BucketedUserConfig) }
|
106
116
|
def generate_bucketed_config(user)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
@wasm_mutex.synchronize do
|
118
|
+
user_addr = malloc_asc_string(user.to_json)
|
119
|
+
@@stack_tracer = @@stack_tracer_raise
|
120
|
+
config_addr = @@instance.invoke("generateBucketedConfigForUser", @sdkKeyAddr, user_addr)
|
121
|
+
bucketed_config_json = read_asc_string(config_addr)
|
122
|
+
bucketed_config_hash = Oj.load(bucketed_config_json)
|
123
|
+
|
124
|
+
BucketedUserConfig.new(bucketed_config_hash['project'],
|
125
|
+
bucketed_config_hash['environment'],
|
126
|
+
bucketed_config_hash['features'],
|
127
|
+
bucketed_config_hash['featureVariationMap'],
|
128
|
+
bucketed_config_hash['variableVariationMap'],
|
129
|
+
bucketed_config_hash['variables'],
|
130
|
+
bucketed_config_hash['knownVariableKeys'])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(user: UserData, key: String, variable_type: Integer).returns(T.nilable(String)) }
|
135
|
+
def variable_for_user(user, key, variable_type)
|
136
|
+
@wasm_mutex.synchronize do
|
137
|
+
user_addr = malloc_asc_string(user.to_json)
|
138
|
+
key_addr = malloc_asc_string(key)
|
139
|
+
@@stack_tracer = @@stack_tracer_raise
|
140
|
+
var_addr = @@instance.invoke("variableForUser", @sdkKeyAddr, user_addr, key_addr, variable_type, 1)
|
141
|
+
read_asc_string(var_addr)
|
142
|
+
end
|
120
143
|
end
|
121
144
|
|
122
145
|
sig { returns(T::Array[EventsPayload]) }
|
123
146
|
def flush_event_queue
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
147
|
+
@wasm_mutex.synchronize do
|
148
|
+
@@stack_tracer = @@stack_tracer_raise
|
149
|
+
payload_addr = @@instance.invoke("flushEventQueue", @sdkKeyAddr)
|
150
|
+
raw_json = read_asc_string(payload_addr)
|
151
|
+
raw_payloads = Oj.load(raw_json)
|
152
|
+
|
153
|
+
if raw_payloads == nil
|
154
|
+
return []
|
155
|
+
end
|
156
|
+
raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
|
131
157
|
end
|
132
|
-
raw_payloads.map { |raw_payload| EventsPayload.new(raw_payload["records"], raw_payload["payloadId"], raw_payload["eventCount"]) }
|
133
158
|
end
|
134
159
|
|
135
160
|
sig { returns(Integer) }
|
136
161
|
def check_event_queue_size
|
137
|
-
|
138
|
-
|
162
|
+
@wasm_mutex.synchronize do
|
163
|
+
@@stack_tracer = @@stack_tracer_raise
|
164
|
+
@@instance.invoke("eventQueueSize", @sdkKeyAddr)
|
165
|
+
end
|
139
166
|
end
|
140
167
|
|
141
168
|
sig { params(payload_id: String).returns(NilClass) }
|
142
169
|
def on_payload_success(payload_id)
|
143
|
-
|
144
|
-
|
145
|
-
|
170
|
+
@wasm_mutex.synchronize do
|
171
|
+
payload_addr = malloc_asc_string(payload_id)
|
172
|
+
@@stack_tracer = @@stack_tracer_raise
|
173
|
+
@@instance.invoke("onPayloadSuccess", @sdkKeyAddr, payload_addr)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
sig { params(payload_id: String, retryable: Object).returns(NilClass) }
|
178
|
+
def on_payload_failure(payload_id, retryable)
|
179
|
+
@wasm_mutex.synchronize do
|
180
|
+
payload_addr = malloc_asc_string(payload_id)
|
181
|
+
@@stack_tracer = @@stack_tracer_raise
|
182
|
+
@@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
|
183
|
+
end
|
146
184
|
end
|
147
185
|
|
148
186
|
sig { params(user: UserData, event: Event).returns(NilClass) }
|
149
187
|
def queue_event(user, event)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
188
|
+
@wasm_mutex.synchronize do
|
189
|
+
begin
|
190
|
+
user_addr = malloc_asc_string(Oj.dump(user))
|
191
|
+
asc_pin(user_addr)
|
192
|
+
event_addr = malloc_asc_string(Oj.dump(event))
|
193
|
+
@@stack_tracer = @@stack_tracer_raise
|
194
|
+
@@instance.invoke("queueEvent", @sdkKeyAddr, user_addr, event_addr)
|
195
|
+
ensure
|
196
|
+
asc_unpin(user_addr)
|
197
|
+
end
|
158
198
|
end
|
159
199
|
end
|
160
200
|
|
161
201
|
sig { params(event: Event, bucketeduser: T.nilable(BucketedUserConfig)).returns(NilClass) }
|
162
202
|
def queue_aggregate_event(event, bucketeduser)
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
bucketeduser.
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
203
|
+
@wasm_mutex.synchronize do
|
204
|
+
begin
|
205
|
+
variable_variation_map =
|
206
|
+
if !bucketeduser.nil?
|
207
|
+
bucketeduser.variable_variation_map
|
208
|
+
else
|
209
|
+
{}
|
210
|
+
end
|
211
|
+
varmap_addr = malloc_asc_string(Oj.dump(variable_variation_map))
|
212
|
+
asc_pin(varmap_addr)
|
213
|
+
event_addr = malloc_asc_string(Oj.dump(event))
|
214
|
+
@@stack_tracer = @@stack_tracer_raise
|
215
|
+
@@instance.invoke("queueAggregateEvent", @sdkKeyAddr, event_addr, varmap_addr)
|
216
|
+
ensure
|
217
|
+
asc_unpin(varmap_addr)
|
218
|
+
end
|
177
219
|
end
|
178
220
|
end
|
179
221
|
|
180
|
-
sig { params(payload_id: String, retryable: Object).returns(NilClass) }
|
181
|
-
def on_payload_failure(payload_id, retryable)
|
182
|
-
payload_addr = malloc_asc_string(payload_id)
|
183
|
-
@@stack_tracer = lambda { |message| raise message }
|
184
|
-
@@instance.invoke("onPayloadFailure", @sdkKeyAddr, payload_addr, retryable ? 1 : 0)
|
185
|
-
end
|
186
|
-
|
187
222
|
sig { params(config: String).returns(NilClass) }
|
188
223
|
def store_config(config)
|
189
|
-
|
190
|
-
|
191
|
-
|
224
|
+
@wasm_mutex.synchronize do
|
225
|
+
config_addr = malloc_asc_string(config)
|
226
|
+
@@stack_tracer = @@stack_tracer_raise
|
227
|
+
@@instance.invoke("setConfigData", @sdkKeyAddr, config_addr)
|
228
|
+
end
|
192
229
|
end
|
193
230
|
|
194
231
|
sig { params(options: EventQueueOptions).returns(NilClass) }
|
195
232
|
def init_event_queue(options)
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
233
|
+
@wasm_mutex.synchronize do
|
234
|
+
options_json = Oj.dump(options)
|
235
|
+
options_addr = malloc_asc_string(options_json)
|
236
|
+
@@stack_tracer = @@stack_tracer_raise
|
237
|
+
@@instance.invoke("initEventQueue", @sdkKeyAddr, options_addr)
|
238
|
+
end
|
200
239
|
end
|
201
240
|
|
202
241
|
sig { params(customdata: Hash).returns(NilClass) }
|
203
242
|
def set_client_custom_data(customdata)
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
243
|
+
@wasm_mutex.synchronize do
|
244
|
+
customdata_json = Oj.dump(customdata)
|
245
|
+
customdata_addr = malloc_asc_string(customdata_json)
|
246
|
+
@@stack_tracer = @@stack_tracer_raise
|
247
|
+
@@instance.invoke("setClientCustomData", @sdkKeyAddr, customdata_addr)
|
248
|
+
end
|
208
249
|
end
|
209
250
|
|
210
251
|
private
|
211
252
|
|
212
253
|
sig { params(platformdata: PlatformData).returns(NilClass) }
|
213
254
|
def set_platform_data(platformdata)
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
255
|
+
@wasm_mutex.synchronize do
|
256
|
+
platformdata_json = Oj.dump(platformdata)
|
257
|
+
platformdata_addr = malloc_asc_string(platformdata_json)
|
258
|
+
@@stack_tracer = @@stack_tracer_raise
|
259
|
+
@@instance.invoke("setPlatformData", platformdata_addr)
|
260
|
+
end
|
218
261
|
end
|
219
262
|
|
220
263
|
def set_sdk_key_internal(sdkKey)
|
221
|
-
|
222
|
-
|
223
|
-
|
264
|
+
@wasm_mutex.synchronize do
|
265
|
+
addr = malloc_asc_string(sdkKey)
|
266
|
+
@sdkKeyAddr = addr
|
267
|
+
asc_pin(addr)
|
268
|
+
end
|
224
269
|
end
|
225
270
|
|
226
271
|
def asc_pin(addr)
|
@@ -236,7 +281,7 @@ module DevCycle
|
|
236
281
|
sig { params(string: String).returns(Integer) }
|
237
282
|
def malloc_asc_string(string)
|
238
283
|
wasm_object_id = 1
|
239
|
-
@@stack_tracer =
|
284
|
+
@@stack_tracer = @@stack_tracer_raise
|
240
285
|
wasm_new = @@instance.export("__new").to_func
|
241
286
|
utf8_bytes = string.bytes
|
242
287
|
byte_len = utf8_bytes.length
|
@@ -244,8 +289,8 @@ module DevCycle
|
|
244
289
|
start_addr = wasm_new.call(byte_len * 2, wasm_object_id)
|
245
290
|
i = 0
|
246
291
|
while i < byte_len
|
247
|
-
@@stack_tracer =
|
248
|
-
@@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('
|
292
|
+
@@stack_tracer = @@stack_tracer_raise
|
293
|
+
@@memory.write(start_addr + (i * 2), [utf8_bytes[i]].pack('c'))
|
249
294
|
i += 1
|
250
295
|
end
|
251
296
|
start_addr
|
@@ -253,23 +298,23 @@ module DevCycle
|
|
253
298
|
|
254
299
|
# @param [Integer] address start address of string.
|
255
300
|
# @return [String] resulting string
|
256
|
-
sig { params(address: Integer).returns(String) }
|
301
|
+
sig { params(address: Integer).returns(T.nilable(String)) }
|
257
302
|
def read_asc_string(address)
|
258
|
-
|
303
|
+
if address == 0
|
304
|
+
@logger.debug("null address passed to read_asc_string")
|
305
|
+
return nil
|
306
|
+
end
|
307
|
+
|
308
|
+
@@stack_tracer = @@stack_tracer_raise
|
259
309
|
raw_bytes = @@memory.read(address - 4, 4).bytes.reverse
|
260
310
|
len = 0
|
261
311
|
raw_bytes.each { |j|
|
262
312
|
len = (len << 8) + (j & 0xFF)
|
263
313
|
}
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
result += @@memory.read(address + i, 1)
|
269
|
-
i += 2
|
270
|
-
end
|
271
|
-
result
|
314
|
+
|
315
|
+
@@stack_tracer = @@stack_tracer_raise
|
316
|
+
result = @@memory.read(address, len).bytes
|
317
|
+
result.select.with_index { |_, i| i.even? }.pack('c*')
|
272
318
|
end
|
273
319
|
end
|
274
320
|
end
|
275
|
-
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devcycle-ruby-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DevCycleHQ
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|