launchdarkly-server-sdk 6.4.0 → 7.0.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ldclient-rb/config.rb +102 -56
  3. data/lib/ldclient-rb/context.rb +487 -0
  4. data/lib/ldclient-rb/evaluation_detail.rb +20 -20
  5. data/lib/ldclient-rb/events.rb +77 -132
  6. data/lib/ldclient-rb/flags_state.rb +4 -4
  7. data/lib/ldclient-rb/impl/big_segments.rb +17 -17
  8. data/lib/ldclient-rb/impl/context.rb +96 -0
  9. data/lib/ldclient-rb/impl/context_filter.rb +145 -0
  10. data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
  11. data/lib/ldclient-rb/impl/evaluator.rb +379 -131
  12. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
  13. data/lib/ldclient-rb/impl/evaluator_helpers.rb +31 -34
  14. data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
  15. data/lib/ldclient-rb/impl/event_sender.rb +6 -6
  16. data/lib/ldclient-rb/impl/event_summarizer.rb +12 -7
  17. data/lib/ldclient-rb/impl/event_types.rb +18 -30
  18. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +7 -7
  19. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +29 -29
  20. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +8 -8
  21. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +92 -12
  22. data/lib/ldclient-rb/impl/model/clause.rb +45 -0
  23. data/lib/ldclient-rb/impl/model/feature_flag.rb +232 -0
  24. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +8 -121
  25. data/lib/ldclient-rb/impl/model/segment.rb +132 -0
  26. data/lib/ldclient-rb/impl/model/serialization.rb +52 -12
  27. data/lib/ldclient-rb/impl/repeating_task.rb +1 -1
  28. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
  29. data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
  30. data/lib/ldclient-rb/impl/util.rb +2 -2
  31. data/lib/ldclient-rb/in_memory_store.rb +2 -2
  32. data/lib/ldclient-rb/integrations/consul.rb +1 -1
  33. data/lib/ldclient-rb/integrations/dynamodb.rb +1 -1
  34. data/lib/ldclient-rb/integrations/file_data.rb +3 -3
  35. data/lib/ldclient-rb/integrations/redis.rb +4 -4
  36. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +218 -62
  37. data/lib/ldclient-rb/integrations/test_data.rb +16 -12
  38. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +9 -9
  39. data/lib/ldclient-rb/interfaces.rb +14 -14
  40. data/lib/ldclient-rb/ldclient.rb +94 -144
  41. data/lib/ldclient-rb/memoized_value.rb +1 -1
  42. data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
  43. data/lib/ldclient-rb/polling.rb +2 -2
  44. data/lib/ldclient-rb/reference.rb +274 -0
  45. data/lib/ldclient-rb/requestor.rb +5 -5
  46. data/lib/ldclient-rb/stream.rb +7 -8
  47. data/lib/ldclient-rb/util.rb +4 -19
  48. data/lib/ldclient-rb/version.rb +1 -1
  49. data/lib/ldclient-rb.rb +2 -3
  50. metadata +34 -17
  51. data/lib/ldclient-rb/file_data_source.rb +0 -23
  52. data/lib/ldclient-rb/newrelic.rb +0 -17
  53. data/lib/ldclient-rb/redis_store.rb +0 -88
  54. data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -163,30 +163,30 @@ module LaunchDarkly
163
163
  end
164
164
 
165
165
  #
166
- # Queries the store for a snapshot of the current segment state for a specific user.
166
+ # Queries the store for a snapshot of the current segment state for a specific context.
167
167
  #
168
- # The user_hash is a base64-encoded string produced by hashing the user key as defined by
168
+ # The context_hash is a base64-encoded string produced by hashing the context key as defined by
169
169
  # the Big Segments specification; the store implementation does not need to know the details
170
170
  # of how this is done, because it deals only with already-hashed keys, but the string can be
171
171
  # assumed to only contain characters that are valid in base64.
172
172
  #
173
- # The return value should be either a Hash, or nil if the user is not referenced in any big
173
+ # The return value should be either a Hash, or nil if the context is not referenced in any big
174
174
  # segments. Each key in the Hash is a "segment reference", which is how segments are
