launchdarkly-server-sdk 7.3.2 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -788,5 +788,102 @@ module LaunchDarkly
788
788
  end
789
789
  end
790
790
  end
791
+
792
+ #
793
+ # Namespace for feature-flag based technology migration support.
794
+ #
795
+ module Migrations
796
+ #
797
+ # A migrator is the interface through which migration support is executed. A migrator is configured through the
798
+ # {LaunchDarkly::Migrations::MigratorBuilder} class.
799
+ #
800
+ module Migrator
801
+ #
802
+ # Uses the provided flag key and context to execute a migration-backed read operation.
803
+ #
804
+ # @param key [String]
805
+ # @param context [LaunchDarkly::LDContext]
806
+ # @param default_stage [Symbol]
807
+ # @param payload [Object, nil]
808
+ #
809
+ # @return [LaunchDarkly::Migrations::OperationResult]
810
+ #
811
+ def read(key, context, default_stage, payload = nil) end
812
+
813
+ #
814
+ # Uses the provided flag key and context to execute a migration-backed write operation.
815
+ #
816
+ # @param key [String]
817
+ # @param context [LaunchDarkly::LDContext]
818
+ # @param default_stage [Symbol]
819
+ # @param payload [Object, nil]
820
+ #
821
+ # @return [LaunchDarkly::Migrations::WriteResult]
822
+ #
823
+ def write(key, context, default_stage, payload = nil) end
824
+ end
825
+
826
+ #
827
+ # An OpTracker is responsible for managing the collection of measurements that which a user might wish to record
828
+ # throughout a migration-assisted operation.
829
+ #
830
+ # Example measurements include latency, errors, and consistency.
831
+ #
832
+ # This data can be provided to the {LaunchDarkly::LDClient.track_migration_op} method to relay this metric
833
+ # information upstream to LaunchDarkly services.
834
+ #
835
+ module OpTracker
836
+ #
837
+ # Sets the migration related operation associated with these tracking measurements.
838
+ #
839
+ # @param [Symbol] op The read or write operation symbol.
840
+ #
841
+ def operation(op) end
842
+
843
+ #
844
+ # Allows recording which origins were called during a migration.
845
+ #
846
+ # @param [Symbol] origin Designation for the old or new origin.
847
+ #
848
+ def invoked(origin) end
849
+
850
+ #
851
+ # Allows recording the results of a consistency check.
852
+ #
853
+ # This method accepts a callable which should take no parameters and return a single boolean to represent the
854
+ # consistency check results for a read operation.
855
+ #
856
+ # A callable is provided in case sampling rules do not require consistency checking to run. In this case, we can
857
+ # avoid the overhead of a function by not using the callable.
858
+ #
859
+ # @param [#call] is_consistent closure to return result of comparison check
860
+ #
861
+ def consistent(is_consistent) end
862
+
863
+ #
864
+ # Allows recording whether an error occurred during the operation.
865
+ #
866
+ # @param [Symbol] origin Designation for the old or new origin.
867
+ #
868
+ def error(origin) end
869
+
870
+ #
871
+ # Allows tracking the recorded latency for an individual operation.
872
+ #
873
+ # @param [Symbol] origin Designation for the old or new origin.
874
+ # @param [Float] duration Duration measurement in milliseconds (ms).
875
+ #
876
+ def latency(origin, duration) end
877
+
878
+ #
879
+ # Creates an instance of {LaunchDarkly::Impl::MigrationOpEventData}.
880
+ #
881
+ # @return [LaunchDarkly::Impl::MigrationOpEvent, String] A migration op event or a string describing the error.
882
+ # failure.
883
+ #
884
+ def build
885
+ end
886
+ end
887
+ end
791
888
  end
792
889
  end
@@ -6,8 +6,10 @@ require "ldclient-rb/impl/diagnostic_events"
6
6
  require "ldclient-rb/impl/evaluator"
7
7
  require "ldclient-rb/impl/flag_tracker"
8
8
  require "ldclient-rb/impl/store_client_wrapper"
9
+ require "ldclient-rb/impl/migrations/tracker"
9
10
  require "concurrent/atomics"
10
11
  require "digest/sha1"
12
+ require "forwardable"
11
13
  require "logger"
12
14
  require "benchmark"
13
15
  require "json"
@@ -20,6 +22,10 @@ module LaunchDarkly
20
22
  #
21
23
  class LDClient
22
24
  include Impl
25
+ extend Forwardable
26
+
27
+ def_delegators :@config, :logger
28
+
23
29
  #
