optimizely-sdk 5.2.0 → 5.2.1

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: f3e60440f1cc553ee8c1eac5ffc7657b6f4de011a4d53a31674c0f5f68843b68
4
- data.tar.gz: 2e9f30000ddabe5118f32d4afda400f02db039b0e1e8ffca23d0bd96439ef71c
3
+ metadata.gz: f3f7080762c8f6eeb95b64da8d451035e5f162208f7556aed1886dfa5bce0b8c
4
+ data.tar.gz: 046cf325408978dc7c0584d7a6ece61ae4795b843717164080b964a60ccd5a85
5
5
  SHA512:
6
- metadata.gz: b563647ea0496d3c1b65d478c00918729e95b32b74e1260c34b92c2115d791101bfa0f22450b92185172773a8c16ae2cb36854f69792652da259b99499f28acd
7
- data.tar.gz: '038f0fd4bf0280b7d68a33bf7b0bb27f3efe482ef94995bb53751966e9fa7c4b3229fb62a513c2b88458557280b27e339a3222a459649d8b91e837e3e13adb00'
6
+ metadata.gz: ff65b8f753315ac052920dd4c1bfe6f7d46d14742911c2a7cfe21d91d85fc424439724df7445eddf9d7453ca0bbd2a7306669ba255df1f811390c3d1e54d645e
7
+ data.tar.gz: 4ca8a3093958b3061c38648c93494c5e36ab4b31917af95f89dc66ec420f384a4189a7e46467210283ea370a98ab71be22b63d7de90d6bb9fa02d9144ad5f1ea
@@ -115,7 +115,7 @@ module Optimizely
115
115
  @variation_id_to_experiment_map = {}
116
116
  @flag_variation_map = {}
117
117
  @holdout_id_map = {}
118
- @global_holdouts = {}
118
+ @global_holdouts = []
119
119
  @included_holdouts = {}
120
120
  @excluded_holdouts = {}
121
121
  @flag_holdouts_map = {}
@@ -123,23 +123,34 @@ module Optimizely
123
123
  @holdouts.each do |holdout|
124
124
  next unless holdout['status'] == 'Running'
125
125
 
126
+ # Ensure holdout has layerId field (holdouts don't have campaigns)
127
+ holdout['layerId'] ||= ''
128
+
126
129
  @holdout_id_map[holdout['id']] = holdout
127
130
 
128
- if holdout['includedFlags'].nil? || holdout['includedFlags'].empty?
129
- @global_holdouts[holdout['id']] = holdout
131
+ included_flags = holdout['includedFlags'] || []
132
+ excluded_flags = holdout['excludedFlags'] || []
130
133
 
131
- excluded_flags = holdout['excludedFlags']
132
- if excluded_flags && !excluded_flags.empty?
133
- excluded_flags.each do |flag_id|
134
- @excluded_holdouts[flag_id] ||= []
135
- @excluded_holdouts[flag_id] << holdout
136
- end
137
- end
138
- else
139
- holdout['includedFlags'].each do |flag_id|
134
+ case [included_flags.empty?, excluded_flags.empty?]
135
+ when [true, true]
136
+ # No included or excluded flags - this is a global holdout
137
+ @global_holdouts << holdout
138
+
139
+ when [false, true], [false, false]
140
+ # Has included flags - add to included_holdouts map
141
+ included_flags.each do |flag_id|
140
142
  @included_holdouts[flag_id] ||= []
141
143
  @included_holdouts[flag_id] << holdout
142
144
  end
145
+
146
+ when [true, false]
147
+ # No included flags but has excluded flags - global with exclusions
148
+ @global_holdouts << holdout
149
+
150
+ excluded_flags.each do |flag_id|
151
+ @excluded_holdouts[flag_id] ||= []
152
+ @excluded_holdouts[flag_id] << holdout
153
+ end
143
154
  end
144
155
  end
145
156
 
@@ -194,18 +205,6 @@ module Optimizely
194
205
  feature_flag['experimentIds'].each do |experiment_id|
195
206
  @experiment_feature_map[experiment_id] = [feature_flag['id']]
196
207
  end
197
-
198
- flag_id = feature_flag['id']
199
- applicable_holdouts = []
200
-
201
- applicable_holdouts.concat(@included_holdouts[flag_id]) if @included_holdouts[flag_id]
202
-
203
- @global_holdouts.each_value do |holdout|
204
- excluded_flag_ids = holdout['excludedFlags'] || []
205
- applicable_holdouts << holdout unless excluded_flag_ids.include?(flag_id)
206
- end
207
-
208
- @flag_holdouts_map[key] = applicable_holdouts unless applicable_holdouts.empty?
209
208
  end