175
175
  # identified in Big Segment data. This string is not identical to the segment key-- the SDK
176
176
  # will add other information. The store implementation should not be concerned with the
177
- # format of the string. Each value in the Hash is true if the user is explicitly included in
178
- # the segment, false if the user is explicitly excluded from the segment-- and is not also
177
+ # format of the string. Each value in the Hash is true if the context is explicitly included in
178
+ # the segment, false if the context is explicitly excluded from the segment-- and is not also
179
179
  # explicitly included (that is, if both an include and an exclude existed in the data, the
180
- # include would take precedence). If the user's status in a particular segment is undefined,
180
+ # include would take precedence). If the context's status in a particular segment is undefined,
181
181
  # there should be no key or value for that segment.
182
182
  #
183
183
  # This Hash may be cached by the SDK, so it should not be modified after it is created. It
184
184
  # is a snapshot of the segment membership state at one point in time.
185
185
  #
186
- # @param user_hash [String]
187
- # @return [Hash] true/false values for Big Segments that reference this user
186
+ # @param context_hash [String]
187
+ # @return [Hash] true/false values for Big Segments that reference this context
188
188
  #
189
- def get_membership(user_hash)
189
+ def get_membership(context_hash)
190
190
  end
191
191
 
192
192
  #
@@ -216,7 +216,7 @@ module LaunchDarkly
216
216
  #
217
217
  # Information about the status of a Big Segment store, provided by {BigSegmentStoreStatusProvider}.
218
218
  #
219
- # Big Segments are a specific type of user segments. For more information, read the LaunchDarkly
219
+ # Big Segments are a specific type of segments. For more information, read the LaunchDarkly
220
220
  # documentation: https://docs.launchdarkly.com/home/users/big-segments
221
221
  #
222
222
  class BigSegmentStoreStatus
@@ -226,11 +226,11 @@ module LaunchDarkly
226
226
  end
227
227
 
228
228
  # True if the Big Segment store is able to respond to queries, so that the SDK can evaluate
229
- # whether a user is in a segment or not.
229
+ # whether a context is in a segment or not.
230
230
  #
231
231
  # If this property is false, the store is not able to make queries (for instance, it may not have
232
232
  # a valid database connection). In this case, the SDK will treat any reference to a Big Segment
233
- # as if no users are included in that segment. Also, the {EvaluationReason} associated with
233
+ # as if no contexts are included in that segment. Also, the {EvaluationReason} associated with
234
234
  # with any flag evaluation that references a Big Segment when the store is not available will
235
235
  # have a `big_segments_status` of `STORE_ERROR`.
236
236
  #
@@ -259,14 +259,14 @@ module LaunchDarkly
259
259
  #
260
260
  # The Big Segment store is the component that receives information about Big Segments, normally
261
261
  # from a database populated by the LaunchDarkly Relay Proxy. Big Segments are a specific type
262
- # of user segments. For more information, read the LaunchDarkly documentation:
262
+ # of segments. For more information, read the LaunchDarkly documentation:
263
263
  # https://docs.launchdarkly.com/home/users/big-segments
264
264
  #
265
265
  # An implementation of this interface is returned by {LDClient#big_segment_store_status_provider}.
266
266
  # Application code never needs to implement this interface.
267
267
  #
268
268
  # There are two ways to interact with the status. One is to simply get the current status; if its
269
- # `available` property is true, then the SDK is able to evaluate user membership in Big Segments,
269
+ # `available` property is true, then the SDK is able to evaluate context membership in Big Segments,
270
270
  # and the `stale`` property indicates whether the data might be out of date.
271
271
  #
272
272
  # The other way is to subscribe to status change notifications. Applications may wish to know if
@@ -59,7 +59,7 @@ module LaunchDarkly
59
59
 
60
60
  get_flag = lambda { |key| @store.get(FEATURES, key) }
61
61
  get_segment = lambda { |key| @store.get(SEGMENTS, key) }
