launchdarkly-server-sdk 6.3.4 → 6.4.0

Sign up to get free protection for your applications and to get access to all the features.
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