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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -4
  3. data/lib/ldclient-rb/config.rb +112 -62
  4. data/lib/ldclient-rb/context.rb +444 -0
  5. data/lib/ldclient-rb/evaluation_detail.rb +26 -22
  6. data/lib/ldclient-rb/events.rb +256 -146
  7. data/lib/ldclient-rb/flags_state.rb +26 -15
  8. data/lib/ldclient-rb/impl/big_segments.rb +18 -18
  9. data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
  10. data/lib/ldclient-rb/impl/context.rb +96 -0
  11. data/lib/ldclient-rb/impl/context_filter.rb +145 -0
  12. data/lib/ldclient-rb/impl/data_source.rb +188 -0
  13. data/lib/ldclient-rb/impl/data_store.rb +59 -0
  14. data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
  15. data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
  16. data/lib/ldclient-rb/impl/evaluator.rb +386 -142
  17. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
  18. data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
  19. data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
  20. data/lib/ldclient-rb/impl/event_sender.rb +7 -6
  21. data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
  22. data/lib/ldclient-rb/impl/event_types.rb +136 -0
  23. data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
  24. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +19 -7
  25. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +38 -30
  26. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +24 -11
  27. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +109 -12
  28. data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
  29. data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
  30. data/lib/ldclient-rb/impl/model/clause.rb +45 -0
  31. data/lib/ldclient-rb/impl/model/feature_flag.rb +255 -0
  32. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
  33. data/lib/ldclient-rb/impl/model/segment.rb +132 -0
  34. data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
  35. data/lib/ldclient-rb/impl/repeating_task.rb +3 -4
  36. data/lib/ldclient-rb/impl/sampler.rb +25 -0
  37. data/lib/ldclient-rb/impl/store_client_wrapper.rb +102 -8
  38. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
  39. data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
  40. data/lib/ldclient-rb/impl/util.rb +59 -1
  41. data/lib/ldclient-rb/in_memory_store.rb +9 -2
  42. data/lib/ldclient-rb/integrations/consul.rb +2 -2
  43. data/lib/ldclient-rb/integrations/dynamodb.rb +2 -2
  44. data/lib/ldclient-rb/integrations/file_data.rb +4 -4
  45. data/lib/ldclient-rb/integrations/redis.rb +5 -5
  46. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +287 -62
  47. data/lib/ldclient-rb/integrations/test_data.rb +18 -14
  48. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +20 -9
  49. data/lib/ldclient-rb/interfaces.rb +600 -14
  50. data/lib/ldclient-rb/ldclient.rb +314 -134
  51. data/lib/ldclient-rb/memoized_value.rb +1 -1
  52. data/lib/ldclient-rb/migrations.rb +230 -0
  53. data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
  54. data/lib/ldclient-rb/polling.rb +52 -6
  55. data/lib/ldclient-rb/reference.rb +274 -0
  56. data/lib/ldclient-rb/requestor.rb +9 -11
  57. data/lib/ldclient-rb/stream.rb +96 -34
  58. data/lib/ldclient-rb/util.rb +97 -14
  59. data/lib/ldclient-rb/version.rb +1 -1
  60. data/lib/ldclient-rb.rb +3 -4
  61. metadata +65 -23
  62. data/lib/ldclient-rb/event_summarizer.rb +0 -55
  63. data/lib/ldclient-rb/file_data_source.rb +0 -23
  64. data/lib/ldclient-rb/impl/event_factory.rb +0 -126
  65. data/lib/ldclient-rb/newrelic.rb +0 -17
  66. data/lib/ldclient-rb/redis_store.rb +0 -88
  67. data/lib/ldclient-rb/user_filter.rb +0 -52