62
- get_big_segments_membership = lambda { |key| @big_segment_store_manager.get_user_membership(key) }
62
+ get_big_segments_membership = lambda { |key| @big_segment_store_manager.get_context_membership(key) }
63
63
  @evaluator = LaunchDarkly::Impl::Evaluator.new(get_flag, get_segment, get_big_segments_membership, @config.logger)
64
64
 
65
65
  if !@config.offline? && @config.send_events && !@config.diagnostic_opt_out?
@@ -120,26 +120,20 @@ module LaunchDarkly
120
120
  end
121
121
 
122
122
  #
123
- # @param key [String] the feature flag key
124
- # @param user [Hash] the user properties
125
- # @param default [Boolean] (false) the value to use if the flag cannot be evaluated
126
- # @return [Boolean] the flag value
127
- # @deprecated Use {#variation} instead.
128
- #
129
- def toggle?(key, user, default = false)
130
- @config.logger.warn { "[LDClient] toggle? is deprecated. Use variation instead" }
131
- variation(key, user, default)
132
- end
133
-
134
- #
135
- # Creates a hash string that can be used by the JavaScript SDK to identify a user.
123
+ # Creates a hash string that can be used by the JavaScript SDK to identify a context.
136
124
  # For more information, see [Secure mode](https://docs.launchdarkly.com/sdk/features/secure-mode#ruby).
137
125
  #
138
- # @param user [Hash] the user properties
139
- # @return [String] a hash string
126
+ # @param context [Hash, LDContext]
127
+ # @return [String, nil] a hash string or nil if the provided context was invalid
140
128
  #
141
- def secure_mode_hash(user)
142
- OpenSSL::HMAC.hexdigest("sha256", @sdk_key, user[:key].to_s)
129
+ def secure_mode_hash(context)
130
+ context = Impl::Context::make_context(context)
131
+ unless context.valid?
132
+ @config.logger.warn("secure_mode_hash called with invalid context: #{context.error}")
133
+ return nil
134
+ end
135
+
136
+ OpenSSL::HMAC.hexdigest("sha256", @sdk_key, context.fully_qualified_key)
143
137
  end
144
138
 
145
139
  #
@@ -165,44 +159,22 @@ module LaunchDarkly
165
159
  end
166
160
 
167
161
  #
168
- # Determines the variation of a feature flag to present to a user.
169
- #
170
- # At a minimum, the user hash should contain a `:key`, which should be the unique
171
- # identifier for your user (or, for an anonymous user, a session identifier or
172
- # cookie).
173
- #
174
- # Other supported user attributes include IP address, country code, and an arbitrary hash of
175
- # custom attributes. For more about the supported user properties and how they work in
176
- # LaunchDarkly, see [Targeting users](https://docs.launchdarkly.com/home/flags/targeting-users).
177
- #
178
- # The optional `:privateAttributeNames` user property allows you to specify a list of
179
- # attribute names that should not be sent back to LaunchDarkly.
180
- # [Private attributes](https://docs.launchdarkly.com/home/users/attributes#creating-private-user-attributes)
181
- # can also be configured globally in {Config}.
182
- #
183
- # @example Basic user hash
184
- # {key: "my-user-id"}
185
- #
186
- # @example More complete user hash
187
- # {key: "my-user-id", ip: "127.0.0.1", country: "US", custom: {customer_rank: 1000}}
188
- #
189
- # @example User with a private attribute
190
- # {key: "my-user-id", email: "email@example.com", privateAttributeNames: ["email"]}
162
+ # Determines the variation of a feature flag to present for a context.
191
163
  #
192
164
  # @param key [String] the unique feature key for the feature flag, as shown
193
165
  # on the LaunchDarkly dashboard
194
- # @param user [Hash] a hash containing parameters for the end user requesting the flag
166
+ # @param context [Hash, LDContext] a hash or LDContext instance describing the context requesting the flag
195
167
  # @param default the default value of the flag; this is used if there is an error
196
168
  # condition making it impossible to find or evaluate the flag
197
169
  #