24
30
  # Creates a new client instance that connects to LaunchDarkly. A custom
25
31
  # configuration parameter can also supplied to specify advanced options,
@@ -149,7 +155,7 @@ module LaunchDarkly
149
155
  # @return [String, nil] a hash string or nil if the provided context was invalid
150
156
  #
151
157
  def secure_mode_hash(context)
152
- context = Impl::Context::make_context(context)
158
+ context = Impl::Context.make_context(context)
153
159
  unless context.valid?
154
160
  @config.logger.warn("secure_mode_hash called with invalid context: #{context.error}")
155
161
  return nil
@@ -189,10 +195,11 @@ module LaunchDarkly
189
195
  # @param default the default value of the flag; this is used if there is an error
190
196
  # condition making it impossible to find or evaluate the flag
191
197
  #
192
- # @return the variation for the provided context, or the default value if there's an an error
198
+ # @return the variation for the provided context, or the default value if there's an error
193
199
  #
194
200
  def variation(key, context, default)
195
- evaluate_internal(key, context, default, false).value
201
+ detail, _, _, = variation_with_flag(key, context, default)
202
+ detail.value
196
203
  end
197
204
 
198
205
  #
@@ -219,7 +226,43 @@ module LaunchDarkly
219
226
  # @return [EvaluationDetail] an object describing the result
220
227
  #
221
228
  def variation_detail(key, context, default)
222
- evaluate_internal(key, context, default, true)
229
+ detail, _, _ = evaluate_internal(key, context, default, true)
230
+ detail
231
+ end
232
+
233
+ #
234
+ # This method returns the migration stage of the migration feature flag for the given evaluation context.
235
+ #
236
+ # This method returns the default stage if there is an error or the flag does not exist. If the default stage is not
237
+ # a valid stage, then a default stage of 'off' will be used instead.
238
+ #
239
+ # @param key [String]
240
+ # @param context [LDContext]
241
+ # @param default_stage [Symbol]
242
+ #
243
+ # @return [Array<Symbol, Interfaces::Migrations::OpTracker>]
244
+ #
245
+ def migration_variation(key, context, default_stage)
246
+ unless Migrations::VALID_STAGES.include? default_stage
247
+ @config.logger.error { "[LDClient] default_stage #{default_stage} is not a valid stage; continuing with 'off' as default" }
248
+ default_stage = Migrations::STAGE_OFF
249
+ end
250
+
251
+ context = Impl::Context::make_context(context)
252
+ detail, flag, _ = variation_with_flag(key, context, default_stage.to_s)
253
+
254
+ stage = detail.value
255
+ stage = stage.to_sym if stage.respond_to? :to_sym
256
+
257
+ if Migrations::VALID_STAGES.include?(stage)
258
+ tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
259
+ return stage, tracker
260
+ end
261
+
262
+ detail = LaunchDarkly::Impl::Evaluator.error_result(LaunchDarkly::EvaluationReason::ERROR_WRONG_TYPE, default_stage.to_s)
263
+ tracker = Impl::Migrations::OpTracker.new(@config.logger, key, flag, context, detail, default_stage)
264
+
265
+ [default_stage, tracker]
223
266
  end
224
267
 
225
268
  #
@@ -282,6 +325,32 @@ module LaunchDarkly
282
325
  @event_processor.record_custom_event(context, event_name, data, metric_value)
283
326
  end
284
327
 
328
+ #
329
+ # Tracks the results of a migrations operation. This event includes measurements which can be used to enhance the
330
+ # observability of a migration within the LaunchDarkly UI.
331
+ #
332
+ # This event should be generated through {Interfaces::Migrations::OpTracker}. If you are using the
333
+ # {Interfaces::Migrations::Migrator} to handle migrations, this event will be created and emitted
334
+ # automatically.
335
+ #
336
+ # @param tracker [LaunchDarkly::Interfaces::Migrations::OpTracker]
337
+ #
338
+ def track_migration_op(tracker)
339
+ unless tracker.is_a? LaunchDarkly::Interfaces::Migrations::OpTracker
340
+ @config.logger.error { "invalid op tracker received in track_migration_op" }
341
+ return
342
+ end
343
+
344
+ event = tracker.build
345
+ if event.is_a? String
346
+ @config.logger.error { "[LDClient] Error occurred generating migration op event; #{event}" }
347
+ return
348
+ end
349
+
350
+
351
+ @event_processor.record_migration_op_event(event)
352
+ end
353
+
285
354
  #
