launchdarkly-server-sdk 6.3.0 → 8.0.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/README.md +3 -4
- data/lib/ldclient-rb/config.rb +112 -62
- data/lib/ldclient-rb/context.rb +444 -0
- data/lib/ldclient-rb/evaluation_detail.rb +26 -22
- data/lib/ldclient-rb/events.rb +256 -146
- data/lib/ldclient-rb/flags_state.rb +26 -15
- data/lib/ldclient-rb/impl/big_segments.rb +18 -18
- data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +145 -0
- data/lib/ldclient-rb/impl/data_source.rb +188 -0
- data/lib/ldclient-rb/impl/data_store.rb +59 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
- data/lib/ldclient-rb/impl/evaluator.rb +386 -142
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
- data/lib/ldclient-rb/impl/event_sender.rb +7 -6
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +136 -0
- data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +19 -7
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +38 -30
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +24 -11
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +109 -12
- data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
- data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
- data/lib/ldclient-rb/impl/model/clause.rb +45 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +255 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +132 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
- data/lib/ldclient-rb/impl/repeating_task.rb +3 -4
- data/lib/ldclient-rb/impl/sampler.rb +25 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +102 -8
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
- data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +59 -1
- data/lib/ldclient-rb/in_memory_store.rb +9 -2
- data/lib/ldclient-rb/integrations/consul.rb +2 -2
- data/lib/ldclient-rb/integrations/dynamodb.rb +2 -2
- data/lib/ldclient-rb/integrations/file_data.rb +4 -4
- data/lib/ldclient-rb/integrations/redis.rb +5 -5
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +287 -62
- data/lib/ldclient-rb/integrations/test_data.rb +18 -14
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +20 -9
- data/lib/ldclient-rb/interfaces.rb +600 -14
- data/lib/ldclient-rb/ldclient.rb +314 -134
- data/lib/ldclient-rb/memoized_value.rb +1 -1
- data/lib/ldclient-rb/migrations.rb +230 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
- data/lib/ldclient-rb/polling.rb +52 -6
- data/lib/ldclient-rb/reference.rb +274 -0
- data/lib/ldclient-rb/requestor.rb +9 -11
- data/lib/ldclient-rb/stream.rb +96 -34
- data/lib/ldclient-rb/util.rb +97 -14
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +3 -4
- metadata +65 -23
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/file_data_source.rb +0 -23
- data/lib/ldclient-rb/impl/event_factory.rb +0 -126
- data/lib/ldclient-rb/newrelic.rb +0 -17
- data/lib/ldclient-rb/redis_store.rb +0 -88
- data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -43,9 +43,50 @@ module LaunchDarkly
|
|
43
43
|
self
|
44
44
|
end
|
45
45
|
|
46
|
+
#
|
47
|
+
# Set the migration related settings for this feature flag.
|
48
|
+
#
|
49
|
+
# The settings hash should be built using the {FlagMigrationSettingsBuilder}.
|
50
|
+
#
|
51
|
+
# @param settings [Hash]
|
52
|
+
# @return [FlagBuilder] the builder
|
53
|
+
#
|
54
|
+
def migration_settings(settings)
|
55
|
+
@migration_settings = settings
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Set the sampling ratio for this flag. This ratio is used to control the emission rate of feature, debug, and
|
61
|
+
# migration op events.
|
62
|
+
#
|
63
|
+
# General usage should not require interacting with this method.
|
64
|
+
#
|
65
|
+
# @param ratio [Integer]
|
66
|
+
# @return [FlagBuilder]
|
67
|
+
#
|
68
|
+
def sampling_ratio(ratio)
|
69
|
+
@sampling_ratio = ratio
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Set the option to exclude this flag from summary events. This is used to control the size of the summary event
|
75
|
+
# in the event certain flag payloads are large.
|
76
|
+
#
|
77
|
+
# General usage should not require interacting with this method.
|
78
|
+
#
|
79
|
+
# @param exclude [Boolean]
|
80
|
+
# @return [FlagBuilder]
|
81
|
+
#
|
82
|
+
def exclude_from_summaries(exclude)
|
83
|
+
@exclude_from_summaries = exclude
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
46
87
|
#
|
47
88
|
# Specifies the fallthrough variation. The fallthrough is the value
|
48
|
-
# that is returned if targeting is on and the
|
89
|
+
# that is returned if targeting is on and the context was not matched by a more specific
|
49
90
|
# target or rule.
|
50
91
|
#
|
51
92
|
# If the flag was previously configured with other variations and the variation specified is a boolean,
|
@@ -56,7 +97,7 @@ module LaunchDarkly
|
|
56
97
|
# @return [FlagBuilder] the builder
|
57
98
|
#
|
58
99
|
def fallthrough_variation(variation)
|
59
|
-
if LaunchDarkly::Impl::Util.
|
100
|
+
if LaunchDarkly::Impl::Util.bool? variation
|
60
101
|
boolean_flag.fallthrough_variation(variation_for_boolean(variation))
|
61
102
|
else
|
62
103
|
@fallthrough_variation = variation
|
@@ -76,7 +117,7 @@ module LaunchDarkly
|
|
76
117
|
# @return [FlagBuilder] the builder
|
77
118
|
#
|
78
119
|
def off_variation(variation)
|
79
|
-
if LaunchDarkly::Impl::Util.
|
120
|
+
if LaunchDarkly::Impl::Util.bool? variation
|
80
121
|
boolean_flag.off_variation(variation_for_boolean(variation))
|
81
122
|
else
|
82
123
|
@off_variation = variation
|
@@ -108,7 +149,7 @@ module LaunchDarkly
|
|
108
149
|
end
|
109
150
|
|
110
151
|
#
|
111
|
-
# Sets the flag to always return the specified variation for all
|
152
|
+
# Sets the flag to always return the specified variation for all contexts.
|
112
153
|
#
|
113
154
|
# The variation is specified, Targeting is switched on, and any existing targets or rules are removed.
|
114
155
|
# The fallthrough variation is set to the specified value. The off variation is left unchanged.
|
@@ -120,31 +161,31 @@ module LaunchDarkly
|
|
120
161
|
# 0 for the first, 1 for the second, etc.
|
121
162
|
# @return [FlagBuilder] the builder
|
122
163
|
#
|
123
|
-
def
|
124
|
-
if LaunchDarkly::Impl::Util.
|
125
|
-
boolean_flag.
|
164
|
+
def variation_for_all(variation)
|
165
|
+
if LaunchDarkly::Impl::Util.bool? variation
|
166
|
+
boolean_flag.variation_for_all(variation_for_boolean(variation))
|
126
167
|
else
|
127
|
-
on(true).clear_rules.
|
168
|
+
on(true).clear_rules.clear_targets.fallthrough_variation(variation)
|
128
169
|
end
|
129
170
|
end
|
130
171
|
|
131
172
|
#
|
132
|
-
# Sets the flag to always return the specified variation value for all
|
173
|
+
# Sets the flag to always return the specified variation value for all context.
|
133
174
|
#
|
134
175
|
# The value may be of any valid JSON type. This method changes the
|
135
176
|
# flag to have only a single variation, which is this value, and to return the same
|
136
177
|
# variation regardless of whether targeting is on or off. Any existing targets or rules
|
137
178
|
# are removed.
|
138
179
|
#
|
139
|
-
# @param value [Object] the desired value to be returned for all
|
180
|
+
# @param value [Object] the desired value to be returned for all contexts
|
140
181
|
# @return [FlagBuilder] the builder
|
141
182
|
#
|
142
|
-
def
|
143
|
-
variations(value).
|
183
|
+
def value_for_all(value)
|
184
|
+
variations(value).variation_for_all(0)
|
144
185
|
end
|
145
186
|
|
146
187
|
#
|
147
|
-
# Sets the flag to return the specified variation for a specific
|
188
|
+
# Sets the flag to return the specified variation for a specific context key when targeting
|
148
189
|
# is on.
|
149
190
|
#
|
150
191
|
# This has no effect when targeting is turned off for the flag.
|
@@ -152,36 +193,87 @@ module LaunchDarkly
|
|
152
193
|
# If the flag was previously configured with other variations and the variation specified is a boolean,
|
153
194
|
# this also changes it to a boolean flag.
|
154
195
|
#
|
155
|
-
# @param
|
196
|
+
# @param context_kind [String] a context kind
|
197
|
+
# @param context_key [String] a context key
|
156
198
|
# @param variation [Boolean, Integer] true or false or the desired variation index to return:
|
157
199
|
# 0 for the first, 1 for the second, etc.
|
158
200
|
# @return [FlagBuilder] the builder
|
159
201
|
#
|
160
|
-
def
|
161
|
-
if LaunchDarkly::Impl::Util.
|
162
|
-
boolean_flag.
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
202
|
+
def variation_for_key(context_kind, context_key, variation)
|
203
|
+
if LaunchDarkly::Impl::Util.bool? variation
|
204
|
+
return boolean_flag.variation_for_key(context_kind, context_key, variation_for_boolean(variation))
|
205
|
+
end
|
206
|
+
|
207
|
+
if @targets.nil?
|
208
|
+
@targets = Hash.new
|
209
|
+
end
|
210
|
+
|
211
|
+
targets = @targets[context_kind] || []
|
212
|
+
@variations.count.times do | i |
|
213
|
+
if i == variation
|
214
|
+
if targets[i].nil?
|
215
|
+
targets[i] = [context_key]
|
216
|
+
else
|
217
|
+
targets[i].push(context_key)
|
176
218
|
end
|
219
|
+
elsif not targets[i].nil?
|
220
|
+
targets[i].delete(context_key)
|
177
221
|
end
|
178
|
-
self
|
179
222
|
end
|
223
|
+
|
224
|
+
@targets[context_kind] = targets
|
225
|
+
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# Sets the flag to return the specified variation for a specific user key when targeting
|
231
|
+
# is on.
|
232
|
+
#
|
233
|
+
# This is a shortcut for calling {variation_for_key} with
|
234
|
+
# `LaunchDarkly::LDContext::KIND_DEFAULT` as the context kind.
|
235
|
+
#
|
236
|
+
# This has no effect when targeting is turned off for the flag.
|
237
|
+
#
|
238
|
+
# If the flag was previously configured with other variations and the variation specified is a boolean,
|
239
|
+
# this also changes it to a boolean flag.
|
240
|
+
#
|
241
|
+
# @param user_key [String] a user key
|
242
|
+
# @param variation [Boolean, Integer] true or false or the desired variation index to return:
|
243
|
+
# 0 for the first, 1 for the second, etc.
|
244
|
+
# @return [FlagBuilder] the builder
|
245
|
+
#
|
246
|
+
def variation_for_user(user_key, variation)
|
247
|
+
variation_for_key(LaunchDarkly::LDContext::KIND_DEFAULT, user_key, variation)
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# Starts defining a flag rule, using the "is one of" operator.
|
252
|
+
#
|
253
|
+
# @example create a rule that returns `true` if the name is "Patsy" or "Edina" and the context kind is "user"
|
254
|
+
# testData.flag("flag")
|
255
|
+
# .if_match_context("user", :name, 'Patsy', 'Edina')
|
256
|
+
# .then_return(true);
|
257
|
+
#
|
258
|
+
# @param context_kind [String] a context kind
|
259
|
+
# @param attribute [Symbol] the context attribute to match against
|
260
|
+
# @param values [Array<Object>] values to compare to
|
261
|
+
# @return [FlagRuleBuilder] a flag rule builder
|
262
|
+
#
|
263
|
+
# @see FlagRuleBuilder#then_return
|
264
|
+
# @see FlagRuleBuilder#and_match
|
265
|
+
# @see FlagRuleBuilder#and_not_match
|
266
|
+
#
|
267
|
+
def if_match_context(context_kind, attribute, *values)
|
268
|
+
FlagRuleBuilder.new(self).and_match_context(context_kind, attribute, *values)
|
180
269
|
end
|
181
270
|
|
182
271
|
#
|
183
272
|
# Starts defining a flag rule, using the "is one of" operator.
|
184
273
|
#
|
274
|
+
# This is a shortcut for calling {if_match_context} with
|
275
|
+
# `LaunchDarkly::LDContext::KIND_DEFAULT` as the context kind.
|
276
|
+
#
|
185
277
|
# @example create a rule that returns `true` if the name is "Patsy" or "Edina"
|
186
278
|
# testData.flag("flag")
|
187
279
|
# .if_match(:name, 'Patsy', 'Edina')
|
@@ -196,7 +288,7 @@ module LaunchDarkly
|
|
196
288
|
# @see FlagRuleBuilder#and_not_match
|
197
289
|
#
|
198
290
|
def if_match(attribute, *values)
|
199
|
-
|
291
|
+
if_match_context(LaunchDarkly::LDContext::KIND_DEFAULT, attribute, *values)
|
200
292
|
end
|
201
293
|
|
202
294
|
#
|
@@ -204,6 +296,30 @@ module LaunchDarkly
|
|
204
296
|
#
|
205
297
|
# @example create a rule that returns `true` if the name is neither "Saffron" nor "Bubble"
|
206
298
|
# testData.flag("flag")
|
299
|
+
# .if_not_match_context("user", :name, 'Saffron', 'Bubble')
|
300
|
+
# .then_return(true)
|
301
|
+
#
|
302
|
+
# @param context_kind [String] a context kind
|
303
|
+
# @param attribute [Symbol] the context attribute to match against
|
304
|
+
# @param values [Array<Object>] values to compare to
|
305
|
+
# @return [FlagRuleBuilder] a flag rule builder
|
306
|
+
#
|
307
|
+
# @see FlagRuleBuilder#then_return
|
308
|
+
# @see FlagRuleBuilder#and_match
|
309
|
+
# @see FlagRuleBuilder#and_not_match
|
310
|
+
#
|
311
|
+
def if_not_match_context(context_kind, attribute, *values)
|
312
|
+
FlagRuleBuilder.new(self).and_not_match_context(context_kind, attribute, *values)
|
313
|
+
end
|
314
|
+
|
315
|
+
#
|
316
|
+
# Starts defining a flag rule, using the "is not one of" operator.
|
317
|
+
#
|
318
|
+
# This is a shortcut for calling {if_not_match_context} with
|
319
|
+
# `LaunchDarkly::LDContext::KIND_DEFAULT` as the context kind.
|
320
|
+
#
|
321
|
+
# @example create a rule that returns `true` if the name is neither "Saffron" nor "Bubble"
|
322
|
+
# testData.flag("flag")
|
207
323
|
# .if_not_match(:name, 'Saffron', 'Bubble')
|
208
324
|
# .then_return(true)
|
209
325
|
#
|
@@ -216,16 +332,16 @@ module LaunchDarkly
|
|
216
332
|
# @see FlagRuleBuilder#and_not_match
|
217
333
|
#
|
218
334
|
def if_not_match(attribute, *values)
|
219
|
-
|
335
|
+
if_not_match_context(LaunchDarkly::LDContext::KIND_DEFAULT, attribute, *values)
|
220
336
|
end
|
221
337
|
|
222
338
|
#
|
223
|
-
# Removes any existing
|
224
|
-
# This undoes the effect of methods like {#
|
339
|
+
# Removes any existing targets from the flag.
|
340
|
+
# This undoes the effect of methods like {#variation_for_key}
|
225
341
|
#
|
226
342
|
# @return [FlagBuilder] the same builder
|
227
343
|
#
|
228
|
-
def
|
344
|
+
def clear_targets
|
229
345
|
@targets = nil
|
230
346
|
self
|
231
347
|
end
|
@@ -243,7 +359,7 @@ module LaunchDarkly
|
|
243
359
|
|
244
360
|
# @private
|
245
361
|
def add_rule(rule)
|
246
|
-
if @rules.nil?
|
362
|
+
if @rules.nil?
|
247
363
|
@rules = Array.new
|
248
364
|
end
|
249
365
|
@rules.push(rule)
|
@@ -261,7 +377,7 @@ module LaunchDarkly
|
|
261
377
|
# @return [FlagBuilder] the builder
|
262
378
|
#
|
263
379
|
def boolean_flag
|
264
|
-
if
|
380
|
+
if boolean_flag?
|
265
381
|
self
|
266
382
|
else
|
267
383
|
variations(true, false)
|
@@ -278,33 +394,90 @@ module LaunchDarkly
|
|
278
394
|
variations: @variations,
|
279
395
|
}
|
280
396
|
|
281
|
-
unless @off_variation.nil?
|
397
|
+
unless @off_variation.nil?
|
282
398
|
res[:offVariation] = @off_variation
|
283
399
|
end
|
284
400
|
|
285
|
-
unless @fallthrough_variation.nil?
|
401
|
+
unless @fallthrough_variation.nil?
|
286
402
|
res[:fallthrough] = { variation: @fallthrough_variation }
|
287
403
|
end
|
288
404
|
|
289
|
-
unless @
|
290
|
-
res[:
|
291
|
-
|
405
|
+
unless @migration_settings.nil?
|
406
|
+
res[:migration] = @migration_settings
|
407
|
+
end
|
408
|
+
|
409
|
+
unless @sampling_ratio.nil? || @sampling_ratio == 1
|
410
|
+
res[:samplingRatio] = @sampling_ratio
|
411
|
+
end
|
412
|
+
|
413
|
+
unless @exclude_from_summaries.nil? || !@exclude_from_summaries
|
414
|
+
res[:excludeFromSummaries] = @exclude_from_summaries
|
415
|
+
end
|
416
|
+
|
417
|
+
unless @targets.nil?
|
418
|
+
targets = []
|
419
|
+
context_targets = []
|
420
|
+
|
421
|
+
@targets.each do |kind, targets_for_kind|
|
422
|
+
targets_for_kind.each_with_index do |values, variation|
|
423
|
+
next if values.nil?
|
424
|
+
if kind == LaunchDarkly::LDContext::KIND_DEFAULT
|
425
|
+
targets << { variation: variation, values: values }
|
426
|
+
context_targets << { contextKind: LaunchDarkly::LDContext::KIND_DEFAULT, variation: variation, values: [] }
|
427
|
+
else
|
428
|
+
context_targets << { contextKind: kind, variation: variation, values: values }
|
429
|
+
end
|
430
|
+
end
|
292
431
|
end
|
432
|
+
|
433
|
+
res[:targets] = targets
|
434
|
+
res[:contextTargets] = context_targets
|
293
435
|
end
|
294
436
|
|
295
|
-
unless @rules.nil?
|
296
|
-
res[:rules] = @rules.each_with_index.
|
437
|
+
unless @rules.nil?
|
438
|
+
res[:rules] = @rules.each_with_index.map { | rule, i | rule.build(i) }
|
297
439
|
end
|
298
440
|
|
299
441
|
res
|
300
442
|
end
|
301
443
|
|
444
|
+
#
|
445
|
+
# A builder for feature flag migration settings to be used with {FlagBuilder}.
|
446
|
+
#
|
447
|
+
# In the LaunchDarkly model, a flag can be a standard feature flag, or it can be a migration-related flag, in
|
448
|
+
# which case it has migration-specified related settings. These settings control things like the rate at which
|
449
|
+
# reads are tested for consistency between origins.
|
450
|
+
#
|
451
|
+
class FlagMigrationSettingsBuilder
|
452
|
+
def initialize()
|
453
|
+
@check_ratio = nil
|
454
|
+
end
|
455
|
+
|
456
|
+
#
|
457
|
+
# @param ratio [Integer]
|
458
|
+
# @return [FlagMigrationSettingsBuilder]
|
459
|
+
#
|
460
|
+
def check_ratio(ratio)
|
461
|
+
return unless ratio.is_a? Integer
|
462
|
+
@check_ratio = ratio
|
463
|
+
self
|
464
|
+
end
|
465
|
+
|
466
|
+
def build
|
467
|
+
return nil if @check_ratio.nil? || @check_ratio == 1
|
468
|
+
|
469
|
+
{
|
470
|
+
"checkRatio": @check_ratio,
|
471
|
+
}
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
302
475
|
#
|
303
476
|
# A builder for feature flag rules to be used with {FlagBuilder}.
|
304
477
|
#
|
305
478
|
# In the LaunchDarkly model, a flag can have any number of rules, and a rule can have any number of
|
306
|
-
# clauses. A clause is an individual test such as "name is 'X'". A rule matches a
|
307
|
-
# rule's clauses match the
|
479
|
+
# clauses. A clause is an individual test such as "name is 'X'". A rule matches a context if all of the
|
480
|
+
# rule's clauses match the context.
|
308
481
|
#
|
309
482
|
# To start defining a rule, use one of the flag builder's matching methods such as
|
310
483
|
# {FlagBuilder#if_match}. This defines the first clause for the rule.
|
@@ -314,7 +487,7 @@ module LaunchDarkly
|
|
314
487
|
#
|
315
488
|
class FlagRuleBuilder
|
316
489
|
# @private
|
317
|
-
FlagRuleClause = Struct.new(:attribute, :op, :values, :negate, keyword_init: true)
|
490
|
+
FlagRuleClause = Struct.new(:contextKind, :attribute, :op, :values, :negate, keyword_init: true)
|
318
491
|
|
319
492
|
# @private
|
320
493
|
def initialize(flag_builder)
|
@@ -331,6 +504,34 @@ module LaunchDarkly
|
|
331
504
|
#
|
332
505
|
# Adds another clause, using the "is one of" operator.
|
333
506
|
#
|
507
|
+
# @example create a rule that returns `true` if the name is "Patsy", the country is "gb", and the context kind is "user"
|
508
|
+
# testData.flag("flag")
|
509
|
+
# .if_match_context("user", :name, 'Patsy')
|
510
|
+
# .and_match_context("user", :country, 'gb')
|
511
|
+
# .then_return(true)
|
512
|
+
#
|
513
|
+
# @param context_kind [String] a context kind
|
514
|
+
# @param attribute [Symbol] the context attribute to match against
|
515
|
+
# @param values [Array<Object>] values to compare to
|
516
|
+
# @return [FlagRuleBuilder] the rule builder
|
517
|
+
#
|
518
|
+
def and_match_context(context_kind, attribute, *values)
|
519
|
+
@clauses.push(FlagRuleClause.new(
|
520
|
+
contextKind: context_kind,
|
521
|
+
attribute: attribute,
|
522
|
+
op: 'in',
|
523
|
+
values: values,
|
524
|
+
negate: false
|
525
|
+
))
|
526
|
+
self
|
527
|
+
end
|
528
|
+
|
529
|
+
#
|
530
|
+
# Adds another clause, using the "is one of" operator.
|
531
|
+
#
|
532
|
+
# This is a shortcut for calling {and_match_context} with
|
533
|
+
# `LaunchDarkly::LDContext::KIND_DEFAULT` as the context kind.
|
534
|
+
#
|
334
535
|
# @example create a rule that returns `true` if the name is "Patsy" and the country is "gb"
|
335
536
|
# testData.flag("flag")
|
336
537
|
# .if_match(:name, 'Patsy')
|
@@ -342,11 +543,30 @@ module LaunchDarkly
|
|
342
543
|
# @return [FlagRuleBuilder] the rule builder
|
343
544
|
#
|
344
545
|
def and_match(attribute, *values)
|
546
|
+
and_match_context(LaunchDarkly::LDContext::KIND_DEFAULT, attribute, *values)
|
547
|
+
end
|
548
|
+
|
549
|
+
#
|
550
|
+
# Adds another clause, using the "is not one of" operator.
|
551
|
+
#
|
552
|
+
# @example create a rule that returns `true` if the name is "Patsy" and the country is not "gb"
|
553
|
+
# testData.flag("flag")
|
554
|
+
# .if_match_context("user", :name, 'Patsy')
|
555
|
+
# .and_not_match_context("user", :country, 'gb')
|
556
|
+
# .then_return(true)
|
557
|
+
#
|
558
|
+
# @param context_kind [String] a context kind
|
559
|
+
# @param attribute [Symbol] the context attribute to match against
|
560
|
+
# @param values [Array<Object>] values to compare to
|
561
|
+
# @return [FlagRuleBuilder] the rule builder
|
562
|
+
#
|
563
|
+
def and_not_match_context(context_kind, attribute, *values)
|
345
564
|
@clauses.push(FlagRuleClause.new(
|
565
|
+
contextKind: context_kind,
|
346
566
|
attribute: attribute,
|
347
567
|
op: 'in',
|
348
568
|
values: values,
|
349
|
-
negate:
|
569
|
+
negate: true
|
350
570
|
))
|
351
571
|
self
|
352
572
|
end
|
@@ -354,6 +574,9 @@ module LaunchDarkly
|
|
354
574
|
#
|
355
575
|
# Adds another clause, using the "is not one of" operator.
|
356
576
|
#
|
577
|
+
# This is a shortcut for calling {and_not_match} with
|
578
|
+
# `LaunchDarkly::LDContext::KIND_DEFAULT` as the context kind.
|
579
|
+
#
|
357
580
|
# @example create a rule that returns `true` if the name is "Patsy" and the country is not "gb"
|
358
581
|
# testData.flag("flag")
|
359
582
|
# .if_match(:name, 'Patsy')
|
@@ -365,13 +588,7 @@ module LaunchDarkly
|
|
365
588
|
# @return [FlagRuleBuilder] the rule builder
|
366
589
|
#
|
367
590
|
def and_not_match(attribute, *values)
|
368
|
-
|
369
|
-
attribute: attribute,
|
370
|
-
op: 'in',
|
371
|
-
values: values,
|
372
|
-
negate: true
|
373
|
-
))
|
374
|
-
self
|
591
|
+
and_not_match_context(LaunchDarkly::LDContext::KIND_DEFAULT, attribute, *values)
|
375
592
|
end
|
376
593
|
|
377
594
|
#
|
@@ -386,7 +603,7 @@ module LaunchDarkly
|
|
386
603
|
# @return [FlagBuilder] the flag builder with this rule added
|
387
604
|
#
|
388
605
|
def then_return(variation)
|
389
|
-
if LaunchDarkly::Impl::Util.
|
606
|
+
if LaunchDarkly::Impl::Util.bool? variation
|
390
607
|
@variation = @flag_builder.variation_for_boolean(variation)
|
391
608
|
@flag_builder.boolean_flag.add_rule(self)
|
392
609
|
else
|
@@ -400,7 +617,7 @@ module LaunchDarkly
|
|
400
617
|
{
|
401
618
|
id: 'rule' + ri.to_s,
|
402
619
|
variation: @variation,
|
403
|
-
clauses: @clauses.
|
620
|
+
clauses: @clauses.map(&:to_h),
|
404
621
|
}
|
405
622
|
end
|
406
623
|
end
|
@@ -415,15 +632,23 @@ module LaunchDarkly
|
|
415
632
|
TRUE_VARIATION_INDEX = 0
|
416
633
|
FALSE_VARIATION_INDEX = 1
|
417
634
|
|
418
|
-
def
|
635
|
+
def boolean_flag?
|
419
636
|
@variations.size == 2 &&
|
420
|
-
|
421
|
-
|
637
|
+
@variations[TRUE_VARIATION_INDEX] == true &&
|
638
|
+
@variations[FALSE_VARIATION_INDEX] == false
|
422
639
|
end
|
423
640
|
|
424
641
|
def deep_copy_hash(from)
|
425
642
|
to = Hash.new
|
426
|
-
from.each
|
643
|
+
from.each do |k, v|
|
644
|
+
if v.is_a?(Hash)
|
645
|
+
to[k] = deep_copy_hash(v)
|
646
|
+
elsif v.is_a?(Array)
|
647
|
+
to[k] = deep_copy_array(v)
|
648
|
+
else
|
649
|
+
to[k] = v.clone
|
650
|
+
end
|
651
|
+
end
|
427
652
|
to
|
428
653
|
end
|
429
654
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'ldclient-rb/impl/integrations/test_data/test_data_source'
|
2
|
+
require 'ldclient-rb/impl/model/feature_flag'
|
3
|
+
require 'ldclient-rb/impl/model/segment'
|
2
4
|
require 'ldclient-rb/integrations/test_data/flag_builder'
|
3
5
|
|
4
6
|
require 'concurrent/atomics'
|
@@ -14,12 +16,12 @@ module LaunchDarkly
|
|
14
16
|
#
|
15
17
|
# @example
|
16
18
|
# td = LaunchDarkly::Integrations::TestData.data_source
|
17
|
-
# td.update(td.flag("flag-key-1").
|
19
|
+
# td.update(td.flag("flag-key-1").variation_for_all(true))
|
18
20
|
# config = LaunchDarkly::Config.new(data_source: td)
|
19
21
|
# client = LaunchDarkly::LDClient.new('sdkKey', config)
|
20
22
|
# # flags can be updated at any time:
|
21
23
|
# td.update(td.flag("flag-key-2")
|
22
|
-
# .
|
24
|
+
# .variation_for_key("user", some-user-key", true)
|
23
25
|
# .fallthrough_variation(false))
|
24
26
|
#
|
25
27
|
# The above example uses a simple boolean flag, but more complex configurations are possible using
|
@@ -77,7 +79,7 @@ module LaunchDarkly
|
|
77
79
|
# starts with the same configuration that was last provided for this flag.
|
78
80
|
#
|
79
81
|
# Otherwise, it starts with a new default configuration in which the flag has `true` and
|
80
|
-
# `false` variations, is `true` for all
|
82
|
+
# `false` variations, is `true` for all contexts when targeting is turned on and
|
81
83
|
# `false` otherwise, and currently has targeting turned on. You can change any of those
|
82
84
|
# properties, and provide more complex behavior, using the {FlagBuilder} methods.
|
83
85
|
#
|
@@ -88,7 +90,7 @@ module LaunchDarkly
|
|
88
90
|
#
|
89
91
|
def flag(key)
|
90
92
|
existing_builder = @lock.with_read_lock { @flag_builders[key] }
|
91
|
-
if existing_builder.nil?
|
93
|
+
if existing_builder.nil?
|
92
94
|
FlagBuilder.new(key).boolean_flag
|
93
95
|
else
|
94
96
|
existing_builder.clone
|
@@ -116,10 +118,10 @@ module LaunchDarkly
|
|
116
118
|
@flag_builders[flag_builder.key] = flag_builder
|
117
119
|
version = 0
|
118
120
|
flag_key = flag_builder.key.to_sym
|
119
|
-
if @current_flags[flag_key]
|
121
|
+
if @current_flags[flag_key]
|
120
122
|
version = @current_flags[flag_key][:version]
|
121
123
|
end
|
122
|
-
new_flag = flag_builder.build(version+1)
|
124
|
+
new_flag = Impl::Model.deserialize(FEATURES, flag_builder.build(version+1))
|
123
125
|
@current_flags[flag_key] = new_flag
|
124
126
|
end
|
125
127
|
update_item(FEATURES, new_flag)
|
@@ -149,15 +151,15 @@ module LaunchDarkly
|
|
149
151
|
end
|
150
152
|
|
151
153
|
#
|
152
|
-
# Copies a full
|
154
|
+
# Copies a full segment data model object into the test data.
|
153
155
|
#
|
154
156
|
# It immediately propagates the change to any `LDClient` instance(s) that you have already
|
155
157
|
# configured to use this `TestData`. If no `LDClient` has been started yet, it simply adds
|
156
158
|
# this segment to the test data which will be provided to any LDClient that you subsequently
|
157
159
|
# configure.
|
158
160
|
#
|
159
|
-
# This method is currently the only way to inject
|
160
|
-
# API for segments. It is mainly intended for the SDK's own tests of
|
161
|
+
# This method is currently the only way to inject segment data, since there is no builder
|
162
|
+
# API for segments. It is mainly intended for the SDK's own tests of segment functionality,
|
161
163
|
# since application tests that need to produce a desired evaluation state could do so more easily
|
162
164
|
# by just setting flag values.
|
163
165
|
#
|
@@ -169,12 +171,14 @@ module LaunchDarkly
|
|
169
171
|
end
|
170
172
|
|
171
173
|
private def use_preconfigured_item(kind, item, current)
|
172
|
-
|
174
|
+
item = Impl::Model.deserialize(kind, item)
|
175
|
+
key = item.key.to_sym
|
173
176
|
@lock.with_write_lock do
|
174
177
|
old_item = current[key]
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
+
unless old_item.nil?
|
179
|
+
data = item.as_json
|
180
|
+
data[:version] = old_item.version + 1
|
181
|
+
item = Impl::Model.deserialize(kind, data)
|
178
182
|
end
|
179
183
|
current[key] = item
|
180
184
|
end
|
@@ -195,7 +199,7 @@ module LaunchDarkly
|
|
195
199
|
@lock.with_read_lock do
|
196
200
|
{
|
197
201
|
FEATURES => @current_flags.clone,
|
198
|
-
SEGMENTS => @current_segments.clone
|
202
|
+
SEGMENTS => @current_segments.clone,
|
199
203
|
}
|
200
204
|
end
|
201
205
|
end
|