launchdarkly-server-sdk 8.8.3-java

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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +13 -0
  3. data/README.md +61 -0
  4. data/lib/launchdarkly-server-sdk.rb +1 -0
  5. data/lib/ldclient-rb/cache_store.rb +45 -0
  6. data/lib/ldclient-rb/config.rb +658 -0
  7. data/lib/ldclient-rb/context.rb +565 -0
  8. data/lib/ldclient-rb/evaluation_detail.rb +387 -0
  9. data/lib/ldclient-rb/events.rb +642 -0
  10. data/lib/ldclient-rb/expiring_cache.rb +77 -0
  11. data/lib/ldclient-rb/flags_state.rb +88 -0
  12. data/lib/ldclient-rb/impl/big_segments.rb +117 -0
  13. data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
  14. data/lib/ldclient-rb/impl/context.rb +96 -0
  15. data/lib/ldclient-rb/impl/context_filter.rb +166 -0
  16. data/lib/ldclient-rb/impl/data_source.rb +188 -0
  17. data/lib/ldclient-rb/impl/data_store.rb +109 -0
  18. data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
  19. data/lib/ldclient-rb/impl/diagnostic_events.rb +129 -0
  20. data/lib/ldclient-rb/impl/evaluation_with_hook_result.rb +34 -0
  21. data/lib/ldclient-rb/impl/evaluator.rb +539 -0
  22. data/lib/ldclient-rb/impl/evaluator_bucketing.rb +86 -0
  23. data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
  24. data/lib/ldclient-rb/impl/evaluator_operators.rb +131 -0
  25. data/lib/ldclient-rb/impl/event_sender.rb +100 -0
  26. data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
  27. data/lib/ldclient-rb/impl/event_types.rb +136 -0
  28. data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
  29. data/lib/ldclient-rb/impl/integrations/consul_impl.rb +170 -0
  30. data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +300 -0
  31. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +229 -0
  32. data/lib/ldclient-rb/impl/integrations/redis_impl.rb +306 -0
  33. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
  34. data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
  35. data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
  36. data/lib/ldclient-rb/impl/model/clause.rb +45 -0
  37. data/lib/ldclient-rb/impl/model/feature_flag.rb +254 -0
  38. data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
  39. data/lib/ldclient-rb/impl/model/segment.rb +132 -0
  40. data/lib/ldclient-rb/impl/model/serialization.rb +72 -0
  41. data/lib/ldclient-rb/impl/repeating_task.rb +46 -0
  42. data/lib/ldclient-rb/impl/sampler.rb +25 -0
  43. data/lib/ldclient-rb/impl/store_client_wrapper.rb +141 -0
  44. data/lib/ldclient-rb/impl/store_data_set_sorter.rb +55 -0
  45. data/lib/ldclient-rb/impl/unbounded_pool.rb +34 -0
  46. data/lib/ldclient-rb/impl/util.rb +95 -0
  47. data/lib/ldclient-rb/impl.rb +13 -0
  48. data/lib/ldclient-rb/in_memory_store.rb +100 -0
  49. data/lib/ldclient-rb/integrations/consul.rb +45 -0
  50. data/lib/ldclient-rb/integrations/dynamodb.rb +92 -0
  51. data/lib/ldclient-rb/integrations/file_data.rb +108 -0
  52. data/lib/ldclient-rb/integrations/redis.rb +98 -0
  53. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +663 -0
  54. data/lib/ldclient-rb/integrations/test_data.rb +213 -0
  55. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +246 -0
  56. data/lib/ldclient-rb/integrations.rb +6 -0
  57. data/lib/ldclient-rb/interfaces.rb +974 -0
  58. data/lib/ldclient-rb/ldclient.rb +822 -0
  59. data/lib/ldclient-rb/memoized_value.rb +32 -0
  60. data/lib/ldclient-rb/migrations.rb +230 -0
  61. data/lib/ldclient-rb/non_blocking_thread_pool.rb +46 -0
  62. data/lib/ldclient-rb/polling.rb +102 -0
  63. data/lib/ldclient-rb/reference.rb +295 -0
  64. data/lib/ldclient-rb/requestor.rb +102 -0
  65. data/lib/ldclient-rb/simple_lru_cache.rb +25 -0
  66. data/lib/ldclient-rb/stream.rb +196 -0
  67. data/lib/ldclient-rb/util.rb +132 -0
  68. data/lib/ldclient-rb/version.rb +3 -0
  69. data/lib/ldclient-rb.rb +27 -0
  70. metadata +400 -0
