launchdarkly-server-sdk 6.3.4 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50934709e1ee8c075c56c149f75753f87498eb9e1f06e39597d2cd4514ea7196
4
- data.tar.gz: e7f153366b7b50c3951f844bf9ce1a0b81093fc733818dbb02e8f0a888d9ac26
3
+ metadata.gz: 147fe8142bffa0d8e754e86669f524f6d787930e8bc6b8a14d30429933a036e3
4
+ data.tar.gz: e4a404cb7bc911f7c156c3fe5059ee9a4a9d7ebfeacba4c52f44ceb116276835
5
5
  SHA512:
6
- metadata.gz: 2469e536ca482489eb46f3ff650aeb573eb691ab6c6c2b8ed669b7f8ba799bf65d68d40f6cabc51ec4b051dd56e309430f94e5dc4fbbd70193efa5156d1d1082
7
- data.tar.gz: 454219746c7b296fd8c92ee5e266f96765f72527706b7d6f7360445eb1d245e2acb37e4cb722ed48bf73dce8e307d2121771270ea9bedb71e91d67f9a79b8ea0
6
+ metadata.gz: '039348a1a0c9284e79e641b69f11bdc4e8ba57c4cad3320eea6f8bb8caf8fc0f52a02c427a2b4f26584945e5750b7728706855cfaf6bdc22a1863b55bcf934a6'
7
+ data.tar.gz: 452c574e6ff6323a74dfdcf6a5c936ff38bfaa0efc3b58ff190068322ea82e25b15dc20868eb22bf93361d3daa699bd51fa362e97f30f4fc6bcf402f9f5597d9
@@ -44,6 +44,7 @@ module LaunchDarkly
44
44
  # @option opts [String] :wrapper_version See {#wrapper_version}.
45
45
  # @option opts [#open] :socket_factory See {#socket_factory}.
46
46
  # @option opts [BigSegmentsConfig] :big_segments See {#big_segments}.
47
+ # @option opts [Hash] :application See {#application}
47
48
  #
48
49
  def initialize(opts = {})
49
50
  @base_uri = (opts[:base_uri] || Config.default_base_uri).chomp("/")
@@ -77,6 +78,7 @@ module LaunchDarkly
77
78
  @wrapper_version = opts[:wrapper_version]
78
79
  @socket_factory = opts[:socket_factory]
79
80
  @big_segments = opts[:big_segments] || BigSegmentsConfig.new(store: nil)
81
+ @application = LaunchDarkly::Impl::Util.validate_application_info(opts[:application] || {}, @logger)
80
82
  end
81
83
 
82
84
  #
@@ -284,6 +286,24 @@ module LaunchDarkly
284
286
  #
285
287
  attr_reader :big_segments
286
288
 
289
+ #
290
+ # An object that allows configuration of application metadata.
291
+ #
292
+ # Application metadata may be used in LaunchDarkly analytics or other product features, but does not affect feature flag evaluations.
293
+ #
294
+ # If you want to set non-default values for any of these fields, provide the appropriately configured hash to the {Config} object.
295
+ #
296
+ # @example Configuring application information
297
+ # opts[:application] = {
298
+ # id: "MY APPLICATION ID",
299
+ # version: "MY APPLICATION VERSION"
300
+ # }
301
+ # config = LDConfig.new(opts)
302
+ #
303
+ # @return [Hash]
304
+ #
305
+ attr_reader :application
306
+
287
307
  # @deprecated This is replaced by {#data_source}.
288
308
  attr_reader :update_processor
289
309
 
@@ -1,5 +1,6 @@
1
1
  require "ldclient-rb/evaluation_detail"
2
2
  require "ldclient-rb/impl/evaluator_bucketing"
3
+ require "ldclient-rb/impl/evaluator_helpers"
3
4
  require "ldclient-rb/impl/evaluator_operators"
4
5
 
5
6
  module LaunchDarkly
@@ -87,19 +88,17 @@ module LaunchDarkly
87
88
 
88
89
  def eval_internal(flag, user, state)
89
90
  if !flag[:on]
90
- return get_off_value(flag, EvaluationReason::off)
91
+ return EvaluatorHelpers.off_result(flag)
91
92
  end
92
93
 
93
- prereq_failure_reason = check_prerequisites(flag, user, state)
94
- if !prereq_failure_reason.nil?
95
- return get_off_value(flag, prereq_failure_reason)
96
- end
94
+ prereq_failure_result = check_prerequisites(flag, user, state)
95
+ return prereq_failure_result if !prereq_failure_result.nil?
97
96
 
