launchdarkly-server-sdk 5.5.12 → 5.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ldrelease/config.yml +17 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/lib/ldclient-rb/evaluation.rb +7 -17
- data/lib/ldclient-rb/events.rb +1 -0
- data/lib/ldclient-rb/impl/event_factory.rb +98 -0
- data/lib/ldclient-rb/ldclient.rb +36 -29
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_spec.rb +38 -40
- data/spec/events_spec.rb +2 -1
- data/spec/ldclient_spec.rb +82 -2
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 502749fe2b55f4a3116ea223872835e76f7f698b
|
4
|
+
data.tar.gz: 9ab5eae90b672dc39db4c3487bd67aa32878b467
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4f9f6642ab989aab6069924fc8b9f543123672cf475be58b72e1410a2563f246ce40d0cfef34dad0c77c37e28d695d05368c719df889eb5766d3618b19848b3
|
7
|
+
data.tar.gz: 8c3af4780de0251380f194c8e4c4c3ce359f2a1612763e45a2a8b2eadab3d0dd20f83d5f0d60f8efb45759f5f8a4af7ccc6143085d2999065e4ca8791cf50291
|
@@ -0,0 +1,17 @@
|
|
1
|
+
repo:
|
2
|
+
public: ruby-server-sdk
|
3
|
+
private: ruby-server-sdk-private
|
4
|
+
|
5
|
+
publications:
|
6
|
+
- url: https://rubygems.org/gems/launchdarkly-server-sdk
|
7
|
+
description: RubyGems
|
8
|
+
- url: https://www.rubydoc.info/gems/launchdarkly-server-sdk
|
9
|
+
description: documentation
|
10
|
+
|
11
|
+
template:
|
12
|
+
name: ruby
|
13
|
+
env:
|
14
|
+
LD_SKIP_DATABASE_TESTS: 1 # Don't run Redis/Consul/DynamoDB tests in release; they are run in CI
|
15
|
+
|
16
|
+
sdk:
|
17
|
+
displayName: "Ruby"
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
All notable changes to the LaunchDarkly Ruby SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [5.6.0] - 2019-08-20
|
6
|
+
### Added:
|
7
|
+
- Added support for upcoming LaunchDarkly experimentation features. See `LDClient.track()`.
|
8
|
+
|
5
9
|
## [5.5.12] - 2019-08-05
|
6
10
|
### Fixed:
|
7
11
|
- Under conditions where analytics events are being generated at an extremely high rate (for instance, if an application is evaluating a flag repeatedly in a tight loop on many threads), it was possible for the internal event processing logic to fall behind on processing the events, causing them to use more and more memory. The logic has been changed to drop events if necessary so that besides the existing limit on the number of events waiting to be sent to LaunchDarkly (`config.capacity`), the same limit also applies on the number of events that are waiting to be processed by the worker thread that decides whether or not to send them to LaunchDarkly. If that limit is exceeded, this warning message will be logged once: "Events are being produced faster than they can be processed; some events will be dropped". Under normal conditions this should never happen; this change is meant to avoid a concurrency bottleneck in applications that are already so busy that thread starvation is likely.
|
data/Gemfile.lock
CHANGED
@@ -199,7 +199,7 @@ module LaunchDarkly
|
|
199
199
|
|
200
200
|
# Evaluates a feature flag and returns an EvalResult. The result.value will be nil if the flag returns
|
201
201
|
# the default value. Error conditions produce a result with an error reason, not an exception.
|
202
|
-
def evaluate(flag, user, store, logger)
|
202
|
+
def evaluate(flag, user, store, logger, event_factory)
|
203
203
|
if user.nil? || user[:key].nil?
|
204
204
|
return EvalResult.new(error_result('USER_NOT_SPECIFIED'), [])
|
205
205
|
end
|
@@ -207,16 +207,16 @@ module LaunchDarkly
|
|
207
207
|
sanitized_user = Util.stringify_attrs(user, USER_ATTRS_TO_STRINGIFY_FOR_EVALUATION)
|
208
208
|
|
209
209
|
events = []
|
210
|
-
detail = eval_internal(flag, sanitized_user, store, events, logger)
|
210
|
+
detail = eval_internal(flag, sanitized_user, store, events, logger, event_factory)
|
211
211
|
return EvalResult.new(detail, events)
|
212
212
|
end
|
213
213
|
|
214
|
-
def eval_internal(flag, user, store, events, logger)
|
214
|
+
def eval_internal(flag, user, store, events, logger, event_factory)
|
215
215
|
if !flag[:on]
|
216
216
|
return get_off_value(flag, { kind: 'OFF' }, logger)
|
217
217
|
end
|
218
218
|
|
219
|
-
prereq_failure_reason = check_prerequisites(flag, user, store, events, logger)
|
219
|
+
prereq_failure_reason = check_prerequisites(flag, user, store, events, logger, event_factory)
|
220
220
|
if !prereq_failure_reason.nil?
|
221
221
|
return get_off_value(flag, prereq_failure_reason, logger)
|
222
222
|
end
|
@@ -249,7 +249,7 @@ module LaunchDarkly
|
|
249
249
|
return EvaluationDetail.new(nil, nil, { kind: 'FALLTHROUGH' })
|
250
250
|
end
|
251
251
|
|
252
|
-
def check_prerequisites(flag, user, store, events, logger)
|
252
|
+
def check_prerequisites(flag, user, store, events, logger, event_factory)
|
253
253
|
(flag[:prerequisites] || []).each do |prerequisite|
|
254
254
|
prereq_ok = true
|
255
255
|
prereq_key = prerequisite[:key]
|
@@ -260,23 +260,13 @@ module LaunchDarkly
|
|
260
260
|
prereq_ok = false
|
261
261
|
else
|
262
262
|
begin
|
263
|
-
prereq_res = eval_internal(prereq_flag, user, store, events, logger)
|
263
|
+
prereq_res = eval_internal(prereq_flag, user, store, events, logger, event_factory)
|
264
264
|
# Note that if the prerequisite flag is off, we don't consider it a match no matter what its
|
265
265
|
# off variation was. But we still need to evaluate it in order to generate an event.
|
266
266
|
if !prereq_flag[:on] || prereq_res.variation_index != prerequisite[:variation]
|
267
267
|
prereq_ok = false
|
268
268
|
end
|
269
|
-
event =
|
270
|
-
kind: "feature",
|
271
|
-
key: prereq_key,
|
272
|
-
user: user,
|
273
|
-
variation: prereq_res.variation_index,
|
274
|
-
value: prereq_res.value,
|
275
|
-
version: prereq_flag[:version],
|
276
|
-
prereqOf: flag[:key],
|
277
|
-
trackEvents: prereq_flag[:trackEvents],
|
278
|
-
debugEventsUntilDate: prereq_flag[:debugEventsUntilDate]
|
279
|
-
}
|
269
|
+
event = event_factory.new_eval_event(prereq_flag, user, prereq_res, nil, flag)
|
280
270
|
events.push(event)
|
281
271
|
rescue => exn
|
282
272
|
Util.log_exception(logger, "Error evaluating prerequisite flag \"#{prereq_key}\" for flag \"#{flag[:key]}\"", exn)
|
data/lib/ldclient-rb/events.rb
CHANGED
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
module LaunchDarkly
|
3
|
+
module Impl
|
4
|
+
# Event constructors are centralized here to avoid mistakes and repetitive logic.
|
5
|
+
# The LDClient owns two instances of EventFactory: one that always embeds evaluation reasons
|
6
|
+
# in the events (for when variation_detail is called) and one that doesn't.
|
7
|
+
#
|
8
|
+
# Note that these methods do not set the "creationDate" property, because in the Ruby client,
|
9
|
+
# that is done by EventProcessor.add_event().
|
10
|
+
class EventFactory
|
11
|
+
def initialize(with_reasons)
|
12
|
+
@with_reasons = with_reasons
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_eval_event(flag, user, detail, default_value, prereq_of_flag = nil)
|
16
|
+
add_experiment_data = is_experiment(flag, detail.reason)
|
17
|
+
e = {
|
18
|
+
kind: 'feature',
|
19
|
+
key: flag[:key],
|
20
|
+
user: user,
|
21
|
+
variation: detail.variation_index,
|
22
|
+
value: detail.value,
|
23
|
+
default: default_value,
|
24
|
+
version: flag[:version]
|
25
|
+
}
|
26
|
+
# the following properties are handled separately so we don't waste bandwidth on unused keys
|
27
|
+
e[:trackEvents] = true if add_experiment_data || flag[:trackEvents]
|
28
|
+
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
29
|
+
e[:prereqOf] = prereq_of_flag[:key] if !prereq_of_flag.nil?
|
30
|
+
e[:reason] = detail.reason if add_experiment_data || @with_reasons
|
31
|
+
e
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_default_event(flag, user, default_value, reason)
|
35
|
+
e = {
|
36
|
+
kind: 'feature',
|
37
|
+
key: flag[:key],
|
38
|
+
user: user,
|
39
|
+
value: default_value,
|
40
|
+
default: default_value,
|
41
|
+
version: flag[:version]
|
42
|
+
}
|
43
|
+
e[:trackEvents] = true if flag[:trackEvents]
|
44
|
+
e[:debugEventsUntilDate] = flag[:debugEventsUntilDate] if flag[:debugEventsUntilDate]
|
45
|
+
e[:reason] = reason if @with_reasons
|
46
|
+
e
|
47
|
+
end
|
48
|
+
|
49
|
+
def new_unknown_flag_event(key, user, default_value, reason)
|
50
|
+
e = {
|
51
|
+
kind: 'feature',
|
52
|
+
key: key,
|
53
|
+
user: user,
|
54
|
+
value: default_value,
|
55
|
+
default: default_value
|
56
|
+
}
|
57
|
+
e[:reason] = reason if @with_reasons
|
58
|
+
e
|
59
|
+
end
|
60
|
+
|
61
|
+
def new_identify_event(user)
|
62
|
+
{
|
63
|
+
kind: 'identify',
|
64
|
+
key: user[:key],
|
65
|
+
user: user
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def new_custom_event(event_name, user, data, metric_value)
|
70
|
+
e = {
|
71
|
+
kind: 'custom',
|
72
|
+
key: event_name,
|
73
|
+
user: user
|
74
|
+
}
|
75
|
+
e[:data] = data if !data.nil?
|
76
|
+
e[:metricValue] = metric_value if !metric_value.nil?
|
77
|
+
e
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def is_experiment(flag, reason)
|
83
|
+
return false if !reason
|
84
|
+
case reason[:kind]
|
85
|
+
when 'RULE_MATCH'
|
86
|
+
index = reason[:ruleIndex]
|
87
|
+
if !index.nil?
|
88
|
+
rules = flag[:rules] || []
|
89
|
+
return index >= 0 && index < rules.length && rules[index][:trackEvents]
|
90
|
+
end
|
91
|
+
when 'FALLTHROUGH'
|
92
|
+
return !!flag[:trackEventsFallthrough]
|
93
|
+
end
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/ldclient-rb/ldclient.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "ldclient-rb/impl/event_factory"
|
1
2
|
require "ldclient-rb/impl/store_client_wrapper"
|
2
3
|
require "concurrent/atomics"
|
3
4
|
require "digest/sha1"
|
@@ -13,6 +14,7 @@ module LaunchDarkly
|
|
13
14
|
#
|
14
15
|
class LDClient
|
15
16
|
include Evaluation
|
17
|
+
include Impl
|
16
18
|
#
|
17
19
|
# Creates a new client instance that connects to LaunchDarkly. A custom
|
18
20
|
# configuration parameter can also supplied to specify advanced options,
|
@@ -32,6 +34,9 @@ module LaunchDarkly
|
|
32
34
|
def initialize(sdk_key, config = Config.default, wait_for_sec = 5)
|
33
35
|
@sdk_key = sdk_key
|
34
36
|
|
37
|
+
@event_factory_default = EventFactory.new(false)
|
38
|
+
@event_factory_with_reasons = EventFactory.new(true)
|
39
|
+
|
35
40
|
# We need to wrap the feature store object with a FeatureStoreClientWrapper in order to add
|
36
41
|
# some necessary logic around updates. Unfortunately, we have code elsewhere that accesses
|
37
42
|
# the feature store through the Config object, so we need to make a new Config that uses
|
@@ -165,7 +170,7 @@ module LaunchDarkly
|
|
165
170
|
# @return the variation to show the user, or the default value if there's an an error
|
166
171
|
#
|
167
172
|
def variation(key, user, default)
|
168
|
-
evaluate_internal(key, user, default,
|
173
|
+
evaluate_internal(key, user, default, @event_factory_default).value
|
169
174
|
end
|
170
175
|
|
171
176
|
#
|
@@ -192,7 +197,7 @@ module LaunchDarkly
|
|
192
197
|
# @return [EvaluationDetail] an object describing the result
|
193
198
|
#
|
194
199
|
def variation_detail(key, user, default)
|
195
|
-
evaluate_internal(key, user, default,
|
200
|
+
evaluate_internal(key, user, default, @event_factory_with_reasons)
|
196
201
|
end
|
197
202
|
|
198
203
|
#
|
@@ -215,7 +220,8 @@ module LaunchDarkly
|
|
215
220
|
@config.logger.warn("Identify called with nil user or nil user key!")
|
216
221
|
return
|
217
222
|
end
|
218
|
-
|
223
|
+
sanitize_user(user)
|
224
|
+
@event_processor.add_event(@event_factory_default.new_identify_event(user))
|
219
225
|
end
|
220
226
|
|
221
227
|
#
|
@@ -225,18 +231,28 @@ module LaunchDarkly
|
|
225
231
|
# Note that event delivery is asynchronous, so the event may not actually be sent
|
226
232
|
# until later; see {#flush}.
|
227
233
|
#
|
234
|
+
# As of this version’s release date, the LaunchDarkly service does not support the `metricValue`
|
235
|
+
# parameter. As a result, specifying `metricValue` will not yet produce any different behavior
|
236
|
+
# from omitting it. Refer to the [SDK reference guide](https://docs.launchdarkly.com/docs/ruby-sdk-reference#section-track)
|
237
|
+
# for the latest status.
|
238
|
+
#
|
228
239
|
# @param event_name [String] The name of the event
|
229
240
|
# @param user [Hash] The user to register; this can have all the same user properties
|
230
241
|
# described in {#variation}
|
231
|
-
# @param data [Hash]
|
242
|
+
# @param data [Hash] An optional hash containing any additional data associated with the event
|
243
|
+
# @param metric_value [Number] A numeric value used by the LaunchDarkly experimentation
|
244
|
+
# feature in numeric custom metrics. Can be omitted if this event is used by only
|
245
|
+
# non-numeric metrics. This field will also be returned as part of the custom event
|
246
|
+
# for Data Export.
|
232
247
|
# @return [void]
|
233
248
|
#
|
234
|
-
def track(event_name, user, data)
|
249
|
+
def track(event_name, user, data = nil, metric_value = nil)
|
235
250
|
if !user || user[:key].nil?
|
236
251
|
@config.logger.warn("Track called with nil user or nil user key!")
|
237
252
|
return
|
238
253
|
end
|
239
|
-
|
254
|
+
sanitize_user(user)
|
255
|
+
@event_processor.add_event(@event_factory_default.new_custom_event(event_name, user, data, metric_value))
|
240
256
|
end
|
241
257
|
|
242
258
|
#
|
@@ -294,7 +310,7 @@ module LaunchDarkly
|
|
294
310
|
next
|
295
311
|
end
|
296
312
|
begin
|
297
|
-
result = evaluate(f, user, @store, @config.logger)
|
313
|
+
result = evaluate(f, user, @store, @config.logger, @event_factory_default)
|
298
314
|
state.add_flag(f, result.detail.value, result.detail.variation_index, with_reasons ? result.detail.reason : nil,
|
299
315
|
details_only_if_tracked)
|
300
316
|
rescue => exn
|
@@ -334,7 +350,7 @@ module LaunchDarkly
|
|
334
350
|
end
|
335
351
|
|
336
352
|
# @return [EvaluationDetail]
|
337
|
-
def evaluate_internal(key, user, default,
|
353
|
+
def evaluate_internal(key, user, default, event_factory)
|
338
354
|
if @config.offline?
|
339
355
|
return error_result('CLIENT_NOT_READY', default)
|
340
356
|
end
|
@@ -344,8 +360,9 @@ module LaunchDarkly
|
|
344
360
|
@config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
|
345
361
|
else
|
346
362
|
@config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
|
347
|
-
|
348
|
-
|
363
|
+
detail = error_result('CLIENT_NOT_READY', default)
|
364
|
+
@event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
|
365
|
+
return detail
|
349
366
|
end
|
350
367
|
end
|
351
368
|
|
@@ -354,20 +371,19 @@ module LaunchDarkly
|
|
354
371
|
if feature.nil?
|
355
372
|
@config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
|
356
373
|
detail = error_result('FLAG_NOT_FOUND', default)
|
357
|
-
@event_processor.add_event(
|
358
|
-
reason: include_reasons_in_events ? detail.reason : nil)
|
374
|
+
@event_processor.add_event(event_factory.new_unknown_flag_event(key, user, default, detail.reason))
|
359
375
|
return detail
|
360
376
|
end
|
361
377
|
|
362
378
|
unless user
|
363
379
|
@config.logger.error { "[LDClient] Must specify user" }
|
364
380
|
detail = error_result('USER_NOT_SPECIFIED', default)
|
365
|
-
@event_processor.add_event(
|
381
|
+
@event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
|
366
382
|
return detail
|
367
383
|
end
|
368
384
|
|
369
385
|
begin
|
370
|
-
res = evaluate(feature, user, @store, @config.logger
|
386
|
+
res = evaluate(feature, user, @store, @config.logger, event_factory)
|
371
387
|
if !res.events.nil?
|
372
388
|
res.events.each do |event|
|
373
389
|
@event_processor.add_event(event)
|
@@ -377,29 +393,20 @@ module LaunchDarkly
|
|
377
393
|
if detail.default_value?
|
378
394
|
detail = EvaluationDetail.new(default, nil, detail.reason)
|
379
395
|
end
|
380
|
-
@event_processor.add_event(
|
396
|
+
@event_processor.add_event(event_factory.new_eval_event(feature, user, detail, default))
|
381
397
|
return detail
|
382
398
|
rescue => exn
|
383
399
|
Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
|
384
400
|
detail = error_result('EXCEPTION', default)
|
385
|
-
@event_processor.add_event(
|
401
|
+
@event_processor.add_event(event_factory.new_default_event(feature, user, default, detail.reason))
|
386
402
|
return detail
|
387
403
|
end
|
388
404
|
end
|
389
405
|
|
390
|
-
def
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
user: user,
|
395
|
-
variation: detail.variation_index,
|
396
|
-
value: detail.value,
|
397
|
-
default: default,
|
398
|
-
version: flag[:version],
|
399
|
-
trackEvents: flag[:trackEvents],
|
400
|
-
debugEventsUntilDate: flag[:debugEventsUntilDate],
|
401
|
-
reason: with_reasons ? detail.reason : nil
|
402
|
-
}
|
406
|
+
def sanitize_user(user)
|
407
|
+
if user[:key]
|
408
|
+
user[:key] = user[:key].to_s
|
409
|
+
end
|
403
410
|
end
|
404
411
|
end
|
405
412
|
|
data/lib/ldclient-rb/version.rb
CHANGED
data/spec/evaluation_spec.rb
CHANGED
@@ -7,6 +7,8 @@ describe LaunchDarkly::Evaluation do
|
|
7
7
|
|
8
8
|
let(:features) { LaunchDarkly::InMemoryFeatureStore.new }
|
9
9
|
|
10
|
+
let(:factory) { LaunchDarkly::Impl::EventFactory.new(false) }
|
11
|
+
|
10
12
|
let(:user) {
|
11
13
|
{
|
12
14
|
key: "userkey",
|
@@ -36,7 +38,7 @@ describe LaunchDarkly::Evaluation do
|
|
36
38
|
}
|
37
39
|
user = { key: 'x' }
|
38
40
|
detail = LaunchDarkly::EvaluationDetail.new('b', 1, { kind: 'OFF' })
|
39
|
-
result = evaluate(flag, user, features, logger)
|
41
|
+
result = evaluate(flag, user, features, logger, factory)
|
40
42
|
expect(result.detail).to eq(detail)
|
41
43
|
expect(result.events).to eq([])
|
42
44
|
end
|
@@ -50,7 +52,7 @@ describe LaunchDarkly::Evaluation do
|
|
50
52
|
}
|
51
53
|
user = { key: 'x' }
|
52
54
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'OFF' })
|
53
|
-
result = evaluate(flag, user, features, logger)
|
55
|
+
result = evaluate(flag, user, features, logger, factory)
|
54
56
|
expect(result.detail).to eq(detail)
|
55
57
|
expect(result.events).to eq([])
|
56
58
|
end
|
@@ -66,7 +68,7 @@ describe LaunchDarkly::Evaluation do
|
|
66
68
|
user = { key: 'x' }
|
67
69
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
68
70
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
69
|
-
result = evaluate(flag, user, features, logger)
|
71
|
+
result = evaluate(flag, user, features, logger, factory)
|
70
72
|
expect(result.detail).to eq(detail)
|
71
73
|
expect(result.events).to eq([])
|
72
74
|
end
|
@@ -82,7 +84,7 @@ describe LaunchDarkly::Evaluation do
|
|
82
84
|
user = { key: 'x' }
|
83
85
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
84
86
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
85
|
-
result = evaluate(flag, user, features, logger)
|
87
|
+
result = evaluate(flag, user, features, logger, factory)
|
86
88
|
expect(result.detail).to eq(detail)
|
87
89
|
expect(result.events).to eq([])
|
88
90
|
end
|
@@ -99,7 +101,7 @@ describe LaunchDarkly::Evaluation do
|
|
99
101
|
user = { key: 'x' }
|
100
102
|
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
|
101
103
|
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'badfeature' })
|
102
|
-
result = evaluate(flag, user, features, logger)
|
104
|
+
result = evaluate(flag, user, features, logger, factory)
|
103
105
|
expect(result.detail).to eq(detail)
|
104
106
|
expect(result.events).to eq([])
|
105
107
|
end
|
@@ -127,10 +129,9 @@ describe LaunchDarkly::Evaluation do
|
|
127
129
|
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
|
128
130
|
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
|
129
131
|
events_should_be = [{
|
130
|
-
kind: 'feature', key: 'feature1', user: user,
|
131
|
-
trackEvents: nil, debugEventsUntilDate: nil
|
132
|
+
kind: 'feature', key: 'feature1', user: user, value: nil, default: nil, variation: nil, version: 2, prereqOf: 'feature0'
|
132
133
|
}]
|
133
|
-
result = evaluate(flag, user, features, logger)
|
134
|
+
result = evaluate(flag, user, features, logger, factory)
|
134
135
|
expect(result.detail).to eq(detail)
|
135
136
|
expect(result.events).to eq(events_should_be)
|
136
137
|
end
|
@@ -159,10 +160,9 @@ describe LaunchDarkly::Evaluation do
|
|
159
160
|
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
|
160
161
|
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
|
161
162
|
events_should_be = [{
|
162
|
-
kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0'
|
163
|
-
trackEvents: nil, debugEventsUntilDate: nil
|
163
|
+
kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
|
164
164
|
}]
|
165
|
-
result = evaluate(flag, user, features, logger)
|
165
|
+
result = evaluate(flag, user, features, logger, factory)
|
166
166
|
expect(result.detail).to eq(detail)
|
167
167
|
expect(result.events).to eq(events_should_be)
|
168
168
|
end
|
@@ -189,10 +189,9 @@ describe LaunchDarkly::Evaluation do
|
|
189
189
|
detail = LaunchDarkly::EvaluationDetail.new('b', 1,
|
190
190
|
{ kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' })
|
191
191
|
events_should_be = [{
|
192
|
-
kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', version: 2, prereqOf: 'feature0'
|
193
|
-
trackEvents: nil, debugEventsUntilDate: nil
|
192
|
+
kind: 'feature', key: 'feature1', user: user, variation: 0, value: 'd', default: nil, version: 2, prereqOf: 'feature0'
|
194
193
|
}]
|
195
|
-
result = evaluate(flag, user, features, logger)
|
194
|
+
result = evaluate(flag, user, features, logger, factory)
|
196
195
|
expect(result.detail).to eq(detail)
|
197
196
|
expect(result.events).to eq(events_should_be)
|
198
197
|
end
|
@@ -218,10 +217,9 @@ describe LaunchDarkly::Evaluation do
|
|
218
217
|
user = { key: 'x' }
|
219
218
|
detail = LaunchDarkly::EvaluationDetail.new('a', 0, { kind: 'FALLTHROUGH' })
|
220
219
|
events_should_be = [{
|
221
|
-
kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', version: 2, prereqOf: 'feature0'
|
222
|
-
trackEvents: nil, debugEventsUntilDate: nil
|
220
|
+
kind: 'feature', key: 'feature1', user: user, variation: 1, value: 'e', default: nil, version: 2, prereqOf: 'feature0'
|
223
221
|
}]
|
224
|
-
result = evaluate(flag, user, features, logger)
|
222
|
+
result = evaluate(flag, user, features, logger, factory)
|
225
223
|
expect(result.detail).to eq(detail)
|
226
224
|
expect(result.events).to eq(events_should_be)
|
227
225
|
end
|
@@ -236,7 +234,7 @@ describe LaunchDarkly::Evaluation do
|
|
236
234
|
}
|
237
235
|
user = { key: 'userkey' }
|
238
236
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
239
|
-
result = evaluate(flag, user, features, logger)
|
237
|
+
result = evaluate(flag, user, features, logger, factory)
|
240
238
|
expect(result.detail).to eq(detail)
|
241
239
|
expect(result.events).to eq([])
|
242
240
|
end
|
@@ -251,7 +249,7 @@ describe LaunchDarkly::Evaluation do
|
|
251
249
|
}
|
252
250
|
user = { key: 'userkey' }
|
253
251
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
254
|
-
result = evaluate(flag, user, features, logger)
|
252
|
+
result = evaluate(flag, user, features, logger, factory)
|
255
253
|
expect(result.detail).to eq(detail)
|
256
254
|
expect(result.events).to eq([])
|
257
255
|
end
|
@@ -266,7 +264,7 @@ describe LaunchDarkly::Evaluation do
|
|
266
264
|
}
|
267
265
|
user = { key: 'userkey' }
|
268
266
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
269
|
-
result = evaluate(flag, user, features, logger)
|
267
|
+
result = evaluate(flag, user, features, logger, factory)
|
270
268
|
expect(result.detail).to eq(detail)
|
271
269
|
expect(result.events).to eq([])
|
272
270
|
end
|
@@ -281,7 +279,7 @@ describe LaunchDarkly::Evaluation do
|
|
281
279
|
}
|
282
280
|
user = { key: 'userkey' }
|
283
281
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil, { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
284
|
-
result = evaluate(flag, user, features, logger)
|
282
|
+
result = evaluate(flag, user, features, logger, factory)
|
285
283
|
expect(result.detail).to eq(detail)
|
286
284
|
expect(result.events).to eq([])
|
287
285
|
end
|
@@ -299,7 +297,7 @@ describe LaunchDarkly::Evaluation do
|
|
299
297
|
}
|
300
298
|
user = { key: 'userkey' }
|
301
299
|
detail = LaunchDarkly::EvaluationDetail.new('c', 2, { kind: 'TARGET_MATCH' })
|
302
|
-
result = evaluate(flag, user, features, logger)
|
300
|
+
result = evaluate(flag, user, features, logger, factory)
|
303
301
|
expect(result.detail).to eq(detail)
|
304
302
|
expect(result.events).to eq([])
|
305
303
|
end
|
@@ -310,7 +308,7 @@ describe LaunchDarkly::Evaluation do
|
|
310
308
|
user = { key: 'userkey' }
|
311
309
|
detail = LaunchDarkly::EvaluationDetail.new(true, 1,
|
312
310
|
{ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid' })
|
313
|
-
result = evaluate(flag, user, features, logger)
|
311
|
+
result = evaluate(flag, user, features, logger, factory)
|
314
312
|
expect(result.detail).to eq(detail)
|
315
313
|
expect(result.events).to eq([])
|
316
314
|
end
|
@@ -321,7 +319,7 @@ describe LaunchDarkly::Evaluation do
|
|
321
319
|
user = { key: 'userkey' }
|
322
320
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
323
321
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
324
|
-
result = evaluate(flag, user, features, logger)
|
322
|
+
result = evaluate(flag, user, features, logger, factory)
|
325
323
|
expect(result.detail).to eq(detail)
|
326
324
|
expect(result.events).to eq([])
|
327
325
|
end
|
@@ -332,7 +330,7 @@ describe LaunchDarkly::Evaluation do
|
|
332
330
|
user = { key: 'userkey' }
|
333
331
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
334
332
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
335
|
-
result = evaluate(flag, user, features, logger)
|
333
|
+
result = evaluate(flag, user, features, logger, factory)
|
336
334
|
expect(result.detail).to eq(detail)
|
337
335
|
expect(result.events).to eq([])
|
338
336
|
end
|
@@ -343,7 +341,7 @@ describe LaunchDarkly::Evaluation do
|
|
343
341
|
user = { key: 'userkey' }
|
344
342
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
345
343
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
346
|
-
result = evaluate(flag, user, features, logger)
|
344
|
+
result = evaluate(flag, user, features, logger, factory)
|
347
345
|
expect(result.detail).to eq(detail)
|
348
346
|
expect(result.events).to eq([])
|
349
347
|
end
|
@@ -355,7 +353,7 @@ describe LaunchDarkly::Evaluation do
|
|
355
353
|
user = { key: 'userkey' }
|
356
354
|
detail = LaunchDarkly::EvaluationDetail.new(nil, nil,
|
357
355
|
{ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' })
|
358
|
-
result = evaluate(flag, user, features, logger)
|
356
|
+
result = evaluate(flag, user, features, logger, factory)
|
359
357
|
expect(result.detail).to eq(detail)
|
360
358
|
expect(result.events).to eq([])
|
361
359
|
end
|
@@ -364,7 +362,7 @@ describe LaunchDarkly::Evaluation do
|
|
364
362
|
clause = { attribute: 'key', op: 'in', values: ['999'] }
|
365
363
|
flag = boolean_flag_with_clauses([clause])
|
366
364
|
user = { key: 999 }
|
367
|
-
result = evaluate(flag, user, features, logger)
|
365
|
+
result = evaluate(flag, user, features, logger, factory)
|
368
366
|
expect(result.detail.value).to eq(true)
|
369
367
|
end
|
370
368
|
|
@@ -375,7 +373,7 @@ describe LaunchDarkly::Evaluation do
|
|
375
373
|
rollout: { salt: '', variations: [ { weight: 100000, variation: 1 } ] } }
|
376
374
|
flag = boolean_flag_with_rules([rule])
|
377
375
|
user = { key: "userkey", secondary: 999 }
|
378
|
-
result = evaluate(flag, user, features, logger)
|
376
|
+
result = evaluate(flag, user, features, logger, factory)
|
379
377
|
expect(result.detail.reason).to eq({ kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'ruleid'})
|
380
378
|
end
|
381
379
|
end
|
@@ -385,28 +383,28 @@ describe LaunchDarkly::Evaluation do
|
|
385
383
|
user = { key: 'x', name: 'Bob' }
|
386
384
|
clause = { attribute: 'name', op: 'in', values: ['Bob'] }
|
387
385
|
flag = boolean_flag_with_clauses([clause])
|
388
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be true
|
386
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
|
389
387
|
end
|
390
388
|
|
391
389
|
it "can match custom attribute" do
|
392
390
|
user = { key: 'x', name: 'Bob', custom: { legs: 4 } }
|
393
391
|
clause = { attribute: 'legs', op: 'in', values: [4] }
|
394
392
|
flag = boolean_flag_with_clauses([clause])
|
395
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be true
|
393
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
|
396
394
|
end
|
397
395
|
|
398
396
|
it "returns false for missing attribute" do
|
399
397
|
user = { key: 'x', name: 'Bob' }
|
400
398
|
clause = { attribute: 'legs', op: 'in', values: [4] }
|
401
399
|
flag = boolean_flag_with_clauses([clause])
|
402
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be false
|
400
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
|
403
401
|
end
|
404
402
|
|
405
403
|
it "returns false for unknown operator" do
|
406
404
|
user = { key: 'x', name: 'Bob' }
|
407
405
|
clause = { attribute: 'name', op: 'unknown', values: [4] }
|
408
406
|
flag = boolean_flag_with_clauses([clause])
|
409
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be false
|
407
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
|
410
408
|
end
|
411
409
|
|
412
410
|
it "does not stop evaluating rules after clause with unknown operator" do
|
@@ -416,14 +414,14 @@ describe LaunchDarkly::Evaluation do
|
|
416
414
|
clause1 = { attribute: 'name', op: 'in', values: ['Bob'] }
|
417
415
|
rule1 = { clauses: [ clause1 ], variation: 1 }
|
418
416
|
flag = boolean_flag_with_rules([rule0, rule1])
|
419
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be true
|
417
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
|
420
418
|
end
|
421
419
|
|
422
420
|
it "can be negated" do
|
423
421
|
user = { key: 'x', name: 'Bob' }
|
424
422
|
clause = { attribute: 'name', op: 'in', values: ['Bob'], negate: true }
|
425
423
|
flag = boolean_flag_with_clauses([clause])
|
426
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be false
|
424
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
|
427
425
|
end
|
428
426
|
|
429
427
|
it "retrieves segment from segment store for segmentMatch operator" do
|
@@ -438,14 +436,14 @@ describe LaunchDarkly::Evaluation do
|
|
438
436
|
user = { key: 'userkey' }
|
439
437
|
clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
|
440
438
|
flag = boolean_flag_with_clauses([clause])
|
441
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be true
|
439
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be true
|
442
440
|
end
|
443
441
|
|
444
442
|
it "falls through with no errors if referenced segment is not found" do
|
445
443
|
user = { key: 'userkey' }
|
446
444
|
clause = { attribute: '', op: 'segmentMatch', values: ['segkey'] }
|
447
445
|
flag = boolean_flag_with_clauses([clause])
|
448
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be false
|
446
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be false
|
449
447
|
end
|
450
448
|
|
451
449
|
it "can be negated" do
|
@@ -454,7 +452,7 @@ describe LaunchDarkly::Evaluation do
|
|
454
452
|
flag = boolean_flag_with_clauses([clause])
|
455
453
|
expect {
|
456
454
|
clause[:negate] = true
|
457
|
-
}.to change {evaluate(flag, user, features, logger).detail.value}.from(true).to(false)
|
455
|
+
}.to change {evaluate(flag, user, features, logger, factory).detail.value}.from(true).to(false)
|
458
456
|
end
|
459
457
|
end
|
460
458
|
|
@@ -557,7 +555,7 @@ describe LaunchDarkly::Evaluation do
|
|
557
555
|
user = { key: 'x', custom: { foo: value1 } }
|
558
556
|
clause = { attribute: 'foo', op: op, values: [value2] }
|
559
557
|
flag = boolean_flag_with_clauses([clause])
|
560
|
-
expect(evaluate(flag, user, features, logger).detail.value).to be shouldBe
|
558
|
+
expect(evaluate(flag, user, features, logger, factory).detail.value).to be shouldBe
|
561
559
|
end
|
562
560
|
end
|
563
561
|
end
|
@@ -648,7 +646,7 @@ describe LaunchDarkly::Evaluation do
|
|
648
646
|
features.upsert(LaunchDarkly::SEGMENTS, segment)
|
649
647
|
clause = make_segment_match_clause(segment)
|
650
648
|
flag = boolean_flag_with_clauses([clause])
|
651
|
-
evaluate(flag, user, features, logger).detail.value
|
649
|
+
evaluate(flag, user, features, logger, factory).detail.value
|
652
650
|
end
|
653
651
|
|
654
652
|
it 'explicitly includes user' do
|
data/spec/events_spec.rb
CHANGED
@@ -342,7 +342,7 @@ describe LaunchDarkly::EventProcessor do
|
|
342
342
|
|
343
343
|
it "queues custom event with user" do
|
344
344
|
@ep = subject.new("sdk_key", default_config, hc)
|
345
|
-
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
345
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" }, metricValue: 1.5 }
|
346
346
|
@ep.add_event(e)
|
347
347
|
|
348
348
|
output = flush_and_get_events
|
@@ -565,6 +565,7 @@ describe LaunchDarkly::EventProcessor do
|
|
565
565
|
else
|
566
566
|
out[:user] = inline_user
|
567
567
|
end
|
568
|
+
out[:metricValue] = e[:metricValue] if e.has_key?(:metricValue)
|
568
569
|
out
|
569
570
|
end
|
570
571
|
|
data/spec/ldclient_spec.rb
CHANGED
@@ -25,6 +25,22 @@ describe LaunchDarkly::LDClient do
|
|
25
25
|
}
|
26
26
|
}
|
27
27
|
end
|
28
|
+
let(:numeric_key_user) do
|
29
|
+
{
|
30
|
+
key: 33,
|
31
|
+
custom: {
|
32
|
+
groups: [ "microsoft", "google" ]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
let(:sanitized_numeric_key_user) do
|
37
|
+
{
|
38
|
+
key: "33",
|
39
|
+
custom: {
|
40
|
+
groups: [ "microsoft", "google" ]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
28
44
|
let(:user_without_key) do
|
29
45
|
{ name: "Keyless Joe" }
|
30
46
|
end
|
@@ -91,7 +107,6 @@ describe LaunchDarkly::LDClient do
|
|
91
107
|
key: "key",
|
92
108
|
version: 100,
|
93
109
|
user: nil,
|
94
|
-
variation: nil,
|
95
110
|
value: "default",
|
96
111
|
default: "default",
|
97
112
|
trackEvents: true,
|
@@ -109,7 +124,6 @@ describe LaunchDarkly::LDClient do
|
|
109
124
|
key: "key",
|
110
125
|
version: 100,
|
111
126
|
user: bad_user,
|
112
|
-
variation: nil,
|
113
127
|
value: "default",
|
114
128
|
default: "default",
|
115
129
|
trackEvents: true,
|
@@ -117,6 +131,61 @@ describe LaunchDarkly::LDClient do
|
|
117
131
|
))
|
118
132
|
client.variation("key", bad_user, "default")
|
119
133
|
end
|
134
|
+
|
135
|
+
it "sets trackEvents and reason if trackEvents is set for matched rule" do
|
136
|
+
flag = {
|
137
|
+
key: 'flag',
|
138
|
+
on: true,
|
139
|
+
variations: [ 'value' ],
|
140
|
+
version: 100,
|
141
|
+
rules: [
|
142
|
+
clauses: [
|
143
|
+
{ attribute: 'key', op: 'in', values: [ user[:key] ] }
|
144
|
+
],
|
145
|
+
variation: 0,
|
146
|
+
id: 'id',
|
147
|
+
trackEvents: true
|
148
|
+
]
|
149
|
+
}
|
150
|
+
config.feature_store.init({ LaunchDarkly::FEATURES => {} })
|
151
|
+
config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
|
152
|
+
expect(event_processor).to receive(:add_event).with(hash_including(
|
153
|
+
kind: 'feature',
|
154
|
+
key: 'flag',
|
155
|
+
version: 100,
|
156
|
+
user: user,
|
157
|
+
value: 'value',
|
158
|
+
default: 'default',
|
159
|
+
trackEvents: true,
|
160
|
+
reason: { kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'id' }
|
161
|
+
))
|
162
|
+
client.variation('flag', user, 'default')
|
163
|
+
end
|
164
|
+
|
165
|
+
it "sets trackEvents and reason if trackEventsFallthrough is set and we fell through" do
|
166
|
+
flag = {
|
167
|
+
key: 'flag',
|
168
|
+
on: true,
|
169
|
+
variations: [ 'value' ],
|
170
|
+
fallthrough: { variation: 0 },
|
171
|
+
version: 100,
|
172
|
+
rules: [],
|
173
|
+
trackEventsFallthrough: true
|
174
|
+
}
|
175
|
+
config.feature_store.init({ LaunchDarkly::FEATURES => {} })
|
176
|
+
config.feature_store.upsert(LaunchDarkly::FEATURES, flag)
|
177
|
+
expect(event_processor).to receive(:add_event).with(hash_including(
|
178
|
+
kind: 'feature',
|
179
|
+
key: 'flag',
|
180
|
+
version: 100,
|
181
|
+
user: user,
|
182
|
+
value: 'value',
|
183
|
+
default: 'default',
|
184
|
+
trackEvents: true,
|
185
|
+
reason: { kind: 'FALLTHROUGH' }
|
186
|
+
))
|
187
|
+
client.variation('flag', user, 'default')
|
188
|
+
end
|
120
189
|
end
|
121
190
|
|
122
191
|
describe '#variation_detail' do
|
@@ -338,6 +407,17 @@ describe LaunchDarkly::LDClient do
|
|
338
407
|
client.track("custom_event_name", user, 42)
|
339
408
|
end
|
340
409
|
|
410
|
+
it "can include a metric value" do
|
411
|
+
expect(event_processor).to receive(:add_event).with(hash_including(
|
412
|
+
kind: "custom", key: "custom_event_name", user: user, metricValue: 1.5))
|
413
|
+
client.track("custom_event_name", user, nil, 1.5)
|
414
|
+
end
|
415
|
+
|
416
|
+
it "sanitizes the user in the event" do
|
417
|
+
expect(event_processor).to receive(:add_event).with(hash_including(user: sanitized_numeric_key_user))
|
418
|
+
client.track("custom_event_name", numeric_key_user, nil)
|
419
|
+
end
|
420
|
+
|
341
421
|
it "does not send an event, and logs a warning, if user is nil" do
|
342
422
|
expect(event_processor).not_to receive(:add_event)
|
343
423
|
expect(logger).to receive(:warn)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: launchdarkly-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- LaunchDarkly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-dynamodb
|
@@ -239,6 +239,7 @@ files:
|
|
239
239
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
240
240
|
- ".gitignore"
|
241
241
|
- ".hound.yml"
|
242
|
+
- ".ldrelease/config.yml"
|
242
243
|
- ".rspec"
|
243
244
|
- ".rubocop.yml"
|
244
245
|
- ".simplecov"
|
@@ -265,6 +266,7 @@ files:
|
|
265
266
|
- lib/ldclient-rb/file_data_source.rb
|
266
267
|
- lib/ldclient-rb/flags_state.rb
|
267
268
|
- lib/ldclient-rb/impl.rb
|
269
|
+
- lib/ldclient-rb/impl/event_factory.rb
|
268
270
|
- lib/ldclient-rb/impl/integrations/consul_impl.rb
|
269
271
|
- lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
|
270
272
|
- lib/ldclient-rb/impl/integrations/redis_impl.rb
|
@@ -342,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
342
344
|
version: '0'
|
343
345
|
requirements: []
|
344
346
|
rubyforge_project:
|
345
|
-
rubygems_version: 2.5.2
|
347
|
+
rubygems_version: 2.5.2.3
|
346
348
|
signing_key:
|
347
349
|
specification_version: 4
|
348
350
|
summary: LaunchDarkly SDK for Ruby
|