198
- # @return the variation to show the user, or the default value if there's an an error
170
+ # @return the variation for the provided context, or the default value if there's an an error
199
171
  #
200
- def variation(key, user, default)
201
- evaluate_internal(key, user, default, false).value
172
+ def variation(key, context, default)
173
+ evaluate_internal(key, context, default, false).value
202
174
  end
203
175
 
204
176
  #
205
- # Determines the variation of a feature flag for a user, like {#variation}, but also
177
+ # Determines the variation of a feature flag for a context, like {#variation}, but also
206
178
  # provides additional information about how this value was calculated.
207
179
  #
208
180
  # The return value of `variation_detail` is an {EvaluationDetail} object, which has
@@ -218,43 +190,48 @@ module LaunchDarkly
218
190
  #
219
191
  # @param key [String] the unique feature key for the feature flag, as shown
220
192
  # on the LaunchDarkly dashboard
221
- # @param user [Hash] a hash containing parameters for the end user requesting the flag
193
+ # @param context [Hash, LDContext] a hash or object describing the context requesting the flag,
222
194
  # @param default the default value of the flag; this is used if there is an error
223
195
  # condition making it impossible to find or evaluate the flag
224
196
  #
225
197
  # @return [EvaluationDetail] an object describing the result
226
198
  #
227
- def variation_detail(key, user, default)
228
- evaluate_internal(key, user, default, true)
199
+ def variation_detail(key, context, default)
200
+ evaluate_internal(key, context, default, true)
229
201
  end
230
202
 
231
203
  #
232
- # Registers the user. This method simply creates an analytics event containing the user
233
- # properties, so that LaunchDarkly will know about that user if it does not already.
204
+ # Registers the context. This method simply creates an analytics event containing the context
205
+ # properties, so that LaunchDarkly will know about that context if it does not already.
234
206
  #
235
- # Calling {#variation} or {#variation_detail} also sends the user information to
207
+ # Calling {#variation} or {#variation_detail} also sends the context information to
236
208
  # LaunchDarkly (if events are enabled), so you only need to use {#identify} if you
237
- # want to identify the user without evaluating a flag.
209
+ # want to identify the context without evaluating a flag.
238
210
  #
239
211
  # Note that event delivery is asynchronous, so the event may not actually be sent
240
212
  # until later; see {#flush}.
241
213
  #
242
- # @param user [Hash] The user to register; this can have all the same user properties
243
- # described in {#variation}
214
+ # @param context [Hash, LDContext] a hash or object describing the context to register
244
215
  # @return [void]
245
216
  #
246
- def identify(user)
247
- if !user || user[:key].nil? || user[:key].empty?
248
- @config.logger.warn("Identify called with nil user or empty user key!")
217
+ def identify(context)
218
+ context = LaunchDarkly::Impl::Context.make_context(context)
219
+ unless context.valid?
220
+ @config.logger.warn("Identify called with invalid context: #{context.error}")
221
+ return
222
+ end
223
+
224
+ if context.key == ""
225
+ @config.logger.warn("Identify called with empty key")
249
226
  return
250
227
  end
251
- sanitize_user(user)
252
- @event_processor.record_identify_event(user)
228
+
229
+ @event_processor.record_identify_event(context)
253
230
  end
254
231
 
255
232
  #
256
- # Tracks that a user performed an event. This method creates a "custom" analytics event
257
- # containing the specified event name (key), user properties, and optional data.
233
+ # Tracks that a context performed an event. This method creates a "custom" analytics event
234
+ # containing the specified event name (key), context properties, and optional data.
258
235
  #
259
236
  # Note that event delivery is asynchronous, so the event may not actually be sent
260
237
  # until later; see {#flush}.
@@ -265,8 +242,7 @@ module LaunchDarkly
265
242
  # for the latest status.
266
243
  #
267
244
  # @param event_name [String] The name of the event
268
- # @param user [Hash] The user to register; this can have all the same user properties
269
- # described in {#variation}
245
+ # @param context [Hash, LDContext] a hash or object describing the context to track
270
246
  # @param data [Hash] An optional hash containing any additional data associated with the event