98
97
  # Check user target matches
99
98
  (flag[:targets] || []).each do |target|
100
99
  (target[:values] || []).each do |value|
101
100
  if value == user[:key]
102
- return get_variation(flag, target[:variation], EvaluationReason::target_match)
101
+ return EvaluatorHelpers.target_match_result(target, flag)
103
102
  end
104
103
  end
105
104
  end
@@ -111,13 +110,15 @@ module LaunchDarkly
111
110
  if rule_match_user(rule, user, state)
112
111
  reason = rule[:_reason] # try to use cached reason for this rule
113
112
  reason = EvaluationReason::rule_match(i, rule[:id]) if reason.nil?
114
- return get_value_for_variation_or_rollout(flag, rule, user, reason)
113
+ return get_value_for_variation_or_rollout(flag, rule, user, reason,
114
+ EvaluatorHelpers.rule_precomputed_results(rule))
115
115
  end
116
116
  end
117
117
 
118
118
  # Check the fallthrough rule
119
119
  if !flag[:fallthrough].nil?
120
- return get_value_for_variation_or_rollout(flag, flag[:fallthrough], user, EvaluationReason::fallthrough)
120
+ return get_value_for_variation_or_rollout(flag, flag[:fallthrough], user, EvaluationReason::fallthrough,
121
+ EvaluatorHelpers.fallthrough_precomputed_results(flag))
121
122
  end
122
123
 
123
124
  return EvaluationDetail.new(nil, nil, EvaluationReason::fallthrough)
@@ -149,8 +150,7 @@ module LaunchDarkly
149
150
  end
150
151
  end
151
152
  if !prereq_ok
152
- reason = prerequisite[:_reason] # try to use cached reason
153
- return reason.nil? ? EvaluationReason::prerequisite_failed(prereq_key) : reason
153
+ return EvaluatorHelpers.prerequisite_failed_result(prerequisite, flag)
154
154
  end
155
155
  end
156
156
  nil
@@ -253,35 +253,26 @@ module LaunchDarkly
253
253
  end
254
254
 
255
255
  private
256
-
257
- def get_variation(flag, index, reason)
258
- if index < 0 || index >= flag[:variations].length
259
- @logger.error("[LDClient] Data inconsistency in feature flag \"#{flag[:key]}\": invalid variation index")
260
- return Evaluator.error_result(EvaluationReason::ERROR_MALFORMED_FLAG)
261
- end
262
- EvaluationDetail.new(flag[:variations][index], index, reason)
263
- end
264
-
265
- def get_off_value(flag, reason)
266
- if flag[:offVariation].nil? # off variation unspecified - return default value
267
- return EvaluationDetail.new(nil, nil, reason)
268
- end
269
- get_variation(flag, flag[:offVariation], reason)
270
- end
271
-
272
- def get_value_for_variation_or_rollout(flag, vr, user, reason)
256
+
257
+ def get_value_for_variation_or_rollout(flag, vr, user, reason, precomputed_results)
273
258
  index, in_experiment = EvaluatorBucketing.variation_index_for_user(flag, vr, user)
274
- #if in experiment is true, set reason to a different reason instance/singleton with in_experiment set
275
- if in_experiment && reason.kind == :FALLTHROUGH
276
- reason = EvaluationReason::fallthrough(in_experiment)
277
- elsif in_experiment && reason.kind == :RULE_MATCH
278
- reason = EvaluationReason::rule_match(reason.rule_index, reason.rule_id, in_experiment)
279
- end
280
259
  if index.nil?
281
260
  @logger.error("[LDClient] Data inconsistency in feature flag \"#{flag[:key]}\": variation/rollout object with no variation or rollout")
282
261
  return Evaluator.error_result(EvaluationReason::ERROR_MALFORMED_FLAG)
283
262
  end
284
- return get_variation(flag, index, reason)
263
+ if precomputed_results
264
+ return precomputed_results.for_variation(index, in_experiment)
265
+ else
266
+ #if in experiment is true, set reason to a different reason instance/singleton with in_experiment set
267
+ if in_experiment
268
+ if reason.kind == :FALLTHROUGH
269
+ reason = EvaluationReason::fallthrough(in_experiment)
270
+ elsif reason.kind == :RULE_MATCH
271
+ reason = EvaluationReason::rule_match(reason.rule_index, reason.rule_id, in_experiment)
272
+ end
273
+ end
274
+ return EvaluatorHelpers.evaluation_detail_for_variation(flag, index, reason)
275
+ end
285
276
  end