@@ -0,0 +1,444 @@
1
+ require 'set'
2
+ require 'ldclient-rb/impl/context'
3
+ require 'ldclient-rb/reference'
4
+
5
+ module LaunchDarkly
6
+ # LDContext is a collection of attributes that can be referenced in flag
7
+ # evaluations and analytics events.
8
+ #
9
+ # To create an LDContext of a single kind, such as a user, you may use
10
+ # {LDContext#create} or {LDContext#with_key}.
11
+ #
12
+ # To create an LDContext with multiple kinds, use {LDContext#create_multi}.
13
+ #
14
+ # Each factory method will always return an LDContext. However, that
15
+ # LDContext may be invalid. You can check the validity of the resulting
16
+ # context, and the associated errors by calling {LDContext#valid?} and
17
+ # {LDContext#error}
18
+ class LDContext
19
+ KIND_DEFAULT = "user"
20
+ KIND_MULTI = "multi"
21
+
22
+ ERR_NOT_HASH = 'context data is not a hash'
23
+ private_constant :ERR_NOT_HASH
24
+ ERR_KEY_EMPTY = 'context key must not be null or empty'
25
+ private_constant :ERR_KEY_EMPTY
26
+ ERR_KIND_MULTI_NON_CONTEXT_ARRAY = 'context data must be an array of valid LDContexts'
27
+ private_constant :ERR_KIND_MULTI_NON_CONTEXT_ARRAY
28
+ ERR_KIND_MULTI_CANNOT_CONTAIN_MULTI = 'multi-kind context cannot contain another multi-kind context'
29
+ private_constant :ERR_KIND_MULTI_CANNOT_CONTAIN_MULTI
30
+ ERR_KIND_MULTI_WITH_NO_KINDS = 'multi-context must contain at least one kind'
31
+ private_constant :ERR_KIND_MULTI_WITH_NO_KINDS
32
+ ERR_KIND_MULTI_DUPLICATES = 'multi-kind context cannot have same kind more than once'
33
+ private_constant :ERR_KIND_MULTI_DUPLICATES
34
+ ERR_CUSTOM_NON_HASH = 'context custom must be a hash'
35
+ private_constant :ERR_CUSTOM_NON_HASH
36
+ ERR_PRIVATE_NON_ARRAY = 'context private attributes must be an array'
37
+
38
+ # @return [String, nil] Returns the key for this context
39
+ attr_reader :key
40
+
41
+ # @return [String, nil] Returns the fully qualified key for this context
42
+ attr_reader :fully_qualified_key
43
+
44
+ # @return [String, nil] Returns the kind for this context
45
+ attr_reader :kind
46
+
47
+ # @return [String, nil] Returns the error associated with this LDContext if invalid
48
+ attr_reader :error
49
+
50
+ # @return [Array<Reference>] Returns the private attributes associated with this LDContext
51
+ attr_reader :private_attributes
52
+
53
+ #
54
+ # @private
55
+ # @param key [String, nil]
56
+ # @param fully_qualified_key [String, nil]
57
+ # @param kind [String, nil]
58
+ # @param name [String, nil]
59
+ # @param anonymous [Boolean, nil]
60
+ # @param attributes [Hash, nil]
61
+ # @param private_attributes [Array<String>, nil]
62
+ # @param error [String, nil]
63
+ # @param contexts [Array<LDContext>, nil]
64
+ #
65
+ def initialize(key, fully_qualified_key, kind, name = nil, anonymous = nil, attributes = nil, private_attributes = nil, error = nil, contexts = nil)
66
+ @key = key
67
+ @fully_qualified_key = fully_qualified_key
68
+ @kind = kind
69
+ @name = name
70
+ @anonymous = anonymous || false
71
+ @attributes = attributes
72
+ @private_attributes = []
73
+ (private_attributes || []).each do |attribute|
74
+ reference = Reference.create(attribute)
75
+ @private_attributes << reference if reference.error.nil?
76
+ end
77
+ @error = error
78
+ @contexts = contexts
79
+ @is_multi = !contexts.nil?
80
+ end
81
+ private_class_method :new
82
+
83
+ #
84
+ # @return [Boolean] Is this LDContext a multi-kind context?
85
+ #
86
+ def multi_kind?
87
+ @is_multi
88
+ end
89
+
90
+ #
91
+ # @return [Boolean] Determine if this LDContext is considered valid
92
+ #
93
+ def valid?
94
+ @error.nil?
95
+ end
96
+
97
+ #
98
+ # Returns a hash mapping each context's kind to its key.
99
+ #
100
+ # @return [Hash<Symbol, String>]
101
+ #
102
+ def keys
103
+ return {} unless valid?
104
+ return Hash[kind, key] unless multi_kind?
105
+
106
+ @contexts.map { |c| [c.kind, c.key] }.to_h
107
+ end
108
+
109
+ #
110
+ # Returns an array of context kinds.
111
+ #
112
+ # @return [Array<String>]
113
+ #
114
+ def kinds
115
+ return [] unless valid?
116
+ return [kind] unless multi_kind?
117
+
118
+ @contexts.map { |c| c.kind }
119
+ end
120
+
121
+ #
122
+ # Return an array of top level attribute keys (excluding built-in attributes)
123
+ #
124
+ # @return [Array<Symbol>]
125
+ #
126
+ def get_custom_attribute_names
127
+ return [] if @attributes.nil?
128
+
129
+ @attributes.keys
130
+ end
131
+
132
+ #
133
+ # get_value looks up the value of any attribute of the Context by name.
134
+ # This includes only attributes that are addressable in evaluations-- not
135
+ # metadata such as private attributes.
136
+ #
137
+ # For a single-kind context, the attribute name can be any custom attribute.
138
+ # It can also be one of the built-in ones like "kind", "key", or "name".
139
+ #
140
+ # For a multi-kind context, the only supported attribute name is "kind".
141
+ # Use {#individual_context} to inspect a Context for a particular kind and
142
+ # then get its attributes.
143
+ #
144
+ # This method does not support complex expressions for getting individual
145
+ # values out of JSON objects or arrays, such as "/address/street". Use
146
+ # {#get_value_for_reference} for that purpose.
147
+ #
148
+ # If the value is found, the return value is the attribute value;
149
+ # otherwise, it is nil.
150
+ #
151
+ # @param attribute [String, Symbol]
152
+ # @return [any]
153
+ #
154
+ def get_value(attribute)
155
+ reference = Reference.create_literal(attribute)
156
+ get_value_for_reference(reference)
157
+ end
158
+
159
+ #
160
+ # get_value_for_reference looks up the value of any attribute of the
161
+ # Context, or a value contained within an attribute, based on a {Reference}
162
+ # instance. This includes only attributes that are addressable in
163
+ # evaluations-- not metadata such as private attributes.
164
+ #
165
+ # This implements the same behavior that the SDK uses to resolve attribute
166
+ # references during a flag evaluation. In a single-kind context, the
167
+ # {Reference} can represent a simple attribute name-- either a built-in one
168
+ # like "name" or "key", or a custom attribute -- or, it can be a
169
+ # slash-delimited path using a JSON-Pointer-like syntax. See {Reference}
170
+ # for more details.
171
+ #
172
+ # For a multi-kind context, the only supported attribute name is "kind".
173
+ # Use {#individual_context} to inspect a Context for a particular kind and
174
+ # then get its attributes.
175
+ #
176
+ # If the value is found, the return value is the attribute value;
177
+ # otherwise, it is nil.
178
+ #
179
+ # @param reference [Reference]
180
+ # @return [any]
181
+ #
182
+ def get_value_for_reference(reference)
183
+ return nil unless valid?
184
+ return nil unless reference.is_a?(Reference)
185
+ return nil unless reference.error.nil?
186
+
187
+ first_component = reference.component(0)
188
+ return nil if first_component.nil?
189
+
190
+ if multi_kind?
191
+ if reference.depth == 1 && first_component == :kind
192
+ return kind
193
+ end
194
+
195
+ # Multi-kind contexts have no other addressable attributes
196
+ return nil
197
+ end
198
+
199
+ value = get_top_level_addressable_attribute_single_kind(first_component)
200
+ return nil if value.nil?
201
+
202
+ (1...reference.depth).each do |i|
203
+ name = reference.component(i)
204
+
205
+ return nil unless value.is_a?(Hash)
206
+ return nil unless value.has_key?(name)
207
+
208
+ value = value[name]
209
+ end
210
+
211
+ value
212
+ end
213
+
214
+ #
215
+ # Returns the number of context kinds in this context.
216
+ #
217
+ # For a valid individual context, this returns 1. For a multi-context, it
218
+ # returns the number of context kinds. For an invalid context, it returns
219
+ # zero.
220
+ #
221
+ # @return [Integer] the number of context kinds
222
+ #
223
+ def individual_context_count
224
+ return 0 unless valid?
225
+ return 1 if @contexts.nil?
226
+ @contexts.count
227
+ end
228
+
229
+ #
230
+ # Returns the single-kind LDContext corresponding to one of the kinds in
231
+ # this context.
232
+ #
233
+ # The `kind` parameter can be either a number representing a zero-based
234
+ # index, or a string representing a context kind.
235
+ #
236
+ # If this method is called on a single-kind LDContext, then the only
237
+ # allowable value for `kind` is either zero or the same value as {#kind},
238
+ # and the return value on success is the same LDContext.
239
+ #
240
+ # If the method is called on a multi-context, and `kind` is a number, it
241
+ # must be a non-negative index that is less than the number of kinds (that
242
+ # is, less than the return value of {#individual_context_count}, and the
243
+ # return value on success is one of the individual LDContexts within. Or,
244
+ # if `kind` is a string, it must match the context kind of one of the
245
+ # individual contexts.
246
+ #
247
+ # If there is no context corresponding to `kind`, the method returns nil.
248
+ #
249
+ # @param kind [Integer, String] the index or string value of a context kind
250
+ # @return [LDContext, nil] the context corresponding to that index or kind,
251
+ # or null if none.
252
+ #
253
+ def individual_context(kind)
254
+ return nil unless valid?
255
+
256
+ if kind.is_a?(Integer)
257
+ unless multi_kind?
258
+ return kind == 0 ? self : nil
259
+ end
260
+
261
+ return kind >= 0 && kind < @contexts.count ? @contexts[kind] : nil
262
+ end
263
+
264
+ return nil unless kind.is_a?(String)
265
+
266
+ unless multi_kind?
267
+ return self.kind == kind ? self : nil
268
+ end
269
+
270
+ @contexts.each do |context|
271
+ return context if context.kind == kind
272
+ end
273
+
274
+ nil
275
+ end
276
+
277
+ #
278
+ # Retrieve the value of any top level, addressable attribute.
279
+ #
280
+ # This method returns an array of two values. The first element is the
281
+ # value of the requested attribute or nil if it does not exist. The second
282
+ # value will be true if the attribute exists; otherwise, it will be false.
283
+ #
284
+ # @param name [Symbol]
285
+ # @return [any]
286
+ #
287
+ private def get_top_level_addressable_attribute_single_kind(name)
288
+ case name
289
+ when :kind
290
+ kind
291
+ when :key
292
+ key
293
+ when :name
294
+ @name
295
+ when :anonymous
296
+ @anonymous
297
+ else
298
+ @attributes&.fetch(name, nil)
299
+ end
300
+ end
301
+
302
+ #
303
+ # Convenience method to create a simple single kind context providing only
304
+ # a key and kind type.
305
+ #
306
+ # @param key [String]
307
+ # @param kind [String]
308
+ #
309
+ def self.with_key(key, kind = KIND_DEFAULT)
310
+ create({key: key, kind: kind})
311
+ end
312
+
313
+ #
314
+ # Create a single kind context from the provided hash.
315
+ #
316
+ # The provided hash must match the format as outlined in the
317
+ # {https://docs.launchdarkly.com/sdk/features/user-config SDK
318
+ # documentation}.
319
+ #
320
+ # @deprecated The old user format will be removed in 8.0.0. Please use the new context specific format.
321
+ #
322
+ # @param data [Hash]
323
+ # @return [LDContext]
324
+ #
325
+ def self.create(data)
326
+ return create_invalid_context(ERR_NOT_HASH) unless data.is_a?(Hash)
327
+
328
+ kind = data[:kind]
329
+ if kind == KIND_MULTI
330
+ contexts = []
331
+ data.each do |key, value|
332
+ next if key == :kind
333
+ contexts << create_single_context(value, key.to_s)
334
+ end
335
+
336
+ return create_multi(contexts)
337
+ end
338
+
339
+ create_single_context(data, kind)
340
+ end
341
+
342
+ #
343
+ # Create a multi-kind context from the array of LDContexts provided.
344
+ #
345
+ # A multi-kind context is comprised of two or more single kind contexts.
346
+ # You cannot include a multi-kind context instead another multi-kind
347
+ # context.
348
+ #
349
+ # Additionally, the kind of each single-kind context must be unique. For
350
+ # instance, you cannot create a multi-kind context that includes two user
351
+ # kind contexts.
352
+ #
353
+ # If you attempt to create a multi-kind context from one single-kind
354
+ # context, this method will return the single-kind context instead of a new
355
+ # multi-kind context wrapping that one single-kind.
356
+ #
357
+ # @param contexts [Array<LDContext>]
358
+ # @return [LDContext]
359
+ #
360
+ def self.create_multi(contexts)
361
+ return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY) unless contexts.is_a?(Array)
362
+ return create_invalid_context(ERR_KIND_MULTI_WITH_NO_KINDS) if contexts.empty?
363
+
364
+ kinds = Set.new
365
+ contexts.each do |context|
366
+ if !context.is_a?(LDContext)
367
+ return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY)
368
+ elsif !context.valid?
369
+ return create_invalid_context(ERR_KIND_MULTI_NON_CONTEXT_ARRAY)
370
+ elsif context.multi_kind?
371
+ return create_invalid_context(ERR_KIND_MULTI_CANNOT_CONTAIN_MULTI)
372
+ elsif kinds.include? context.kind
373
+ return create_invalid_context(ERR_KIND_MULTI_DUPLICATES)
374
+ end
375
+
376
+ kinds.add(context.kind)
377
+ end
378
+
379
+ return contexts[0] if contexts.length == 1
380
+
381
+ full_key = contexts.sort_by(&:kind)
382
+ .map { |c| LaunchDarkly::Impl::Context::canonicalize_key_for_kind(c.kind, c.key) }
383
+ .join(":")
384
+
385
+ new(nil, full_key, "multi", nil, false, nil, nil, nil, contexts)
386
+ end
387
+
388
+ #
389
+ # @param error [String]
390
+ # @return [LDContext]
391
+ #
392
+ private_class_method def self.create_invalid_context(error)
393
+ new(nil, nil, nil, nil, false, nil, nil, error)
394
+ end
395
+
396
+ #
397
+ # @param data [Hash]
398
+ # @param kind [String]
399
+ # @return [LaunchDarkly::LDContext]
400
+ #
401
+ private_class_method def self.create_single_context(data, kind)
402
+ unless data.is_a?(Hash)
403
+ return create_invalid_context(ERR_NOT_HASH)
404
+ end
405
+
406
+ kind_error = LaunchDarkly::Impl::Context.validate_kind(kind)
407
+ return create_invalid_context(kind_error) unless kind_error.nil?
408
+
409
+ key = data[:key]
410
+ key_error = LaunchDarkly::Impl::Context.validate_key(key)
411
+ return create_invalid_context(key_error) unless key_error.nil?
412
+
413
+ name = data[:name]
414
+ name_error = LaunchDarkly::Impl::Context.validate_name(name)
415
+ return create_invalid_context(name_error) unless name_error.nil?
416
+
417
+ anonymous = data.fetch(:anonymous, false)
418
+ anonymous_error = LaunchDarkly::Impl::Context.validate_anonymous(anonymous, false)
419
+ return create_invalid_context(anonymous_error) unless anonymous_error.nil?
420
+
421
+ meta = data.fetch(:_meta, {})
422
+ private_attributes = meta[:privateAttributes]
423
+ if private_attributes && !private_attributes.is_a?(Array)
424
+ return create_invalid_context(ERR_PRIVATE_NON_ARRAY)
425
+ end
426
+
427
+ # We only need to create an attribute hash if there are keys set outside
428
+ # of the ones we store in dedicated instance variables.
429
+ attributes = nil
430
+ data.each do |k, v|
431
+ case k
432
+ when :kind, :key, :name, :anonymous, :_meta
433
+ next
434
+ else
435
+ attributes ||= {}
436
+ attributes[k] = v.clone
437
+ end
438
+ end
439
+
440
+ full_key = kind == LDContext::KIND_DEFAULT ? key.to_s : LaunchDarkly::Impl::Context::canonicalize_key_for_kind(kind, key.to_s)
441
+ new(key.to_s, full_key, kind, name, anonymous, attributes, private_attributes)
442
+ end
443
+ end
444
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  module LaunchDarkly
3
2
  # An object returned by {LDClient#variation_detail}, combining the result of a flag evaluation with