271
247
  # @param metric_value [Number] A numeric value used by the LaunchDarkly experimentation
272
248
  # feature in numeric custom metrics. Can be omitted if this event is used by only
@@ -274,52 +250,22 @@ module LaunchDarkly
274
250
  # for Data Export.
275
251
  # @return [void]
276
252
  #
277
- def track(event_name, user, data = nil, metric_value = nil)
278
- if !user || user[:key].nil?
279
- @config.logger.warn("Track called with nil user or nil user key!")
280
- return
281
- end
282
- sanitize_user(user)
283
- @event_processor.record_custom_event(user, event_name, data, metric_value)
284
- end
285
-
286
- #
287
- # Associates a new and old user object for analytics purposes via an alias event.
288
- #
289
- # @param current_context [Hash] The current version of a user.
290
- # @param previous_context [Hash] The previous version of a user.
291
- # @return [void]
292
- #
293
- def alias(current_context, previous_context)
294
- if !current_context || current_context[:key].nil? || !previous_context || previous_context[:key].nil?
295
- @config.logger.warn("Alias called with nil user or nil user key!")
253
+ def track(event_name, context, data = nil, metric_value = nil)
254
+ context = LaunchDarkly::Impl::Context.make_context(context)
255
+ unless context.valid?
256
+ @config.logger.warn("Track called with invalid context: #{context.error}")
296
257
  return
297
258
  end
298
- sanitize_user(current_context)
299
- sanitize_user(previous_context)
300
- @event_processor.record_alias_event(current_context, previous_context)
301
- end
302
259
 
303
- #
304
- # Returns all feature flag values for the given user.
305
- #
306
- # @deprecated Please use {#all_flags_state} instead. Current versions of the
307
- # client-side SDK will not generate analytics events correctly if you pass the
308
- # result of `all_flags`.
309
- #
310
- # @param user [Hash] The end user requesting the feature flags
311
- # @return [Hash] a hash of feature flag keys to values
312
- #
313
- def all_flags(user)
314
- all_flags_state(user).values_map
260
+ @event_processor.record_custom_event(context, event_name, data, metric_value)
315
261
  end
316
262
 
317
263
  #
318
- # Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given user,
264
+ # Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given context,
319
265
  # including the flag values and also metadata that can be used on the front end. This method does not
320
266
  # send analytics events back to LaunchDarkly.
321
267
  #
322
- # @param user [Hash] The end user requesting the feature flags
268
+ # @param context [Hash, LDContext] a hash or object describing the context requesting the flags,
323
269
  # @param options [Hash] Optional parameters to control how the state is generated
324
270
  # @option options [Boolean] :client_side_only (false) True if only flags marked for use with the
325
271
  # client-side SDK should be included in the state. By default, all flags are included.
@@ -331,10 +277,10 @@ module LaunchDarkly
331
277
  # of the JSON data if you are passing the flag state to the front end.
332
278
  # @return [FeatureFlagsState] a {FeatureFlagsState} object which can be serialized to JSON
333
279
  #
334
- def all_flags_state(user, options={})
280
+ def all_flags_state(context, options={})
335
281
  return FeatureFlagsState.new(false) if @config.offline?
336
282
 
337
- if !initialized?
283
+ unless initialized?
338
284
  if @store.initialized?
339
285
  @config.logger.warn { "Called all_flags_state before client initialization; using last known values from data store" }
340
286
  else
@@ -343,8 +289,9 @@ module LaunchDarkly
343
289
  end
344
290
  end
345
291
 
346
- unless user && !user[:key].nil?
347
- @config.logger.error { "[LDClient] User and user key must be specified in all_flags_state" }
292
+ context = Impl::Context::make_context(context)
293
+ unless context.valid?
294
+ @config.logger.error { "[LDClient] Context was invalid for all_flags_state (#{context.error})" }
348
295
  return FeatureFlagsState.new(false)
349
296
  end
350
297
 
@@ -364,13 +311,13 @@ module LaunchDarkly
364
311
  next
365
312
  end
366
313
  begin