210
209
 
211
210
  # Adding Holdout variations in variation id and key maps
@@ -645,6 +644,33 @@ module Optimizely
645
644
 
646
645
  return [] if @holdouts.nil? || @holdouts.empty?
647
646
 
647
+ # Check cache first (before validation, so we cache the validation result too)
648
+ return @flag_holdouts_map[flag_id] if @flag_holdouts_map.key?(flag_id)
649
+
650
+ # Validate that the flag exists in the datafile
651
+ flag_exists = @feature_flags.any? { |flag| flag['id'] == flag_id }
652
+ unless flag_exists
653
+ # Cache the empty result for non-existent flags
654
+ @flag_holdouts_map[flag_id] = []
655
+ return []
656
+ end
657
+
658
+ # Prioritize global holdouts first
659
+ excluded = @excluded_holdouts[flag_id] || []
660
+
661
+ active_holdouts = if excluded.any?
662
+ @global_holdouts.reject { |holdout| excluded.include?(holdout) }
663
+ else
664
+ @global_holdouts.dup
665
+ end
666
+
667
+ # Append included holdouts
668
+ included = @included_holdouts[flag_id] || []
669
+ active_holdouts.concat(included)
670
+
671
+ # Cache the result
672
+ @flag_holdouts_map[flag_id] = active_holdouts
673
+
648
674
  @flag_holdouts_map[flag_id] || []
649
675
  end
650
676
 
@@ -214,6 +214,9 @@ module Optimizely
214
214
 
215
215
  return DecisionResult.new(experiment_decision.decision, experiment_decision.error, reasons) if experiment_decision.decision
216
216
 
217
+ # If there's an error (e.g., CMAB error), return immediately without falling back to rollout
218
+ return DecisionResult.new(nil, experiment_decision.error, reasons) if experiment_decision.error
219
+
217
220
  # Check if the feature flag has a rollout and the user is bucketed into that rollout
218
221
  rollout_decision = get_variation_for_feature_rollout(project_config, feature_flag, user_context)
219
222
  reasons.push(*rollout_decision.reasons)
@@ -312,15 +315,8 @@ module Optimizely
312
315
 
313
316
  decisions = []
314
317
  feature_flags.each do |feature_flag|
315
- # check if the feature is being experiment on and whether the user is bucketed into the experiment
316
- decision_result = get_variation_for_feature_experiment(project_config, feature_flag, user_context, user_profile_tracker, decide_options)
317
- # Only process rollout if no experiment decision was found and no error
318
- if decision_result.decision.nil? && !decision_result.error
319
- decision_result_rollout = get_variation_for_feature_rollout(project_config, feature_flag, user_context) unless decision_result.decision
320
- decision_result.decision = decision_result_rollout.decision
321
- decision_result.reasons.push(*decision_result_rollout.reasons)
322
- end
323
- decisions << decision_result
318
+ decision = get_decision_for_flag(feature_flag, user_context, project_config, decide_options, user_profile_tracker)
319
+ decisions << decision
324
320
  end
325
321
  user_profile_tracker&.save_user_profile
326
322
  decisions
@@ -17,5 +17,5 @@
17
17
  #
18
18
  module Optimizely
19
19
  CLIENT_ENGINE = 'ruby-sdk'
20
- VERSION = '5.2.0'
20
+ VERSION = '5.2.1'
21
21
  end
data/lib/optimizely.rb CHANGED
@@ -220,7 +220,7 @@ module Optimizely
220
220
  decision_source = decision.source
221
221
  end
222
222
 
223
- if !decide_options.include?(OptimizelyDecideOption::DISABLE_DECISION_EVENT) && (decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions)
223
+ if !decide_options.include?(OptimizelyDecideOption::DISABLE_DECISION_EVENT) && (decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || decision_source == Optimizely::DecisionService::DECISION_SOURCES['HOLDOUT'] || config.send_flag_decisions)
224
224
  send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes, decision&.cmab_uuid)
225
225
  decision_event_dispatched = true
226
226
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optimizely-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Optimizely
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-13 00:00:00.000000000 Z
11
+ date: 2025-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -216,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
216
  - !ruby/object:Gem::Version
217
217
  version: '0'
218
218
  requirements: []
219
- rubygems_version: 3.4.19
219
+ rubygems_version: 3.4.10
220
220
  signing_key:
221
221
  specification_version: 4
222
222
  summary: Ruby SDK for Optimizely's testing framework