From 7e5cc397495fe8c98b84865934d675bd86681077 Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 07:34:23 +0600 Subject: [PATCH 1/6] [AI-FSSDK] [FSSDK-12337] Add Feature Rollout support Add Feature Rollout support to the Ruby SDK. During project config parsing, inject the "everyone else" variation from the flag's rollout into any experiment with type "feature_rollout", enabling correct evaluation without changes to decision logic. - Added config parsing logic to inject the everyone else rollout variation into feature_rollout experiments - Added traffic allocation entry (endOfRange=10000) for injected variation - Added get_everyone_else_variation helper to extract the last rollout rule's first variation - Added 6 unit tests covering injection, variation maps, edge cases, and backward compatibility --- .../config/datafile_project_config.rb | 54 +++ spec/config/datafile_project_config_spec.rb | 347 ++++++++++++++++++ 2 files changed, 401 insertions(+) diff --git a/lib/optimizely/config/datafile_project_config.rb b/lib/optimizely/config/datafile_project_config.rb index cfb67f24..ecd02fd4 100644 --- a/lib/optimizely/config/datafile_project_config.rb +++ b/lib/optimizely/config/datafile_project_config.rb @@ -205,6 +205,28 @@ def initialize(datafile, logger, error_handler) feature_flag['experimentIds'].each do |experiment_id| @experiment_feature_map[experiment_id] = [feature_flag['id']] end + + # Feature Rollout support: inject the "everyone else" variation + # into any experiment with type == "feature_rollout" + everyone_else_variation = get_everyone_else_variation(feature_flag) + next if everyone_else_variation.nil? + + feature_flag['experimentIds'].each do |exp_id| + experiment = @experiment_id_map[exp_id] + next unless experiment && experiment['type'] == 'feature_rollout' + + experiment['variations'].push(everyone_else_variation) + experiment['trafficAllocation'].push( + 'entityId' => everyone_else_variation['id'], + 'endOfRange' => 10_000 + ) + @variation_key_map[experiment['key']][everyone_else_variation['key']] = everyone_else_variation + @variation_id_map[experiment['key']][everyone_else_variation['id']] = everyone_else_variation + @variation_id_map_by_experiment_id[exp_id][everyone_else_variation['id']] = everyone_else_variation + @variation_key_map_by_experiment_id[exp_id][everyone_else_variation['key']] = everyone_else_variation + variation_variables = everyone_else_variation['variables'] + @variation_id_to_variable_usage_map[everyone_else_variation['id']] = generate_key_map(variation_variables, 'id') if variation_variables + end end # Adding Holdout variations in variation id and key maps @@ -690,6 +712,38 @@ def get_holdout(holdout_id) private + def get_everyone_else_variation(feature_flag) + # Get the "everyone else" variation for a feature flag. + # + # The "everyone else" rule is the last experiment in the flag's rollout, + # and its first variation is the "everyone else" variation. + # + # feature_flag - Feature flag hash + # + # Returns the "everyone else" variation hash, or nil if not available. + + rollout_id = feature_flag['rolloutId'] + return nil if rollout_id.nil? || rollout_id.empty? + + rollout = @rollout_id_map[rollout_id] + return nil if rollout.nil? + + experiments = rollout['experiments'] + return nil if experiments.nil? || experiments.empty? + + everyone_else_rule = experiments.last + variations = everyone_else_rule['variations'] + return nil if variations.nil? || variations.empty? + + variation = variations.first + { + 'id' => variation['id'], + 'key' => variation['key'], + 'featureEnabled' => variation['featureEnabled'] == true, + 'variables' => variation.fetch('variables', []) + } + end + def generate_feature_variation_map(feature_flags) flag_variation_map = {} feature_flags.each do |flag| diff --git a/spec/config/datafile_project_config_spec.rb b/spec/config/datafile_project_config_spec.rb index ea47fe6f..a04c1342 100644 --- a/spec/config/datafile_project_config_spec.rb +++ b/spec/config/datafile_project_config_spec.rb @@ -1801,4 +1801,351 @@ end end end + + describe 'Feature Rollout support' do + def build_datafile(experiments: [], rollouts: [], feature_flags: []) + { + 'version' => '4', + 'accountId' => '12001', + 'projectId' => '111001', + 'revision' => '1', + 'experiments' => experiments, + 'events' => [], + 'attributes' => [], + 'audiences' => [], + 'groups' => [], + 'rollouts' => rollouts, + 'featureFlags' => feature_flags + } + end + + it 'should set experiment type to nil when type field is missing' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_ab', + 'key' => 'ab_test_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'var_1', 'endOfRange' => 5000}], + 'variations' => [{'key' => 'var_1', 'id' => 'var_1', 'featureEnabled' => true}] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_ab'], + 'rolloutId' => '', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_ab'] + expect(experiment['type']).to be_nil + end + + it 'should inject everyone else variation into feature_rollout experiments' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'rollout_var', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'rollout_var', 'id' => 'rollout_var', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'rollout_targeted_rule', + 'key' => 'rollout_targeted_rule', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => ['audience_1'], + 'trafficAllocation' => [{'entityId' => 'targeted_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'targeted_var', 'id' => 'targeted_var', 'featureEnabled' => true} + ] + }, + { + 'id' => 'rollout_everyone_else', + 'key' => 'rollout_everyone_else', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'everyone_else_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'everyone_else_var', 'id' => 'everyone_else_var', 'featureEnabled' => false} + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + + # Should now have 2 variations: original + everyone else + expect(experiment['variations'].length).to eq(2) + + variation_ids = experiment['variations'].map { |v| v['id'] } + expect(variation_ids).to include('everyone_else_var') + + # Verify traffic allocation was appended with endOfRange=10000 + expect(experiment['trafficAllocation'].length).to eq(2) + last_allocation = experiment['trafficAllocation'].last + expect(last_allocation['entityId']).to eq('everyone_else_var') + expect(last_allocation['endOfRange']).to eq(10_000) + end + + it 'should update all variation maps after injection' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'rollout_var', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'rollout_var', 'id' => 'rollout_var', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'rollout_everyone_else', + 'key' => 'rollout_everyone_else', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'everyone_else_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'everyone_else_var', 'id' => 'everyone_else_var', 'featureEnabled' => false} + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + + expect(config.variation_key_map['feature_rollout_exp']).to have_key('everyone_else_var') + expect(config.variation_id_map['feature_rollout_exp']).to have_key('everyone_else_var') + expect(config.variation_id_map_by_experiment_id['exp_fr']).to have_key('everyone_else_var') + expect(config.variation_key_map_by_experiment_id['exp_fr']).to have_key('everyone_else_var') + end + + it 'should not modify non-feature_rollout experiments' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_ab', + 'key' => 'ab_test_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'var_1', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'var_1', 'id' => 'var_1', 'featureEnabled' => true} + ], + 'type' => 'a/b' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'rollout_everyone_else', + 'key' => 'rollout_everyone_else', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'everyone_else_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'everyone_else_var', 'id' => 'everyone_else_var', 'featureEnabled' => false} + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_ab'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_ab'] + + expect(experiment['variations'].length).to eq(1) + expect(experiment['trafficAllocation'].length).to eq(1) + end + + it 'should not modify feature_rollout experiment when rolloutId is empty' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'var_1', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'var_1', 'id' => 'var_1', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => '', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + + expect(experiment['variations'].length).to eq(1) + expect(experiment['trafficAllocation'].length).to eq(1) + end + + it 'should use the LAST rollout rule for everyone else variation' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'fr_var', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'fr_var', 'id' => 'fr_var', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'targeted_rule_1', + 'key' => 'targeted_rule_1', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => ['aud_1'], + 'trafficAllocation' => [{'entityId' => 'targeted_var_1', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'targeted_var_1', 'id' => 'targeted_var_1', 'featureEnabled' => true} + ] + }, + { + 'id' => 'targeted_rule_2', + 'key' => 'targeted_rule_2', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => ['aud_2'], + 'trafficAllocation' => [{'entityId' => 'targeted_var_2', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'targeted_var_2', 'id' => 'targeted_var_2', 'featureEnabled' => true} + ] + }, + { + 'id' => 'everyone_else_rule', + 'key' => 'everyone_else_rule', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'correct_everyone_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'correct_everyone_var', 'id' => 'correct_everyone_var', 'featureEnabled' => false} + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + + variation_ids = experiment['variations'].map { |v| v['id'] } + expect(variation_ids).to include('correct_everyone_var') + expect(variation_ids).not_to include('targeted_var_1') + expect(variation_ids).not_to include('targeted_var_2') + end + end end From 9185fef642cc83f7970f0d955d66d8c50dae6b68 Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 07:49:36 +0600 Subject: [PATCH 2/6] [AI-FSSDK] [FSSDK-12337] Add type field to experiment JSON schema Add optional 'type' string field to the experiment properties in the datafile JSON schema validation constants. --- lib/optimizely/helpers/constants.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/optimizely/helpers/constants.rb b/lib/optimizely/helpers/constants.rb index 4334f56d..869f402c 100644 --- a/lib/optimizely/helpers/constants.rb +++ b/lib/optimizely/helpers/constants.rb @@ -205,6 +205,9 @@ module Constants 'cmab' => { 'type' => 'object' }, + 'type' => { + 'type' => 'string' + }, 'holdouts' => { 'type' => 'array' } From 8971420eab9579a58a7d9ba04f36eaa309494407 Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 07:51:59 +0600 Subject: [PATCH 3/6] [AI-FSSDK] [FSSDK-12337] Add test for experiment type field parsing Verify that the type field from the datafile is correctly preserved on experiment hashes after config parsing. --- spec/config/datafile_project_config_spec.rb | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spec/config/datafile_project_config_spec.rb b/spec/config/datafile_project_config_spec.rb index a04c1342..be7f73f2 100644 --- a/spec/config/datafile_project_config_spec.rb +++ b/spec/config/datafile_project_config_spec.rb @@ -1819,6 +1819,37 @@ def build_datafile(experiments: [], rollouts: [], feature_flags: []) } end + it 'should parse experiment type field from datafile' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'var_1', 'endOfRange' => 5000}], + 'variations' => [{'key' => 'var_1', 'id' => 'var_1', 'featureEnabled' => true}], + 'type' => 'feature_rollout' + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => '', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + expect(experiment['type']).to eq('feature_rollout') + end + it 'should set experiment type to nil when type field is missing' do datafile = build_datafile( experiments: [ From deda391486b49c114d5f7ca8b3b24171756803d2 Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 08:20:58 +0600 Subject: [PATCH 4/6] [AI-FSSDK] [FSSDK-12337] Move flag_variation_map after rollout injection Move @flag_variation_map generation to after the feature rollout injection block so the everyone-else variation is included in get_variation_from_flag lookups used by forced decisions. --- lib/optimizely/config/datafile_project_config.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/optimizely/config/datafile_project_config.rb b/lib/optimizely/config/datafile_project_config.rb index ecd02fd4..763e9fa6 100644 --- a/lib/optimizely/config/datafile_project_config.rb +++ b/lib/optimizely/config/datafile_project_config.rb @@ -180,7 +180,6 @@ def initialize(datafile, logger, error_handler) @all_segments.concat Audience.get_segments(audience['conditions']) end - @flag_variation_map = generate_feature_variation_map(@feature_flags) @all_experiments = @experiment_id_map.merge(@rollout_experiment_id_map) @all_experiments.each do |id, exp| variations = exp.fetch('variations') @@ -229,6 +228,9 @@ def initialize(datafile, logger, error_handler) end end + # Generate flag_variation_map after injection so it includes everyone-else variations + @flag_variation_map = generate_feature_variation_map(@feature_flags) + # Adding Holdout variations in variation id and key maps return unless @holdouts && !@holdouts.empty? From 9425dd218d171001d210950a213d029c841a2f2e Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 08:22:23 +0600 Subject: [PATCH 5/6] [AI-FSSDK] [FSSDK-12337] Add tests for featureEnabled and variables propagation Add tests verifying: - Injected everyone-else variation preserves featureEnabled=false - Variables from the rollout variation carry through to the injected variation and populate variation_id_to_variable_usage_map --- spec/config/datafile_project_config_spec.rb | 122 ++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/spec/config/datafile_project_config_spec.rb b/spec/config/datafile_project_config_spec.rb index be7f73f2..506497d3 100644 --- a/spec/config/datafile_project_config_spec.rb +++ b/spec/config/datafile_project_config_spec.rb @@ -2178,5 +2178,127 @@ def build_datafile(experiments: [], rollouts: [], feature_flags: []) expect(variation_ids).not_to include('targeted_var_1') expect(variation_ids).not_to include('targeted_var_2') end + + it 'should preserve featureEnabled value on injected variation' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'rollout_var', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'rollout_var', 'id' => 'rollout_var', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'rollout_everyone_else', + 'key' => 'rollout_everyone_else', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'everyone_else_var', 'endOfRange' => 10_000}], + 'variations' => [ + {'key' => 'everyone_else_var', 'id' => 'everyone_else_var', 'featureEnabled' => false} + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + + injected = experiment['variations'].find { |v| v['id'] == 'everyone_else_var' } + expect(injected).not_to be_nil + expect(injected['featureEnabled']).to eq(false) + end + + it 'should propagate variables from the everyone else variation' do + datafile = build_datafile( + experiments: [ + { + 'id' => 'exp_fr', + 'key' => 'feature_rollout_exp', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'layer_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'rollout_var', 'endOfRange' => 5000}], + 'variations' => [ + {'key' => 'rollout_var', 'id' => 'rollout_var', 'featureEnabled' => true} + ], + 'type' => 'feature_rollout' + } + ], + rollouts: [ + { + 'id' => 'rollout_1', + 'experiments' => [ + { + 'id' => 'rollout_everyone_else', + 'key' => 'rollout_everyone_else', + 'status' => 'Running', + 'forcedVariations' => {}, + 'layerId' => 'rollout_1', + 'audienceIds' => [], + 'trafficAllocation' => [{'entityId' => 'everyone_else_var', 'endOfRange' => 10_000}], + 'variations' => [ + { + 'key' => 'everyone_else_var', + 'id' => 'everyone_else_var', + 'featureEnabled' => false, + 'variables' => [ + {'id' => 'var_1', 'value' => 'default_value'} + ] + } + ] + } + ] + } + ], + feature_flags: [ + { + 'id' => 'flag_1', + 'key' => 'test_flag', + 'experimentIds' => ['exp_fr'], + 'rolloutId' => 'rollout_1', + 'variables' => [] + } + ] + ) + + config = Optimizely::DatafileProjectConfig.new(JSON.dump(datafile), logger, error_handler) + experiment = config.experiment_id_map['exp_fr'] + + injected = experiment['variations'].find { |v| v['id'] == 'everyone_else_var' } + expect(injected).not_to be_nil + expect(injected['variables']).to eq([{'id' => 'var_1', 'value' => 'default_value'}]) + + # Verify variation_id_to_variable_usage_map is populated + variable_usage = config.variation_id_to_variable_usage_map['everyone_else_var'] + expect(variable_usage).not_to be_nil + expect(variable_usage).to have_key('var_1') + end end end From ef4116d55fa70c4876502e135ba06e883dfa0b8c Mon Sep 17 00:00:00 2001 From: FarhanAnjum-opti Date: Thu, 5 Mar 2026 08:29:54 +0600 Subject: [PATCH 6/6] [AI-FSSDK] [FSSDK-12337] Fix pre-existing rubocop Lint/Void offense in spec_params Remove redundant else clause in deep_clone that referenced a variable in void context (Lint/Void: Variable new_obj used in void context). --- spec/spec_params.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/spec_params.rb b/spec/spec_params.rb index 824e2ac7..6d7ff5c3 100644 --- a/spec/spec_params.rb +++ b/spec/spec_params.rb @@ -2062,8 +2062,6 @@ def self.deep_clone(obj) new_obj.map! do |val| deep_clone(val) end - else - new_obj end end end