367
- detail = @evaluator.evaluate(f, user).detail
314
+ detail = @evaluator.evaluate(f, context).detail
368
315
  rescue => exn
369
316
  detail = EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_EXCEPTION))
370
317
  Util.log_exception(@config.logger, "Error evaluating flag \"#{k}\" in all_flags_state", exn)
371
318
  end
372
319
 
373
- requires_experiment_data = is_experiment(f, detail.reason)
320
+ requires_experiment_data = experiment?(f, detail.reason)
374
321
  flag_state = {
375
322
  key: f[:key],
376
323
  value: detail.value,
@@ -425,32 +372,34 @@ module LaunchDarkly
425
372
  end
426
373
  end
427
374
 
375
+ # @param context [Hash, LDContext]
428
376
  # @return [EvaluationDetail]
429
- def evaluate_internal(key, user, default, with_reasons)
377
+ def evaluate_internal(key, context, default, with_reasons)
430
378
  if @config.offline?
431
379
  return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
432
380
  end
433
381
 
434
- unless user
435
- @config.logger.error { "[LDClient] Must specify user" }
382
+ if context.nil?
383
+ @config.logger.error { "[LDClient] Must specify context" }
436
384
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
437
385
  return detail
438
386
  end
439
387
 
440
- if user[:key].nil?
441
- @config.logger.warn { "[LDClient] Variation called with nil user key; returning default value" }
388
+ context = Impl::Context::make_context(context)
389
+ unless context.valid?
390
+ @config.logger.error { "[LDClient] Context was invalid for flag evaluation (#{context.error}); returning default value" }
442
391
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
443
392
  return detail
444
393
  end
445
394
 
446
- if !initialized?
395
+ unless initialized?
447
396
  if @store.initialized?
448
397
  @config.logger.warn { "[LDClient] Client has not finished initializing; using last known values from feature store" }
449
398
  else
450
399
  @config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
451
400
  detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
452
- record_unknown_flag_eval(key, user, default, detail.reason, with_reasons)
453
- return detail
401
+ record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
402
+ return detail
454
403
  end
455
404
  end
456
405
 
@@ -459,35 +408,35 @@ module LaunchDarkly
459
408
  if feature.nil?
460
409
  @config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
461
410
  detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
462
- record_unknown_flag_eval(key, user, default, detail.reason, with_reasons)
411
+ record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
463
412
  return detail
464
413
  end
465
414
 
466
415
  begin
467
- res = @evaluator.evaluate(feature, user)
468
- if !res.prereq_evals.nil?
416
+ res = @evaluator.evaluate(feature, context)
417
+ unless res.prereq_evals.nil?
469
418
  res.prereq_evals.each do |prereq_eval|
470
- record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag, user, prereq_eval.detail, with_reasons)
419
+ record_prereq_flag_eval(prereq_eval.prereq_flag, prereq_eval.prereq_of_flag, context, prereq_eval.detail, with_reasons)
471
420
  end
472
421
  end
473
422
  detail = res.detail
474
423
  if detail.default_value?
475
424
  detail = EvaluationDetail.new(default, nil, detail.reason)
476
425
  end
477
- record_flag_eval(feature, user, detail, default, with_reasons)
478
- return detail
426
+ record_flag_eval(feature, context, detail, default, with_reasons)
427
+ detail
479
428
  rescue => exn
480
429
  Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
481
430
  detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
482
- record_flag_eval_error(feature, user, default, detail.reason, with_reasons)
483
- return detail
431
+ record_flag_eval_error(feature, context, default, detail.reason, with_reasons)
432
+ detail
484
433
  end
485
434
  end
486
435
 
487
- private def record_flag_eval(flag, user, detail, default, with_reasons)
488
- add_experiment_data = is_experiment(flag, detail.reason)
436
+ private def record_flag_eval(flag, context, detail, default, with_reasons)
437
+ add_experiment_data = experiment?(flag, detail.reason)
489
438
  @event_processor.record_eval_event(
490
- user,
439
+ context,
491
440
  flag[:key],
492
441
  flag[:version],
493
442
  detail.variation_index,
@@ -499,11 +448,11 @@ module LaunchDarkly
499
448
  nil
500
449
  )