286
277
  end
287
278
  end
@@ -0,0 +1,53 @@
1
+ require "ldclient-rb/evaluation_detail"
2
+
3
+ # This file contains any pieces of low-level evaluation logic that don't need to be inside the Evaluator
4
+ # class, because they don't depend on any SDK state outside of their input parameters.
5
+
6
+ module LaunchDarkly
7
+ module Impl
8
+ module EvaluatorHelpers
9
+ def self.off_result(flag, logger = nil)
10
+ pre = flag[:_preprocessed]
11
+ pre ? pre.off_result : evaluation_detail_for_off_variation(flag, EvaluationReason::off, logger)
12
+ end
13
+
14
+ def self.target_match_result(target, flag, logger = nil)
15
+ pre = target[:_preprocessed]
16
+ pre ? pre.match_result : evaluation_detail_for_variation(
17
+ flag, target[:variation], EvaluationReason::target_match, logger)
18
+ end
19
+
20
+ def self.prerequisite_failed_result(prereq, flag, logger = nil)
21
+ pre = prereq[:_preprocessed]
22
+ pre ? pre.failed_result : evaluation_detail_for_off_variation(
23
+ flag, EvaluationReason::prerequisite_failed(prereq[:key]), logger
24
+ )
25
+ end
26
+
27
+ def self.fallthrough_precomputed_results(flag)
28
+ pre = flag[:_preprocessed]
29
+ pre ? pre.fallthrough_factory : nil
30
+ end
31
+
32
+ def self.rule_precomputed_results(rule)
33
+ pre = rule[:_preprocessed]
34
+ pre ? pre.all_match_results : nil
35
+ end
36
+
37
+ def self.evaluation_detail_for_off_variation(flag, reason, logger = nil)
38
+ index = flag[:offVariation]
39
+ index.nil? ? EvaluationDetail.new(nil, nil, reason) : evaluation_detail_for_variation(flag, index, reason, logger)
40
+ end
41
+
42
+ def self.evaluation_detail_for_variation(flag, index, reason, logger = nil)
43
+ vars = flag[:variations] || []
44
+ if index < 0 || index >= vars.length
45
+ logger.error("[LDClient] Data inconsistency in feature flag \"#{flag[:key]}\": invalid variation index") unless logger.nil?
46
+ EvaluationDetail.new(nil, nil, EvaluationReason::error(EvaluationReason::ERROR_MALFORMED_FLAG))
47
+ else
48
+ EvaluationDetail.new(vars[index], index, reason)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,177 @@
1
+ require "ldclient-rb/impl/evaluator_helpers"
2
+
3
+ module LaunchDarkly
4
+ module Impl
5
+ module DataModelPreprocessing
6
+ #
7
+ # Container for a precomputed result that includes a specific variation index and value, an
8
+ # evaluation reason, and optionally an alternate evaluation reason that corresponds to the
9
+ # "in experiment" state.
10
+ #
11
+ class EvalResultsForSingleVariation
12
+ def initialize(value, variation_index, regular_reason, in_experiment_reason = nil)
13
+ @regular_result = EvaluationDetail.new(value, variation_index, regular_reason)
14
+ @in_experiment_result = in_experiment_reason ?
15
+ EvaluationDetail.new(value, variation_index, in_experiment_reason) :
16
+ @regular_result
17
+ end
18
+
19
+ # @param in_experiment [Boolean] indicates whether we want the result to include
20
+ # "inExperiment: true" in the reason or not
21
+ # @return [EvaluationDetail]
22
+ def get_result(in_experiment = false)
23
+ in_experiment ? @in_experiment_result : @regular_result
24
+ end
25
+ end
26
+
27
+ #
28
+ # Container for a set of precomputed results, one for each possible flag variation.
29
+ #
30
+ class EvalResultFactoryMultiVariations
31
+ def initialize(variation_factories)
32
+ @factories = variation_factories
33
+ end
34
+
35
+ # @param index [Integer] the variation index
36
+ # @param in_experiment [Boolean] indicates whether we want the result to include
37
+ # "inExperiment: true" in the reason or not
38
+ def for_variation(index, in_experiment)
39
+ if index < 0 || index >= @factories.length
40
+ EvaluationDetail.new(nil, nil, EvaluationReason.error(EvaluationReason::ERROR_MALFORMED_FLAG))
41
+ else
42
+ @factories[index].get_result(in_experiment)
43
+ end
44
+ end
45
+ end
46
+
47
+ # Base class for all of the preprocessed data classes we embed in our data model. Using this class
48
+ # ensures that none of its properties will be included in JSON representations. It also overrides
49
+ # == to say that it is always equal with another instance of the same class; equality tests on
50
+ # this class are only ever done in test code, and we want the contents of these classes to be
51
+ # ignored in test code unless we are looking at specific attributes.
52
+ class PreprocessedDataBase
53
+ def as_json(*)
54
+ nil
55
+ end
56
+
57
+ def to_json(*a)
58
+ "null"
59
+ end
60
+
61
+ def ==(other)
62
+ other.class == self.class
63
+ end
64
+ end
65
+
66
+ class FlagPreprocessed < PreprocessedDataBase
67
+ def initialize(off_result, fallthrough_factory)
68
+ @off_result = off_result
69
+ @fallthrough_factory = fallthrough_factory
70
+ end
71
+
72
+ # @return [EvalResultsForSingleVariation]
73
+ attr_reader :off_result
74
+ # @return [EvalResultFactoryMultiVariations]
75
+ attr_reader :fallthrough_factory
76
+ end
77
+
78
+ class PrerequisitePreprocessed < PreprocessedDataBase
79
+ def initialize(failed_result)
80
+ @failed_result = failed_result
81
+ end
82
+
83
+ # @return [EvalResultsForSingleVariation]
84
+ attr_reader :failed_result
85
+ end
86
+
87
+ class TargetPreprocessed < PreprocessedDataBase
88
+ def initialize(match_result)
89
+ @match_result = match_result
90
+ end
91
+
92
+ # @return [EvalResultsForSingleVariation]
93
+ attr_reader :match_result
94
+ end
95
+
96
+ class FlagRulePreprocessed < PreprocessedDataBase
97
+ def initialize(all_match_results)
98
+ @all_match_results = all_match_results
99
+ end
100
+
101
+ # @return [EvalResultsForSingleVariation]
102
+ attr_reader :all_match_results
103
+ end
104
+
105
+ class Preprocessor
106
+ def initialize(logger = nil)
107
+ @logger = logger
108
+ end
109
+
110
+ def preprocess_item!(kind, item)
111
+ if kind.eql? FEATURES
112
+ preprocess_flag!(item)
113
+ elsif kind.eql? SEGMENTS
114
+ preprocess_segment!(item)
115
+ end
116
+ end
117
+
118
+ def preprocess_all_items!(kind, items_map)
119
+ return items_map if !items_map
120
+ items_map.each do |key, item|
121
+ preprocess_item!(kind, item)
122
+ end
123
+ end
124
+
125
+ def preprocess_flag!(flag)
126
+ flag[:_preprocessed] = FlagPreprocessed.new(
127
+ EvaluatorHelpers.off_result(flag),
128
+ precompute_multi_variation_results(flag, EvaluationReason::fallthrough(false), EvaluationReason::fallthrough(true))
129
+ )
130
+ (flag[:prerequisites] || []).each do |prereq|
131
+ preprocess_prerequisite!(prereq, flag)
132
+ end
133
+ (flag[:targets] || []).each do |target|
134
+ preprocess_target!(target, flag)
135
+ end
136
+ rules = flag[:rules]
137
+ (rules || []).each_index do |index|
138
+ preprocess_flag_rule!(rules[index], index, flag)
139
+ end
140
+ end
141
+
142
+ def preprocess_segment!(segment)
143
+ # nothing to do for segments currently
144
+ end
145
+
146
+ private def preprocess_prerequisite!(prereq, flag)
147
+ prereq[:_preprocessed] = PrerequisitePreprocessed.new(
148
+ EvaluatorHelpers.prerequisite_failed_result(prereq, flag, @logger)
149
+ )
150
+ end
151
+
152
+ private def preprocess_target!(target, flag)
153
+ target[:_preprocessed] = TargetPreprocessed.new(
154
+ EvaluatorHelpers.target_match_result(target, flag, @logger)
155
+ )
156
+ end
157
+
158
+ private def preprocess_flag_rule!(rule, index, flag)
159
+ match_reason = EvaluationReason::rule_match(index, rule[:id])
160
+ match_reason_in_experiment = EvaluationReason::rule_match(index, rule[:id], true)
161
+ rule[:_preprocessed] = FlagRulePreprocessed.new(
162
+ precompute_multi_variation_results(flag, match_reason, match_reason_in_experiment)
163
+ )
164
+ end
165
+
166
+ private def precompute_multi_variation_results(flag, regular_reason, in_experiment_reason)
167
+ factories = []
168
+ vars = flag[:variations] || []
169
+ vars.each_index do |index|
170
+ factories << EvalResultsForSingleVariation.new(vars[index], index, regular_reason, in_experiment_reason)
171
+ end
172
+ EvalResultFactoryMultiVariations.new(factories)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -1,13 +1,14 @@
1
+ require "ldclient-rb/impl/model/preprocessed_data"
1
2
 