4
3
  # an explanation of how it was calculated.
@@ -12,7 +11,8 @@ module LaunchDarkly
12
11
  # @raise [ArgumentError] if `variation_index` or `reason` is not of the correct type
13
12
  def initialize(value, variation_index, reason)
14
13
  raise ArgumentError.new("variation_index must be a number") if !variation_index.nil? && !(variation_index.is_a? Numeric)
15
- raise ArgumentError.new("reason must be an EvaluationReason") if !(reason.is_a? EvaluationReason)
14
+ raise ArgumentError.new("reason must be an EvaluationReason") unless reason.is_a? EvaluationReason
15
+
16
16
  @value = value
17
17
  @variation_index = variation_index
18
18
  @reason = reason
@@ -70,20 +70,20 @@ module LaunchDarkly
70
70
  class EvaluationReason
71
71
  # Value for {#kind} indicating that the flag was off and therefore returned its configured off value.
72
72
  OFF = :OFF
73
-
74
- # Value for {#kind} indicating that the flag was on but the user did not match any targets or rules.
73
+
74
+ # Value for {#kind} indicating that the flag was on but the context did not match any targets or rules.
75
75
  FALLTHROUGH = :FALLTHROUGH
76
-
77
- # Value for {#kind} indicating that the user key was specifically targeted for this flag.
76
+
77
+ # Value for {#kind} indicating that the context key was specifically targeted for this flag.
78
78
  TARGET_MATCH = :TARGET_MATCH