501
450
  end
502
-
503
- private def record_prereq_flag_eval(prereq_flag, prereq_of_flag, user, detail, with_reasons)
504
- add_experiment_data = is_experiment(prereq_flag, detail.reason)
451
+
452
+ private def record_prereq_flag_eval(prereq_flag, prereq_of_flag, context, detail, with_reasons)
453
+ add_experiment_data = experiment?(prereq_flag, detail.reason)
505
454
  @event_processor.record_eval_event(
506
- user,
455
+ context,
507
456
  prereq_flag[:key],
508
457
  prereq_flag[:version],
509
458
  detail.variation_index,
@@ -515,19 +464,26 @@ module LaunchDarkly
515
464
  prereq_of_flag[:key]
516
465
  )
517
466
  end
518
-
519
- private def record_flag_eval_error(flag, user, default, reason, with_reasons)
520
- @event_processor.record_eval_event(user, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
467
+
468
+ private def record_flag_eval_error(flag, context, default, reason, with_reasons)
469
+ @event_processor.record_eval_event(context, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
521
470
  flag[:trackEvents], flag[:debugEventsUntilDate], nil)
522
471
  end
523
472
 
524
- private def record_unknown_flag_eval(flag_key, user, default, reason, with_reasons)
525
- @event_processor.record_eval_event(user, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
473
+ #
474
+ # @param flag_key [String]
475
+ # @param context [LaunchDarkly::LDContext]
476
+ # @param default [any]
477
+ # @param reason [LaunchDarkly::EvaluationReason]
478
+ # @param with_reasons [Boolean]
479
+ #
480
+ private def record_unknown_flag_eval(flag_key, context, default, reason, with_reasons)
481
+ @event_processor.record_eval_event(context, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
526
482
  false, nil, nil)
527
483
  end
528
484
 
529
- private def is_experiment(flag, reason)
530
- return false if !reason
485
+ private def experiment?(flag, reason)
486
+ return false unless reason
531
487
 
532
488
  if reason.in_experiment
533
489
  return true
@@ -536,7 +492,7 @@ module LaunchDarkly
536
492
  case reason[:kind]
537
493
  when 'RULE_MATCH'
538
494
  index = reason[:ruleIndex]
539
- if !index.nil?
495
+ unless index.nil?
540
496
  rules = flag[:rules] || []
541
497
  return index >= 0 && index < rules.length && rules[index][:trackEvents]
542
498
  end
@@ -545,12 +501,6 @@ module LaunchDarkly
545
501
  end
546
502
  false
547
503
  end
548
-
549
- private def sanitize_user(user)
550
- if user[:key]
551
- user[:key] = user[:key].to_s
552
- end
553
- end
554
504
  end
555
505
 
556
506
  #
@@ -14,7 +14,7 @@ module LaunchDarkly
14
14
 
15
15
  def get
16
16
  @mutex.synchronize do
17
- if !@inited
17
+ unless @inited
18
18
  @value = @generator.call
19
19
  @inited = true
20
20
  end
@@ -17,7 +17,7 @@ module LaunchDarkly
17
17
  # Attempts to submit a job, but only if a worker is available. Unlike the regular post method,
18
18
  # this returns a value: true if the job was submitted, false if all workers are busy.
19
19
  def post
20
- if !@semaphore.try_acquire(1)
20
+ unless @semaphore.try_acquire(1)
21
21
  return
22
22
  end
23
23
  @pool.post do
@@ -43,8 +43,8 @@ module LaunchDarkly
43
43
  end
44
44
  rescue UnexpectedResponseError => e
45
45
  message = Util.http_error_message(e.status, "polling request", "will retry")
46
- @config.logger.error { "[LDClient] #{message}" };
47
- if !Util.http_error_recoverable?(e.status)
46
+ @config.logger.error { "[LDClient] #{message}" }
47
+ unless Util.http_error_recoverable?(e.status)
48
48
  @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
49
49
  stop
50
50
  end