2
3
  module LaunchDarkly
3
4
  module Impl
4
5
  module Model
5
6
  # Abstraction of deserializing a feature flag or segment that was read from a data store or
6
7
  # received from LaunchDarkly.
7
- def self.deserialize(kind, json)
8
+ def self.deserialize(kind, json, logger = nil)
8
9
  return nil if json.nil?
9
10
  item = JSON.parse(json, symbolize_names: true)
10
- postprocess_item_after_deserializing!(kind, item)
11
+ DataModelPreprocessing::Preprocessor.new(logger).preprocess_item!(kind, item)
11
12
  item
12
13
  end
13
14
 
@@ -18,45 +19,14 @@ module LaunchDarkly
18
19
  end
19
20
 
20
21
  # Translates a { flags: ..., segments: ... } object received from LaunchDarkly to the data store format.
21
- def self.make_all_store_data(received_data)
22
+ def self.make_all_store_data(received_data, logger = nil)
23
+ preprocessor = DataModelPreprocessing::Preprocessor.new(logger)
22
24
  flags = received_data[:flags]
23
- postprocess_items_after_deserializing!(FEATURES, flags)
25
+ preprocessor.preprocess_all_items!(FEATURES, flags)
24
26
  segments = received_data[:segments]
25
- postprocess_items_after_deserializing!(SEGMENTS, segments)
27
+ preprocessor.preprocess_all_items!(SEGMENTS, segments)
26
28
  { FEATURES => flags, SEGMENTS => segments }