286
355
  # Returns a {FeatureFlagsState} object that encapsulates the state of all feature flags for a given context,
287
356
  # including the flag values and also metadata that can be used on the front end. This method does not
@@ -431,24 +500,41 @@ module LaunchDarkly
431
500
  end
432
501
  end
433
502
 
503
+ #
504
+ # @param key [String]
434
505
  # @param context [Hash, LDContext]
435
- # @return [EvaluationDetail]
506
+ # @param default [Object]
507
+ #
508
+ # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
509
+ #
510
+ def variation_with_flag(key, context, default)
511
+ evaluate_internal(key, context, default, false)
512
+ end
513
+
514
+ #
515
+ # @param key [String]
516
+ # @param context [Hash, LDContext]
517
+ # @param default [Object]
518
+ # @param with_reasons [Boolean]
519
+ #
520
+ # @return [Array<EvaluationDetail, [LaunchDarkly::Impl::Model::FeatureFlag, nil], [String, nil]>]
521
+ #
436
522
  def evaluate_internal(key, context, default, with_reasons)
437
523
  if @config.offline?
438
- return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
524
+ return Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default), nil, nil
439
525
  end
440
526
 
441
527
  if context.nil?
442
528
  @config.logger.error { "[LDClient] Must specify context" }
443
529
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
444
- return detail
530
+ return detail, nil, "no context provided"
445
531
  end
446
532
 
447
533
  context = Impl::Context::make_context(context)
448
534
  unless context.valid?
449
535
  @config.logger.error { "[LDClient] Context was invalid for evaluation of flag '#{key}' (#{context.error}); returning default value" }
450
536
  detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)
451
- return detail
537
+ return detail, nil, context.error
452
538
  end
453
539
 
454
540
  unless initialized?
@@ -458,7 +544,7 @@ module LaunchDarkly
458
544
  @config.logger.error { "[LDClient] Client has not finished initializing; feature store unavailable, returning default value" }
459
545
  detail = Evaluator.error_result(EvaluationReason::ERROR_CLIENT_NOT_READY, default)
460
546
  record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
461
- return detail
547
+ return detail, nil, "client not initialized"
462
548
  end
463
549
  end
464
550
 
@@ -472,7 +558,7 @@ module LaunchDarkly
472
558
  @config.logger.info { "[LDClient] Unknown feature flag \"#{key}\". Returning default value" }
473
559
  detail = Evaluator.error_result(EvaluationReason::ERROR_FLAG_NOT_FOUND, default)
474
560
  record_unknown_flag_eval(key, context, default, detail.reason, with_reasons)
475
- return detail
561
+ return detail, nil, "feature flag not found"
476
562
  end
477
563
 
478
564
  begin
@@ -487,12 +573,12 @@ module LaunchDarkly
487
573
  detail = EvaluationDetail.new(default, nil, detail.reason)
488
574
  end
489
575
  record_flag_eval(feature, context, detail, default, with_reasons)
490
- detail
576
+ [detail, feature, nil]
491
577
  rescue => exn
492
578
  Util.log_exception(@config.logger, "Error evaluating feature flag \"#{key}\"", exn)
493
579
  detail = Evaluator.error_result(EvaluationReason::ERROR_EXCEPTION, default)
494
580
  record_flag_eval_error(feature, context, default, detail.reason, with_reasons)
495
- detail
581
+ [detail, feature, exn.to_s]
496
582
  end
497
583
  end
498
584
 
@@ -508,7 +594,9 @@ module LaunchDarkly
508
594
  default,
509
595
  add_experiment_data || flag[:trackEvents] || false,
510
596
  flag[:debugEventsUntilDate],
511
- nil
597
+ nil,
598
+ flag[:samplingRatio],
599
+ !!flag[:excludeFromSummaries]
512
600
  )
513
601
  end
514
602
 
@@ -524,13 +612,15 @@ module LaunchDarkly
524
612
  nil,
525
613
  add_experiment_data || prereq_flag[:trackEvents] || false,
526
614
  prereq_flag[:debugEventsUntilDate],
527
- prereq_of_flag[:key]
615
+ prereq_of_flag[:key],
616
+ prereq_flag[:samplingRatio],
617
+ !!prereq_flag[:excludeFromSummaries]
528
618
  )
529
619
  end
530
620
 
531
621
  private def record_flag_eval_error(flag, context, default, reason, with_reasons)