79
-
80
- # Value for {#kind} indicating that the user matched one of the flag's rules.
79
+
80
+ # Value for {#kind} indicating that the context matched one of the flag's rules.
81
81
  RULE_MATCH = :RULE_MATCH
82
-
82
+
83
83
  # Value for {#kind} indicating that the flag was considered off because it had at least one
84
84
  # prerequisite flag that either was off or did not return the desired variation.
85
85
  PREREQUISITE_FAILED = :PREREQUISITE_FAILED
86
-
86
+
87
87
  # Value for {#kind} indicating that the flag could not be evaluated, e.g. because it does not exist
88
88
  # or due to an unexpected error. In this case the result value will be the application default value
89
89
  # that the caller passed to the client. Check {#error_kind} for more details on the problem.
@@ -100,8 +100,12 @@ module LaunchDarkly
100
100
  # a rule specified a nonexistent variation. An error message will always be logged in this case.
101
101
  ERROR_MALFORMED_FLAG = :MALFORMED_FLAG
102
102
 
103
- # Value for {#error_kind} indicating that the caller passed `nil` for the user parameter, or the
104
- # user lacked a key.
103
+ # Value for {#error_kind} indicating that there was an inconsistency between the expected type of the flag, and the
104
+ # actual type of the variation evaluated.
105
+ ERROR_WRONG_TYPE = :WRONG_TYPE
106
+
107
+ # Value for {#error_kind} indicating that the caller passed `nil` for the context parameter, or the
108
+ # context was invalid.
105
109
  ERROR_USER_NOT_SPECIFIED = :USER_NOT_SPECIFIED