27
29
  end
28
-
29
- # Called after we have deserialized a model item from JSON (because we received it from LaunchDarkly,
30
- # or read it from a persistent data store). This allows us to precompute some derived attributes that
31
- # will never change during the lifetime of that item.
32
- def self.postprocess_item_after_deserializing!(kind, item)
33
- return if !item
34
- # Currently we are special-casing this for FEATURES; eventually it will be handled by delegating
35
- # to the "kind" object or the item class.
36
- if kind.eql? FEATURES
37
- # For feature flags, we precompute all possible parameterized EvaluationReason instances.
38
- prereqs = item[:prerequisites]
39
- if !prereqs.nil?
40
- prereqs.each do |prereq|
41
- prereq[:_reason] = EvaluationReason::prerequisite_failed(prereq[:key])
42
- end
43
- end
44
- rules = item[:rules]
45
- if !rules.nil?
46
- rules.each_index do |i|
47
- rule = rules[i]
48
- rule[:_reason] = EvaluationReason::rule_match(i, rule[:id])
49
- end
50
- end
51
- end
52
- end
53
-
54
- def self.postprocess_items_after_deserializing!(kind, items_map)
55
- return items_map if !items_map
56
- items_map.each do |key, item|
57
- postprocess_item_after_deserializing!(kind, item)
58
- end
59
- end
60
30
  end
61
31
  end
62
32
  end
@@ -15,8 +15,66 @@ module LaunchDarkly
15
15
  ret["X-LaunchDarkly-Wrapper"] = config.wrapper_name +
16
16
  (config.wrapper_version ? "/" + config.wrapper_version : "")
17
17
  end
18
+
19
+ app_value = application_header_value config.application
20
+ ret["X-LaunchDarkly-Tags"] = app_value unless app_value.nil? || app_value.empty?
21
+
18
22
  ret
19
23
  end