532
622
  @event_processor.record_eval_event(context, flag[:key], flag[:version], nil, default, with_reasons ? reason : nil, default,
533
- flag[:trackEvents], flag[:debugEventsUntilDate], nil)
623
+ flag[:trackEvents], flag[:debugEventsUntilDate], nil, flag[:samplingRatio], !!flag[:excludeFromSummaries])
534
624
  end
535
625
 
536
626
  #
@@ -542,7 +632,7 @@ module LaunchDarkly
542
632
  #
543
633
  private def record_unknown_flag_eval(flag_key, context, default, reason, with_reasons)
544
634
  @event_processor.record_eval_event(context, flag_key, nil, nil, default, with_reasons ? reason : nil, default,
545
- false, nil, nil)
635
+ false, nil, nil, 1, false)
546
636
  end
547
637
 
548
638
  private def experiment?(flag, reason)
@@ -0,0 +1,230 @@
1
+ require 'ldclient-rb/impl/migrations/migrator'
2
+
3
+ module LaunchDarkly
4
+ #
5
+ # Namespace for feature-flag based technology migration support.
6
+ #
7
+ module Migrations
8
+ # Symbol representing the old origin, or the old technology source you are migrating away from.
9
+ ORIGIN_OLD = :old
10
+ # Symbol representing the new origin, or the new technology source you are migrating towards.
11
+ ORIGIN_NEW = :new
12
+
13
+ # Symbol defining a read-related operation
14
+ OP_READ = :read
15
+ # Symbol defining a write-related operation
16
+ OP_WRITE = :write
17
+
18
+ STAGE_OFF = :off
19
+ STAGE_DUALWRITE = :dualwrite
20
+ STAGE_SHADOW = :shadow
21
+ STAGE_LIVE = :live
22
+ STAGE_RAMPDOWN = :rampdown
23
+ STAGE_COMPLETE = :complete
24
+
25
+ VALID_OPERATIONS = [
26
+ OP_READ,
27
+ OP_WRITE,
28
+ ]
29
+
30
+ VALID_ORIGINS = [
31
+ ORIGIN_OLD,
32
+ ORIGIN_NEW,
33
+ ]
34
+
35
+ VALID_STAGES = [
36
+ STAGE_OFF,
37
+ STAGE_DUALWRITE,
38
+ STAGE_SHADOW,
39
+ STAGE_LIVE,
40
+ STAGE_RAMPDOWN,
41
+ STAGE_COMPLETE,
42
+ ]
43
+
44
+ #
45
+ # The OperationResult wraps the {LaunchDarkly::Result} class to tie an operation origin to a result.
46
+ #
47
+ class OperationResult
48
+ extend Forwardable
49
+ def_delegators :@result, :value, :error, :exception, :success?
50
+
51
+ #
52
+ # @param origin [Symbol]
53
+ # @param result [LaunchDarkly::Result]
54
+ #
55
+ def initialize(origin, result)
56
+ @origin = origin
57
+ @result = result
58
+ end
59
+
60
+ #
61
+ # @return [Symbol] The origin this result is associated with.
62
+ #
63
+ attr_reader :origin
64
+ end
65
+
66
+ #
67
+ # A write result contains the operation results against both the authoritative and non-authoritative origins.
68
+ #
69
+ # Authoritative writes are always executed first. In the event of a failure, the non-authoritative write will not
70
+ # be executed, resulting in a nil value in the final WriteResult.
71
+ #
72
+ class WriteResult
73
+ #
74
+ # @param authoritative [OperationResult]
75
+ # @param nonauthoritative [OperationResult, nil]
76
+ #
77
+ def initialize(authoritative, nonauthoritative = nil)
78
+ @authoritative = authoritative
79
+ @nonauthoritative = nonauthoritative
80
+ end
81
+
82
+ #
83
+ # Returns the operation result for the authoritative origin.
84
+ #
85
+ # @return [OperationResult]
86
+ #
87
+ attr_reader :authoritative
88
+
89
+ #
90
+ # Returns the operation result for the non-authoritative origin.
91
+ #
92
+ # This result might be nil as the non-authoritative write does not execute in every stage, and will not execute
93
+ # if the authoritative write failed.
94
+ #
95
+ # @return [OperationResult, nil]
96
+ #
97
+ attr_reader :nonauthoritative
98
+ end
99
+
100
+
101
+ #
102
+ # The migration builder is used to configure and construct an instance of a
103
+ # {LaunchDarkly::Interfaces::Migrations::Migrator}. This migrator can be used to perform LaunchDarkly assisted
104
+ # technology migrations through the use of migration-based feature flags.
105
+ #
106
+ class MigratorBuilder
107
+ EXECUTION_SERIAL = :serial
108
+ EXECUTION_RANDOM = :random
109
+ EXECUTION_PARALLEL = :parallel
110
+
111
+ VALID_EXECUTION_ORDERS = [EXECUTION_SERIAL, EXECUTION_RANDOM, EXECUTION_PARALLEL]
112
+ private_constant :VALID_EXECUTION_ORDERS
113
+
114
+ #
115
+ # @param client [LaunchDarkly::LDClient]
116
+ #
117
+ def initialize(client)
118
+ @client = client
119
+
120
+ # Default settings as required by the spec
121
+ @read_execution_order = EXECUTION_PARALLEL
122
+ @measure_latency = true
123
+ @measure_errors = true
124
+
125
+ @read_config = nil # @type [LaunchDarkly::Impl::Migrations::MigrationConfig, nil]
126
+ @write_config = nil # @type [LaunchDarkly::Impl::Migrations::MigrationConfig, nil]
127
+ end
128
+
129
+ #
130
+ # The read execution order influences the parallelism and execution order for read operations involving multiple
131
+ # origins.
132
+ #
133
+ # @param order [Symbol]
134
+ #
135
+ def read_execution_order(order)
136
+ return unless VALID_EXECUTION_ORDERS.include? order
137
+
138
+ @read_execution_order = order
139
+ end
140
+
141
+ #
142
+ # Enable or disable latency tracking for migration operations. This latency information can be sent upstream to
143
+ # LaunchDarkly to enhance migration visibility.
144
+ #
145
+ # @param enabled [Boolean]
146
+ #
147
+ def track_latency(enabled)
148
+ @measure_latency = !!enabled
149
+ end
150
+
151
+ #
152
+ # Enable or disable error tracking for migration operations. This error information can be sent upstream to
153
+ # LaunchDarkly to enhance migration visibility.
154
+ #
155
+ # @param enabled [Boolean]
156
+ #
157
+ def track_errors(enabled)
158
+ @measure_errors = !!enabled
159
+ end
160
+
161
+ #
162
+ # Read can be used to configure the migration-read behavior of the resulting
163
+ # {LaunchDarkly::Interfaces::Migrations::Migrator} instance.
164
+ #
165
+ # Users are required to provide two different read methods -- one to read from the old migration origin, and one
166
+ # to read from the new origin. Additionally, customers can opt-in to consistency tracking by providing a
167
+ # comparison function.
168
+ #
169
+ # Depending on the migration stage, one or both of these read methods may be called.
170
+ #
171
+ # The read methods should accept a single nullable parameter. This parameter is a payload passed through the
172
+ # {LaunchDarkly::Interfaces::Migrations::Migrator#read} method. This method should return a {LaunchDarkly::Result}
173
+ # instance.
174
+ #
175
+ # The consistency method should accept 2 parameters of any type. These parameters are the results of executing the
176
+ # read operation against the old and new origins. If both operations were successful, the consistency method will
177
+ # be invoked. This method should return true if the two parameters are equal, or false otherwise.
178
+ #
179
+ # @param old_read [#call]
180
+ # @param new_read [#call]
181
+ # @param comparison [#call, nil]
182
+ #
183
+ def read(old_read, new_read, comparison = nil)
184
+ return unless old_read.respond_to?(:call) && old_read.arity == 1
185
+ return unless new_read.respond_to?(:call) && new_read.arity == 1
186
+ return unless comparison.nil? || (comparison.respond_to?(:call) && comparison.arity == 2)
187
+
188
+ @read_config = LaunchDarkly::Impl::Migrations::MigrationConfig.new(old_read, new_read, comparison)
189
+ end
190
+
191
+ #
192
+ # Write can be used to configure the migration-write behavior of the resulting
193
+ # {LaunchDarkly::Interfaces::Migrations::Migrator} instance.
194
+ #
195
+ # Users are required to provide two different write methods -- one to write to the old migration origin, and one
196
+ # to write to the new origin.
197
+ #
198
+ # Depending on the migration stage, one or both of these write methods may be called.
199
+ #
200
+ # The write methods should accept a single nullable parameter. This parameter is a payload passed through the
201
+ # {LaunchDarkly::Interfaces::Migrations::Migrator#write} method. This method should return a {LaunchDarkly::Result}
202
+ # instance.
203
+ #
204
+ # @param old_write [#call]
205
+ # @param new_write [#call]
206
+ #
207
+ def write(old_write, new_write)
208
+ return unless old_write.respond_to?(:call) && old_write.arity == 1
209
+ return unless new_write.respond_to?(:call) && new_write.arity == 1
210
+
211
+ @write_config = LaunchDarkly::Impl::Migrations::MigrationConfig.new(old_write, new_write, nil)
212
+ end
213
+
214
+ #
215
+ # Build constructs a {LaunchDarkly::Interfaces::Migrations::Migrator} instance to support migration-based reads
216
+ # and writes. A string describing any failure conditions will be returned if the build fails.
217
+ #
218
+ # @return [LaunchDarkly::Interfaces::Migrations::Migrator, string]
219
+ #
220
+ def build
221
+ return "client not provided" if @client.nil?
222
+ return "read configuration not provided" if @read_config.nil?
223
+ return "write configuration not provided" if @write_config.nil?
224
+
225
+ LaunchDarkly::Impl::Migrations::Migrator.new(@client, @read_execution_order, @read_config, @write_config, @measure_latency, @measure_errors)
226
+ end
227
+ end
228
+
229
+ end
230
+ end
@@ -109,6 +109,8 @@ module LaunchDarkly
109
109
  end