106
110
 
107
111
  # Value for {#error_kind} indicating that an unexpected exception stopped flag evaluation. An error
@@ -141,7 +145,7 @@ module LaunchDarkly
141
145
  # querying at least one Big Segment. Otherwise it returns `nil`. Possible values are defined by
142
146
  # {BigSegmentsStatus}.
143
147
  #
144
- # Big Segments are a specific kind of user segments. For more information, read the LaunchDarkly
148
+ # Big Segments are a specific kind of context segments. For more information, read the LaunchDarkly
145
149
  # documentation: https://docs.launchdarkly.com/home/users/big-segments
146
150
  # @return [Symbol]
147
151
  attr_reader :big_segments_status
@@ -176,9 +180,9 @@ module LaunchDarkly
176
180
  # @return [EvaluationReason]
177
181
  # @raise [ArgumentError] if `rule_index` is not a number or `rule_id` is not a string
178
182
  def self.rule_match(rule_index, rule_id, in_experiment=false)
179
- raise ArgumentError.new("rule_index must be a number") if !(rule_index.is_a? Numeric)
183
+ raise ArgumentError.new("rule_index must be a number") unless rule_index.is_a? Numeric
180
184
  raise ArgumentError.new("rule_id must be a string") if !rule_id.nil? && !(rule_id.is_a? String) # in test data, ID could be nil