@@ -0,0 +1,188 @@
1
+ require "concurrent"
2
+ require "forwardable"
3
+ require "ldclient-rb/impl/dependency_tracker"
4
+ require "ldclient-rb/interfaces"
5
+ require "set"
6
+
7
+ module LaunchDarkly
8
+ module Impl
9
+ module DataSource
10
+ class StatusProvider
11
+ include LaunchDarkly::Interfaces::DataSource::StatusProvider
12
+
13
+ extend Forwardable
14
+ def_delegators :@status_broadcaster, :add_listener, :remove_listener
15
+
16
+ def initialize(status_broadcaster, update_sink)
17
+ # @type [Broadcaster]
18
+ @status_broadcaster = status_broadcaster
19
+ # @type [UpdateSink]
20
+ @data_source_update_sink = update_sink
21
+ end
22
+
23
+ def status
24
+ @data_source_update_sink.current_status
25
+ end
26
+ end
27
+
28
+ class UpdateSink
29
+ include LaunchDarkly::Interfaces::DataSource::UpdateSink
30
+
31
+ # @return [LaunchDarkly::Interfaces::DataSource::Status]
32
+ attr_reader :current_status
33
+
34
+ def initialize(data_store, status_broadcaster, flag_change_broadcaster)
35
+ # @type [LaunchDarkly::Interfaces::FeatureStore]
36
+ @data_store = data_store
37
+ # @type [Broadcaster]
38
+ @status_broadcaster = status_broadcaster
39
+ # @type [Broadcaster]
40
+ @flag_change_broadcaster = flag_change_broadcaster
41
+ @dependency_tracker = LaunchDarkly::Impl::DependencyTracker.new
42
+
43
+ @mutex = Mutex.new
44
+ @current_status = LaunchDarkly::Interfaces::DataSource::Status.new(
45
+ LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING,
46
+ Time.now,
47
+ nil)
48
+ end
49
+
50
+ def init(all_data)
51
+ old_data = nil
52
+ monitor_store_update do
53
+ if @flag_change_broadcaster.has_listeners?
54
+ old_data = {}
55
+ LaunchDarkly::ALL_KINDS.each do |kind|
56
+ old_data[kind] = @data_store.all(kind)
57
+ end
58
+ end
59
+
60
+ @data_store.init(all_data)
61
+ end
62
+
63
+ update_full_dependency_tracker(all_data)
64
+
65
+ return if old_data.nil?
66
+
67
+ send_change_events(
68
+ compute_changed_items_for_full_data_set(old_data, all_data)
69
+ )
70
+ end
71
+
72
+ def upsert(kind, item)
73
+ monitor_store_update { @data_store.upsert(kind, item) }
74
+
75
+ # TODO(sc-197908): We only want to do this if the store successfully
76
+ # updates the record.
77
+ @dependency_tracker.update_dependencies_from(kind, item[:key], item)
78
+ if @flag_change_broadcaster.has_listeners?
79
+ affected_items = Set.new
80
+ @dependency_tracker.add_affected_items(affected_items, {kind: kind, key: item[:key]})
81
+ send_change_events(affected_items)
82
+ end
83
+ end
84
+
85
+ def delete(kind, key, version)
86
+ monitor_store_update { @data_store.delete(kind, key, version) }
87
+
88
+ @dependency_tracker.update_dependencies_from(kind, key, nil)
89
+ if @flag_change_broadcaster.has_listeners?
90
+ affected_items = Set.new
91
+ @dependency_tracker.add_affected_items(affected_items, {kind: kind, key: key})
92
+ send_change_events(affected_items)
93
+ end
94
+ end
95
+
96
+ def update_status(new_state, new_error)
97
+ return if new_state.nil?
98
+
99
+ status_to_broadcast = nil
100
+
101
+ @mutex.synchronize do
102
+ old_status = @current_status
103
+
104
+ if new_state == LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED && old_status.state == LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING
105
+ # See {LaunchDarkly::Interfaces::DataSource::UpdateSink#update_status} for more information
106
+ new_state = LaunchDarkly::Interfaces::DataSource::Status::INITIALIZING
107
+ end
108
+
109
+ unless new_state == old_status.state && new_error.nil?
110
+ @current_status = LaunchDarkly::Interfaces::DataSource::Status.new(
111
+ new_state,
112
+ new_state == current_status.state ? current_status.state_since : Time.now,
113
+ new_error.nil? ? current_status.last_error : new_error
114
+ )
115
+ status_to_broadcast = current_status
116
+ end
117
+ end
118
+
119
+ @status_broadcaster.broadcast(status_to_broadcast) unless status_to_broadcast.nil?
120
+ end
121
+
122
+ private def update_full_dependency_tracker(all_data)
123
+ @dependency_tracker.reset
124
+ all_data.each do |kind, items|
125
+ items.each do |key, item|
126
+ @dependency_tracker.update_dependencies_from(kind, item.key, item)
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+ #
133
+ # Method to monitor updates to the store. You provide a block to update
134
+ # the store. This mthod wraps that block, catching and re-raising all
135
+ # errors, and notifying all status listeners of the error.
136
+ #
137
+ private def monitor_store_update
138
+ begin
139
+ yield
140
+ rescue => e
141
+ error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::STORE_ERROR, 0, e.to_s, Time.now)
142
+ update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
143
+ raise
144
+ end
145
+ end
146
+
147
+
148
+ #
149
+ # @param [Hash] old_data
150
+ # @param [Hash] new_data
151
+ # @return [Set]
152
+ #
153
+ private def compute_changed_items_for_full_data_set(old_data, new_data)
154
+ affected_items = Set.new
155
+
156
+ LaunchDarkly::ALL_KINDS.each do |kind|
157
+ old_items = old_data[kind] || {}
158
+ new_items = new_data[kind] || {}
159
+
160
+ old_items.keys.concat(new_items.keys).each do |key|
161
+ old_item = old_items[key]
162
+ new_item = new_items[key]
163
+
164
+ next if old_item.nil? && new_item.nil?
165
+
166
+ if old_item.nil? || new_item.nil? || old_item[:version] < new_item[:version]
167
+ @dependency_tracker.add_affected_items(affected_items, {kind: kind, key: key.to_s})
168
+ end
169
+ end
170
+ end
171
+
172
+ affected_items
173
+ end
174
+
175
+ #
176
+ # @param affected_items [Set]
177
+ #
178
+ private def send_change_events(affected_items)
179
+ affected_items.each do |item|
180
+ if item[:kind] == LaunchDarkly::FEATURES
181
+ @flag_change_broadcaster.broadcast(LaunchDarkly::Interfaces::FlagChange.new(item[:key]))
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,109 @@
1
+ require 'concurrent'
2
+ require "ldclient-rb/interfaces"
3
+
4
+ module LaunchDarkly
5
+ module Impl
6
+ module DataStore
7
+
8
+ class DataKind
9
+ FEATURES = "features".freeze
10
+ SEGMENTS = "segments".freeze
11
+
12
+ FEATURE_PREREQ_FN = lambda { |flag| (flag[:prerequisites] || []).map { |p| p[:key] } }.freeze
13
+
14
+ attr_reader :namespace
15
+ attr_reader :priority
16
+
17
+ #
18
+ # @param namespace [String]
19
+ # @param priority [Integer]
20
+ #
21
+ def initialize(namespace:, priority:)
22
+ @namespace = namespace
23
+ @priority = priority
24
+ end
25
+
26
+ #
27
+ # Maintain the same behavior when these data kinds were standard ruby hashes.
28
+ #
29
+ # @param key [Symbol]
30
+ # @return [Object]
31
+ #
32
+ def [](key)
33
+ return priority if key == :priority
34
+ return namespace if key == :namespace
35
+ return get_dependency_keys_fn() if key == :get_dependency_keys
36
+ nil
37
+ end
38
+
39
+ #
40
+ # Retrieve the dependency keys for a particular data kind. Right now, this is only defined for flags.
41
+ #
42
+ def get_dependency_keys_fn()
43
+ return nil unless @namespace == FEATURES
44
+
45
+ FEATURE_PREREQ_FN
46
+ end
47
+
48
+ def eql?(other)
49
+ other.is_a?(DataKind) && namespace == other.namespace && priority == other.priority
50
+ end
51
+
52
+ def hash
53
+ [namespace, priority].hash
54
+ end
55
+ end
56
+
57
+ class StatusProvider
58
+ include LaunchDarkly::Interfaces::DataStore::StatusProvider
59
+
60
+ def initialize(store, update_sink)
61
+ # @type [LaunchDarkly::Impl::FeatureStoreClientWrapper]
62
+ @store = store
63
+ # @type [UpdateSink]
64
+ @update_sink = update_sink
65
+ end
66
+
67
+ def status
68
+ @update_sink.last_status.get
69
+ end
70
+
71
+ def monitoring_enabled?
72
+ @store.monitoring_enabled?
73
+ end
74
+
75
+ def add_listener(listener)
76
+ @update_sink.broadcaster.add_listener(listener)
77
+ end
78
+
79
+ def remove_listener(listener)
80
+ @update_sink.broadcaster.remove_listener(listener)
81
+ end
82
+ end
83
+
84
+ class UpdateSink
85
+ include LaunchDarkly::Interfaces::DataStore::UpdateSink
86
+
87
+ # @return [LaunchDarkly::Impl::Broadcaster]
88
+ attr_reader :broadcaster
89
+
90
+ # @return [Concurrent::AtomicReference]
91
+ attr_reader :last_status
92
+
93
+ def initialize(broadcaster)
94
+ @broadcaster = broadcaster
95
+ @last_status = Concurrent::AtomicReference.new(
96
+ LaunchDarkly::Interfaces::DataStore::Status.new(true, false)
97
+ )
98
+ end
99
+
100
+ def update_status(status)
101
+ return if status.nil?
102
+
103
+ old_status = @last_status.get_and_set(status)
104
+ @broadcaster.broadcast(status) unless old_status == status
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,102 @@
1
+ module LaunchDarkly
2
+ module Impl
3
+ class DependencyTracker
4
+ def initialize
5
+ @from = {}
6
+ @to = {}
7
+ end
8
+
9
+ #
10
+ # Updates the dependency graph when an item has changed.
11
+ #
12
+ # @param from_kind [Object] the changed item's kind
13
+ # @param from_key [String] the changed item's key
14
+ # @param from_item [Object] the changed item
15
+ #
16
+ def update_dependencies_from(from_kind, from_key, from_item)
17
+ from_what = { kind: from_kind, key: from_key }
18
+ updated_dependencies = DependencyTracker.compute_dependencies_from(from_kind, from_item)
19
+
20
+ old_dependency_set = @from[from_what]
21
+ unless old_dependency_set.nil?
22
+ old_dependency_set.each do |kind_and_key|
23
+ deps_to_this_old_dep = @to[kind_and_key]
24
+ deps_to_this_old_dep&.delete(from_what)
25
+ end
26
+ end
27
+
28
+ @from[from_what] = updated_dependencies
29
+ updated_dependencies.each do |kind_and_key|
30
+ deps_to_this_new_dep = @to[kind_and_key]
31
+ if deps_to_this_new_dep.nil?
32
+ deps_to_this_new_dep = Set.new
33
+ @to[kind_and_key] = deps_to_this_new_dep
34
+ end
35
+ deps_to_this_new_dep.add(from_what)
36
+ end
37
+ end
38
+
39
+ def self.segment_keys_from_clauses(clauses)
40
+ clauses.flat_map do |clause|
41
+ if clause.op == :segmentMatch
42
+ clause.values.map { |value| {kind: LaunchDarkly::SEGMENTS, key: value }}
43
+ else
44
+ []
45
+ end
46
+ end
47
+ end
48
+
49
+ #
50
+ # @param from_kind [String]
51
+ # @param from_item [LaunchDarkly::Impl::Model::FeatureFlag, LaunchDarkly::Impl::Model::Segment]
52
+ # @return [Set]
53
+ #
54
+ def self.compute_dependencies_from(from_kind, from_item)
55
+ return Set.new if from_item.nil?
56
+
57
+ if from_kind == LaunchDarkly::FEATURES
58
+ prereq_keys = from_item.prerequisites.map { |prereq| {kind: from_kind, key: prereq.key} }
59
+ segment_keys = from_item.rules.flat_map { |rule| DependencyTracker.segment_keys_from_clauses(rule.clauses) }
60
+
61
+ results = Set.new(prereq_keys)
62
+ results.merge(segment_keys)
63
+ elsif from_kind == LaunchDarkly::SEGMENTS
64
+ kind_and_keys = from_item.rules.flat_map do |rule|
65
+ DependencyTracker.segment_keys_from_clauses(rule.clauses)
66
+ end
67
+ Set.new(kind_and_keys)
68
+ else
69
+ Set.new
70
+ end
71
+ end
72
+
73
+ #
74
+ # Clear any tracked dependencies and reset the tracking state to a clean slate.
75
+ #
76
+ def reset
77
+ @from.clear
78
+ @to.clear
79
+ end
80
+
81
+ #
82
+ # Populates the given set with the union of the initial item and all items that directly or indirectly
83
+ # depend on it (based on the current state of the dependency graph).
84
+ #
85
+ # @param items_out [Set]
86
+ # @param initial_modified_item [Object]
87
+ #
88
+ def add_affected_items(items_out, initial_modified_item)
89
+ return if items_out.include? initial_modified_item
90
+
91
+ items_out.add(initial_modified_item)
92
+ affected_items = @to[initial_modified_item]
93
+
94
+ return if affected_items.nil?
95
+
96
+ affected_items.each do |affected_item|
97
+ add_affected_items(items_out, affected_item)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,129 @@
1
+ require "ldclient-rb/impl/util"
2
+
3
+ require "rbconfig"
4
+ require "securerandom"
5
+
6
+ module LaunchDarkly
7
+ module Impl
8
+ class DiagnosticAccumulator
9
+ def self.create_diagnostic_id(sdk_key)
10
+ {
11
+ diagnosticId: SecureRandom.uuid,
12
+ sdkKeySuffix: sdk_key[-6..-1] || sdk_key,
13
+ }
14
+ end
15
+
16
+ def initialize(diagnostic_id)
17
+ @id = diagnostic_id
18
+ @lock = Mutex.new
19
+ self.reset(Util.current_time_millis)
20
+ end
21
+
22
+ def reset(time)
23
+ @data_since_date = time
24
+ @stream_inits = []
25
+ end
26
+
27
+ def create_init_event(config)
28
+ {
29
+ kind: 'diagnostic-init',
30
+ creationDate: Util.current_time_millis,
31
+ id: @id,
32
+ configuration: DiagnosticAccumulator.make_config_data(config),
33
+ sdk: DiagnosticAccumulator.make_sdk_data(config),
34
+ platform: DiagnosticAccumulator.make_platform_data,
35
+ }
36
+ end
37
+
38
+ def record_stream_init(timestamp, failed, duration_millis)
39
+ @lock.synchronize do
40
+ @stream_inits.push({ timestamp: timestamp, failed: failed, durationMillis: duration_millis })
41
+ end
42
+ end
43
+
44
+ def create_periodic_event_and_reset(dropped_events, deduplicated_users, events_in_last_batch)
45
+ previous_stream_inits = @lock.synchronize do
46
+ si = @stream_inits
47
+ @stream_inits = []
48
+ si
49
+ end
50
+
51
+ current_time = Util.current_time_millis
52
+ event = {
53
+ kind: 'diagnostic',
54
+ creationDate: current_time,
55
+ id: @id,
56
+ dataSinceDate: @data_since_date,
57
+ droppedEvents: dropped_events,
58
+ deduplicatedUsers: deduplicated_users,
59
+ eventsInLastBatch: events_in_last_batch,
60
+ streamInits: previous_stream_inits,
61
+ }
62
+ @data_since_date = current_time
63
+ event
64
+ end
65
+
66
+ def self.make_config_data(config)
67
+ ret = {
68
+ allAttributesPrivate: config.all_attributes_private,
69
+ connectTimeoutMillis: self.seconds_to_millis(config.connect_timeout),
70
+ customBaseURI: config.base_uri != Config.default_base_uri,
71
+ customEventsURI: config.events_uri != Config.default_events_uri,
72
+ customStreamURI: config.stream_uri != Config.default_stream_uri,
73
+ diagnosticRecordingIntervalMillis: self.seconds_to_millis(config.diagnostic_recording_interval),
74
+ eventsCapacity: config.capacity,
75
+ eventsFlushIntervalMillis: self.seconds_to_millis(config.flush_interval),
76
+ pollingIntervalMillis: self.seconds_to_millis(config.poll_interval),
77
+ socketTimeoutMillis: self.seconds_to_millis(config.read_timeout),
78
+ streamingDisabled: !config.stream?,
79
+ userKeysCapacity: config.context_keys_capacity,
80
+ userKeysFlushIntervalMillis: self.seconds_to_millis(config.context_keys_flush_interval),
81
+ usingProxy: ENV.has_key?('http_proxy') || ENV.has_key?('https_proxy') || ENV.has_key?('HTTP_PROXY') || ENV.has_key?('HTTPS_PROXY'),
82
+ usingRelayDaemon: config.use_ldd?,
83
+ }
84
+ ret
85
+ end
86
+
87
+ def self.make_sdk_data(config)
88
+ ret = {
89
+ name: 'ruby-server-sdk',
90
+ version: LaunchDarkly::VERSION,
91
+ }
92
+ if config.wrapper_name
93
+ ret[:wrapperName] = config.wrapper_name
94
+ ret[:wrapperVersion] = config.wrapper_version
95
+ end
96
+ ret
97
+ end
98
+
99
+ def self.make_platform_data
100
+ conf = RbConfig::CONFIG
101
+ {
102
+ name: 'ruby',
103
+ osArch: conf['host_cpu'],
104
+ osName: self.normalize_os_name(conf['host_os']),
105
+ osVersion: 'unknown', # there seems to be no portable way to detect this in Ruby
106
+ rubyVersion: conf['ruby_version'],
107
+ rubyImplementation: Object.constants.include?(:RUBY_ENGINE) ? RUBY_ENGINE : 'unknown',
108
+ }
109
+ end
110
+
111
+ def self.normalize_os_name(name)
112
+ case name
113
+ when /linux|arch/i
114
+ 'Linux'
115
+ when /darwin/i
116
+ 'MacOS'
117
+ when /mswin|windows/i
118
+ 'Windows'
119
+ else
120
+ name
121
+ end
122
+ end
123
+
124
+ def self.seconds_to_millis(s)
125
+ (s * 1000).to_i
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,34 @@
1
+ module LaunchDarkly
2
+ module Impl
3
+ #
4
+ # Simple helper class for returning formatted data.
5
+ #
6
+ # The variation methods make use of the new hook support. Those methods all need to return an evaluation detail, and
7
+ # some other unstructured bit of data.
8
+ #
9
+ class EvaluationWithHookResult
10
+ #
11
+ # Return the evaluation detail that was generated as part of the evaluation.
12
+ #
13
+ # @return [LaunchDarkly::EvaluationDetail]
14
+ #
15
+ attr_reader :evaluation_detail
16
+
17
+ #
18
+ # All purpose container for additional return values from the wrapping method
19
+ #
20
+ # @return [any]
21
+ #
22
+ attr_reader :results
23
+
24
+ #
25
+ # @param evaluation_detail [LaunchDarkly::EvaluationDetail]
26
+ # @param results [any]
27
+ #
28
+ def initialize(evaluation_detail, results = nil)
29
+ @evaluation_detail = evaluation_detail
30
+ @results = results
31
+ end
32
+ end
33
+ end
34
+ end