110
110
  private_class_method :new
111
111
 
112
+ protected attr_reader :components
113
+
112
114
  #
113
115
  # Creates a Reference from a string. For the supported syntax and examples,
114
116
  # see comments on the Reference type.
@@ -227,6 +229,15 @@ module LaunchDarkly
227
229
  @components[index]
228
230
  end
229
231
 
232
+ def ==(other)
233
+ self.error == other.error && self.components == other.components
234
+ end
235
+ alias eql? ==
236
+
237
+ def hash
238
+ ([error] + components).hash
239
+ end
240
+
230
241
  #
231
242
  # Performs unescaping of attribute reference path components:
232
243
  #
@@ -2,6 +2,69 @@ require "uri"
2
2
  require "http"
3
3
 
4
4
  module LaunchDarkly
5
+ #
6
+ # A Result is used to reflect the outcome of any operation.
7
+ #
8
+ # Results can either be considered a success or a failure.
9
+ #
10
+ # In the event of success, the Result will contain an option, nullable value to hold any success value back to the
11
+ # calling function.
12
+ #
13
+ # If the operation fails, the Result will contain an error describing the value.
14
+ #
15
+ class Result
16
+ #
17
+ # Create a successful result with the provided value.
18
+ #
19
+ # @param value [Object, nil]
20
+ # @return [Result]
21
+ #
22
+ def self.success(value)
23
+ Result.new(value)
24
+ end
25
+
26
+ #
27
+ # Create a failed result with the provided error description.
28
+ #
29
+ # @param error [String]
30
+ # @param exception [Exception, nil]
31
+ # @return [Result]
32
+ #
33
+ def self.fail(error, exception = nil)
34
+ Result.new(nil, error, exception)
35
+ end
36
+
37
+ #
38
+ # Was this result successful or did it encounter an error?
39
+ #
40
+ # @return [Boolean]
41
+ #
42
+ def success?
43
+ @error.nil?
44
+ end
45
+
46
+ #
47
+ # @return [Object, nil] The value returned from the operation if it was successful; nil otherwise.
48
+ #
49
+ attr_reader :value
50
+
51
+ #
52
+ # @return [String, nil] An error description of the failure; nil otherwise
53
+ #
54
+ attr_reader :error
55
+
56
+ #
57
+ # @return [Exception, nil] An optional exception which caused the failure
58
+ #
59
+ attr_reader :exception
60
+
61
+ private def initialize(value, error = nil, exception = nil)
62
+ @value = value
63
+ @error = error
64
+ @exception = exception
65
+ end
66
+ end
67
+
5
68
  # @private
6
69
  module Util
7
70
  #
@@ -1,3 +1,3 @@
1
1
  module LaunchDarkly
2
- VERSION = "7.3.2" # x-release-please-version
2
+ VERSION = "8.1.0" # x-release-please-version
3
3
  end
data/lib/ldclient-rb.rb CHANGED
@@ -9,6 +9,7 @@ require "ldclient-rb/version"
9
9
  require "ldclient-rb/interfaces"
10
10
  require "ldclient-rb/util"
11
11
  require "ldclient-rb/flags_state"
12
+ require "ldclient-rb/migrations"
12
13
  require "ldclient-rb/ldclient"
13
14
  require "ldclient-rb/cache_store"
14
15
  require "ldclient-rb/expiring_cache"