181
-
185
+
182
186
  if in_experiment
183
187
  er = new(:RULE_MATCH, rule_index, rule_id, nil, nil, true)
184
188
  else
@@ -193,7 +197,7 @@ module LaunchDarkly
193
197
  # @return [EvaluationReason]
194
198
  # @raise [ArgumentError] if `prerequisite_key` is nil or not a string
195
199
  def self.prerequisite_failed(prerequisite_key)
196
- raise ArgumentError.new("prerequisite_key must be a string") if !(prerequisite_key.is_a? String)
200
+ raise ArgumentError.new("prerequisite_key must be a string") unless prerequisite_key.is_a? String
197
201
  new(:PREREQUISITE_FAILED, nil, nil, prerequisite_key, nil)
198
202
  end
199
203
 
@@ -203,7 +207,7 @@ module LaunchDarkly
203
207
  # @return [EvaluationReason]
204
208
  # @raise [ArgumentError] if `error_kind` is not a symbol
205
209
  def self.error(error_kind)
206
- raise ArgumentError.new("error_kind must be a symbol") if !(error_kind.is_a? Symbol)
210
+ raise ArgumentError.new("error_kind must be a symbol") unless error_kind.is_a? Symbol
207
211
  e = @@error_instances[error_kind]
208
212
  e.nil? ? make_error(error_kind) : e