24
+
25
+ #
26
+ # Generate an HTTP Header value containing the application meta information (@see #application).
27
+ #
28
+ # @return [String]
29
+ #
30
+ def self.application_header_value(application)
31
+ parts = []
32
+ unless application[:id].empty?
33
+ parts << "application-id/#{application[:id]}"
34
+ end
35
+
36
+ unless application[:version].empty?
37
+ parts << "application-version/#{application[:version]}"
38
+ end
39
+
40
+ parts.join(" ")
41
+ end
42
+
43
+ #
44
+ # @param value [String]
45
+ # @param name [Symbol]
46
+ # @param logger [Logger]
47
+ # @return [String]
48
+ #
49
+ def self.validate_application_value(value, name, logger)
50
+ value = value.to_s
51
+
52
+ return "" if value.empty?
53
+
54
+ if value.length > 64
55
+ logger.warn { "Value of application[#{name}] was longer than 64 characters and was discarded" }
56
+ return ""
57
+ end
58
+
59
+ if value.match(/[^a-zA-Z0-9._-]/)
60
+ logger.warn { "Value of application[#{name}] contained invalid characters and was discarded" }
61
+ return ""
62
+ end
63
+
64
+ value
65
+ end
66
+
67
+ #
68
+ # @param app [Hash]
69
+ # @param logger [Logger]
70
+ # @return [Hash]
71
+ #
72
+ def self.validate_application_info(app, logger)
73
+ {
74
+ id: validate_application_value(app[:id], :id, logger),
75
+ version: validate_application_value(app[:version], :version, logger),
76
+ }
77
+ end
20
78
  end
21
79
  end
22
80
  end
@@ -31,7 +31,7 @@ module LaunchDarkly
31
31
 
32
32
  def request_all_data()
33
33
  all_data = JSON.parse(make_request("/sdk/latest-all"), symbolize_names: true)
34
- Impl::Model.make_all_store_data(all_data)
34
+ Impl::Model.make_all_store_data(all_data, @config.logger)
35
35
  end
36
36
 
37
37
  def stop
@@ -44,7 +44,7 @@ module LaunchDarkly
44
44
  private
45
45
 
46
46
  def request_single_item(kind, path)
47
- Impl::Model.deserialize(kind, make_request(path))
47
+ Impl::Model.deserialize(kind, make_request(path), @config.logger)
48
48
  end
49
49
 
50
50
  def make_request(path)
@@ -86,7 +86,7 @@ module LaunchDarkly
86
86
  @config.logger.debug { "[LDClient] Stream received #{method} message: #{message.data}" }
87
87
  if method == PUT
88
88
  message = JSON.parse(message.data, symbolize_names: true)
89
- all_data = Impl::Model.make_all_store_data(message[:data])
89
+ all_data = Impl::Model.make_all_store_data(message[:data], @config.logger)
90
90
  @feature_store.init(all_data)
91
91
  @initialized.make_true
92
92
  @config.logger.info { "[LDClient] Stream initialized" }
@@ -97,7 +97,7 @@ module LaunchDarkly
97
97
  key = key_for_path(kind, data[:path])
98
98
  if key
99
99
  data = data[:data]
100
- Impl::Model.postprocess_item_after_deserializing!(kind, data)
100
+ Impl::DataModelPreprocessing::Preprocessor.new(@config.logger).preprocess_item!(kind, data)
101
101
  @feature_store.upsert(kind, data)
102
102
  break
103
103
  end
@@ -1,3 +1,3 @@
1
1
  module LaunchDarkly
2
- VERSION = "6.3.4"
2
+ VERSION = "6.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: launchdarkly-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.4
4
+ version: 6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-30 00:00:00.000000000 Z
11
+ date: 2022-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -263,6 +263,7 @@ files:
263
263
  - lib/ldclient-rb/impl/diagnostic_events.rb
264
264
  - lib/ldclient-rb/impl/evaluator.rb
265
265
  - lib/ldclient-rb/impl/evaluator_bucketing.rb
266
+ - lib/ldclient-rb/impl/evaluator_helpers.rb
266
267
  - lib/ldclient-rb/impl/evaluator_operators.rb
267
268
  - lib/ldclient-rb/impl/event_sender.rb
268
269
  - lib/ldclient-rb/impl/event_summarizer.rb
@@ -272,6 +273,7 @@ files:
272
273
  - lib/ldclient-rb/impl/integrations/file_data_source.rb
273
274
  - lib/ldclient-rb/impl/integrations/redis_impl.rb
274
275
  - lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb
276
+ - lib/ldclient-rb/impl/model/preprocessed_data.rb
275
277
  - lib/ldclient-rb/impl/model/serialization.rb
276
278
  - lib/ldclient-rb/impl/repeating_task.rb
277
279
  - lib/ldclient-rb/impl/store_client_wrapper.rb
@@ -319,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
321
  - !ruby/object:Gem::Version
320
322
  version: '0'
321
323
  requirements: []
322
- rubygems_version: 3.3.17
324
+ rubygems_version: 3.3.22
323
325
  signing_key:
324
326
  specification_version: 4
325
327
  summary: LaunchDarkly SDK for Ruby