209
213
  end
@@ -279,7 +283,7 @@ module LaunchDarkly
279
283
  else
280
284
  { kind: @kind }
281
285
  end
282
- if !@big_segments_status.nil?
286
+ unless @big_segments_status.nil?
283
287
  ret[:bigSegmentsStatus] = @big_segments_status
284
288
  end
285
289
  ret
@@ -288,7 +292,7 @@ module LaunchDarkly
288
292
  # Same as {#as_json}, but converts the JSON structure into a string.
289
293
  # @return [String]
290
294
  def to_json(*a)
291
- as_json.to_json(a)
295
+ as_json.to_json(*a)
292
296
  end
293
297
 
294
298
  # Allows this object to be treated as a hash corresponding to its JSON representation. For
@@ -327,9 +331,9 @@ module LaunchDarkly
327
331
  @kind = kind.to_sym
328
332
  @rule_index = rule_index
329
333
  @rule_id = rule_id
330
- @rule_id.freeze if !rule_id.nil?
334
+ @rule_id.freeze unless rule_id.nil?
331
335
  @prerequisite_key = prerequisite_key
332
- @prerequisite_key.freeze if !prerequisite_key.nil?
336
+ @prerequisite_key.freeze unless prerequisite_key.nil?
333
337
  @error_kind = error_kind
334
338
  @in_experiment = in_experiment
335
339
  @big_segments_status = big_segments_status
@@ -348,7 +352,7 @@ module LaunchDarkly
348
352
  ERROR_FLAG_NOT_FOUND => make_error(ERROR_FLAG_NOT_FOUND),
349
353
  ERROR_MALFORMED_FLAG => make_error(ERROR_MALFORMED_FLAG),
350
354
  ERROR_USER_NOT_SPECIFIED => make_error(ERROR_USER_NOT_SPECIFIED),
351
- ERROR_EXCEPTION => make_error(ERROR_EXCEPTION)
355
+ ERROR_EXCEPTION => make_error(ERROR_EXCEPTION),
352
356
  }
353
357
  end
354
358