From 8e0376373e3e914f10df190953b5fc107426a6b9 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 2 Jul 2025 09:49:38 -0700 Subject: [PATCH 01/10] Updated engine and sync classes --- .../cache/senders/impressions_formatter.rb | 6 ++- .../engine/common/impressions_manager.rb | 13 +++++-- .../impressions_repository_spec.rb | 38 ++++++++++--------- .../senders/impressions_formatter_spec.rb | 17 ++++++--- spec/cache/senders/impressions_sender_spec.rb | 8 ++-- spec/engine/common/impression_manager_spec.rb | 14 ++++--- 6 files changed, 58 insertions(+), 38 deletions(-) diff --git a/lib/splitclient-rb/cache/senders/impressions_formatter.rb b/lib/splitclient-rb/cache/senders/impressions_formatter.rb index b2f1de95..49e15f70 100644 --- a/lib/splitclient-rb/cache/senders/impressions_formatter.rb +++ b/lib/splitclient-rb/cache/senders/impressions_formatter.rb @@ -44,7 +44,8 @@ def current_impressions(feature_impressions) b: impression[:i][:b], r: impression[:i][:r], c: impression[:i][:c], - pt: impression[:i][:pt] + pt: impression[:i][:pt], + properties: impression[:i][:properties].to_json.to_s } end end @@ -73,7 +74,8 @@ def impression_hash(impression) "#{impression[:i][:b]}:" \ "#{impression[:i][:c]}:" \ "#{impression[:i][:t]}:" \ - "#{impression[:i][:pt]}" + "#{impression[:i][:pt]}" \ + "#{impression[:i][:properties]}" \ end end end diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index adb71c6f..cdc7990e 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -18,15 +18,19 @@ def initialize(config, @unique_keys_tracker = unique_keys_tracker end - def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}) - impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time]) + def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, properties = nil) + impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties) begin if @config.impressions_mode == :none || impressions_disabled @impression_counter.inc(split_name, impression_data[:m]) @unique_keys_tracker.track(split_name, matching_key) elsif @config.impressions_mode == :debug # In DEBUG mode we should calculate the pt only. + return impression(impression_data, params[:attributes]) unless properties.nil? + impression_data[:pt] = @impression_observer.test_and_set(impression_data) else # In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions. + return impression(impression_data, params[:attributes]) unless properties.nil? + impression_data[:pt] = @impression_observer.test_and_set(impression_data) @impression_counter.inc(split_name, impression_data[:m]) unless impression_data[:pt].nil? end @@ -79,7 +83,7 @@ def record_stats(stats) end # added param time for test - def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil) + def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil, properties = nil) { k: matching_key, b: bucketing_key, @@ -88,7 +92,8 @@ def impression_data(matching_key, bucketing_key, split_name, treatment, time = n r: applied_rule(treatment[:label]), c: treatment[:change_number], m: time || (Time.now.to_f * 1000.0).to_i, - pt: nil + pt: nil, + properties: properties } end diff --git a/spec/cache/repositories/impressions_repository_spec.rb b/spec/cache/repositories/impressions_repository_spec.rb index c422d34c..4b815e34 100644 --- a/spec/cache/repositories/impressions_repository_spec.rb +++ b/spec/cache/repositories/impressions_repository_spec.rb @@ -25,7 +25,8 @@ r: 'sample_rule', c: 1_533_177_602_748, m: 1_478_113_516_002, - pt: nil + pt: nil, + properties: nil }, attributes: {} }, @@ -39,7 +40,8 @@ r: 'sample_rule', c: 1_533_177_602_749, m: 1_478_113_516_002, - pt: nil + pt: nil, + properties: nil }, attributes: {} } @@ -55,8 +57,8 @@ it 'adds impressions' do params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :bar, treatment2, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :bar, treatment2, false, params, nil), :disabled => false } impressions_manager.track(impressions) expect(repository.batch).to match_array(result) @@ -67,8 +69,8 @@ it 'adds impressions in bulk' do params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :bar, treatment2, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :bar, treatment2, false, params, nil), :disabled => false } impressions_manager.track(impressions) expect(repository.batch).to match_array(result) @@ -80,7 +82,7 @@ config.labels_enabled = false params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params, nil), :disabled => false } impressions_manager.track(impressions) expect(repository.batch.first[:i][:r]).to be_nil @@ -89,8 +91,8 @@ it 'bulk size less than the actual queue' do params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment2, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment1, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo, treatment2, false, params, nil), :disabled => false } impressions_manager.track(impressions) config.impressions_bulk_size = 1 @@ -142,8 +144,8 @@ treatment = { treatment: 'on', label: 'sample_rule', change_number: 1_533_177_602_748 } params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo1, treatment, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key2', nil, :foo1, treatment, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key1', nil, :foo1, treatment, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key2', nil, :foo1, treatment, false, params, nil), :disabled => false } impressions_manager.track(impressions) expect(repository.batch.size).to eq(1) @@ -200,8 +202,8 @@ expect(config.impressions_adapter).to receive(:expire).once.with(anything, 3600) params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params, nil), :disabled => false } impressions_manager.track(impressions) end @@ -211,7 +213,7 @@ params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params, nil), :disabled => false } impressions_manager.track(impressions) expect(repository.batch).to eq([]) @@ -221,8 +223,8 @@ other_treatment = { treatment: 'on', label: 'sample_rule_2', change_number: 1_533_177_602_748 } params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo2, other_treatment, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params, nil), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key', nil, :foo2, other_treatment, false, params, nil), :disabled => false } impressions_manager.track(impressions) adapter.get_from_queue('SPLITIO.impressions', 0).map do |e| @@ -252,8 +254,8 @@ params = { attributes: {}, time: 1_478_113_516_002 } impressions = [] - impressions << { :impression => custom_impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params), :disabled => false } - impressions << { :impression => custom_impressions_manager.build_impression('matching_key', nil, :foo2, other_treatment, false, params), :disabled => false } + impressions << { :impression => custom_impressions_manager.build_impression('matching_key', nil, :foo1, treatment, false, params, nil), :disabled => false } + impressions << { :impression => custom_impressions_manager.build_impression('matching_key', nil, :foo2, other_treatment, false, params, nil), :disabled => false } custom_impressions_manager.track(impressions) custom_adapter.get_from_queue('SPLITIO.impressions', 0).map do |e| diff --git a/spec/cache/senders/impressions_formatter_spec.rb b/spec/cache/senders/impressions_formatter_spec.rb index e55228ff..272c0455 100644 --- a/spec/cache/senders/impressions_formatter_spec.rb +++ b/spec/cache/senders/impressions_formatter_spec.rb @@ -58,7 +58,8 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil }] + pt: nil, + properties: "null" }] }, { f: :foo2, @@ -68,14 +69,15 @@ b: 'foo2', r: 'custom_label2', c: 123_499, - pt: nil }] + pt: nil, + properties: "null" }] }]) end it 'formats multiple impressions for one key' do params = { attributes: {}, time: 1_478_113_518_900 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params, {"prop": "val"}), :disabled => false } impressions_manager.track(impressions) expect(formatted_impressions.find { |i| i[:f] == :foo1 }[:i]).to match_array( @@ -87,7 +89,8 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil + pt: nil, + properties: 'null' } ] ) @@ -101,7 +104,8 @@ b: 'foo2', r: 'custom_label2', c: 123_499, - pt: nil + pt: nil, + properties: "null" }, { k: 'matching_key3', @@ -110,7 +114,8 @@ b: nil, r: nil, c: nil, - pt: nil + pt: nil, + properties: '{"prop":"val"}' } ] ) diff --git a/spec/cache/senders/impressions_sender_spec.rb b/spec/cache/senders/impressions_sender_spec.rb index 96da8aa9..39011264 100644 --- a/spec/cache/senders/impressions_sender_spec.rb +++ b/spec/cache/senders/impressions_sender_spec.rb @@ -46,7 +46,7 @@ params2 = { attributes: {}, time: 1_478_113_518_285 } impressions = [] impressions << { :impression => impressions_manager.build_impression('matching_key', 'foo1', 'foo1', treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2, {:prop=>"val"}), :disabled => false } impressions_manager.track(impressions) end @@ -75,7 +75,8 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil + pt: nil, + properties: "null" } ] }, @@ -89,7 +90,8 @@ b: 'foo2', r: 'custom_label2', c: 123_499, - pt: nil + pt: nil, + properties: '{"prop":"val"}' } ] } diff --git a/spec/engine/common/impression_manager_spec.rb b/spec/engine/common/impression_manager_spec.rb index 21875236..ea93d3ac 100644 --- a/spec/engine/common/impression_manager_spec.rb +++ b/spec/engine/common/impression_manager_spec.rb @@ -59,7 +59,8 @@ r: 'default label', c: 1_478_113_516_002, m: 1_478_113_516_222, - pt: nil + pt: nil, + properties: {"prop":"val"} }, attributes: {} } @@ -71,7 +72,7 @@ 'split_name_test', treatment, false, - params) + params, {"prop":"val"}) expect(impression).to match(expected) result_count = impression_counter.pop_all @@ -97,7 +98,8 @@ r: 'default label', c: 1_478_113_516_002, m: 1_478_113_516_222, - pt: nil + pt: nil, + properties: nil }, attributes: {} } @@ -155,7 +157,8 @@ r: 'default label', c: 1_478_113_516_002, m: 1_478_113_516_222, - pt: nil + pt: nil, + properties: nil }, attributes: {} } @@ -297,7 +300,8 @@ r: 'default label', c: 1_478_113_516_002, m: 1_478_113_516_222, - pt: nil + pt: nil, + properties: nil }, attributes: {} } From f8a9d9357af82a6ade2f96b4b8bcf999a6c3f151 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 2 Jul 2025 11:56:59 -0700 Subject: [PATCH 02/10] Removed properties if nil in sender --- .../cache/senders/impressions_formatter.rb | 33 +++++++++++++------ .../engine/common/impressions_manager.rb | 6 ++-- .../senders/impressions_formatter_spec.rb | 12 +++---- spec/cache/senders/impressions_sender_spec.rb | 3 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/lib/splitclient-rb/cache/senders/impressions_formatter.rb b/lib/splitclient-rb/cache/senders/impressions_formatter.rb index 49e15f70..eed8570e 100644 --- a/lib/splitclient-rb/cache/senders/impressions_formatter.rb +++ b/lib/splitclient-rb/cache/senders/impressions_formatter.rb @@ -36,17 +36,30 @@ def feature_impressions(filtered_impressions, feature) end def current_impressions(feature_impressions) + formatted = [] feature_impressions.map do |impression| - { - k: impression[:i][:k], - t: impression[:i][:t], - m: impression[:i][:m], - b: impression[:i][:b], - r: impression[:i][:r], - c: impression[:i][:c], - pt: impression[:i][:pt], - properties: impression[:i][:properties].to_json.to_s - } + if impression[:i][:properties].nil? + impression = { + k: impression[:i][:k], + t: impression[:i][:t], + m: impression[:i][:m], + b: impression[:i][:b], + r: impression[:i][:r], + c: impression[:i][:c], + pt: impression[:i][:pt] + } + else + impression = { + k: impression[:i][:k], + t: impression[:i][:t], + m: impression[:i][:m], + b: impression[:i][:b], + r: impression[:i][:r], + c: impression[:i][:c], + pt: impression[:i][:pt], + properties: impression[:i][:properties].to_json.to_s + } + end end end diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index cdc7990e..46efc3b2 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -18,7 +18,8 @@ def initialize(config, @unique_keys_tracker = unique_keys_tracker end - def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, properties = nil) + def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, + properties = nil) impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties) begin if @config.impressions_mode == :none || impressions_disabled @@ -83,7 +84,8 @@ def record_stats(stats) end # added param time for test - def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil, properties = nil) + def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil, + properties = nil) { k: matching_key, b: bucketing_key, diff --git a/spec/cache/senders/impressions_formatter_spec.rb b/spec/cache/senders/impressions_formatter_spec.rb index 272c0455..965665be 100644 --- a/spec/cache/senders/impressions_formatter_spec.rb +++ b/spec/cache/senders/impressions_formatter_spec.rb @@ -58,8 +58,7 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil, - properties: "null" }] + pt: nil}] }, { f: :foo2, @@ -69,8 +68,7 @@ b: 'foo2', r: 'custom_label2', c: 123_499, - pt: nil, - properties: "null" }] + pt: nil}] }]) end @@ -89,8 +87,7 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil, - properties: 'null' + pt: nil } ] ) @@ -104,8 +101,7 @@ b: 'foo2', r: 'custom_label2', c: 123_499, - pt: nil, - properties: "null" + pt: nil }, { k: 'matching_key3', diff --git a/spec/cache/senders/impressions_sender_spec.rb b/spec/cache/senders/impressions_sender_spec.rb index 39011264..219b789f 100644 --- a/spec/cache/senders/impressions_sender_spec.rb +++ b/spec/cache/senders/impressions_sender_spec.rb @@ -75,8 +75,7 @@ b: 'foo1', r: 'custom_label1', c: 123_456, - pt: nil, - properties: "null" + pt: nil } ] }, From 4bc21d50208dba3ec540b9cbb9db16fff40056db Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 3 Jul 2025 15:05:34 -0700 Subject: [PATCH 03/10] Update client --- lib/splitclient-rb/clients/split_client.rb | 86 ++++++++++++------- .../engine/common/impressions_manager.rb | 3 +- spec/engine/common/impression_manager_spec.rb | 2 +- spec/splitclient/split_client_spec.rb | 22 +++++ 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index ed0e1021..69f7e906 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -35,23 +35,23 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage end def get_treatment( - key, split_name, attributes = {}, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) - result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple) + result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, options) return result.tap { |t| t.delete(:config) } if multiple result[:treatment] end def get_treatment_with_config( - key, split_name, attributes = {}, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) - treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple) + treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, options) end - def get_treatments(key, split_names, attributes = {}) - treatments = treatments(key, split_names, attributes) + def get_treatments(key, split_names, attributes = {}, options = nil) + treatments = treatments(key, split_names, attributes, options) return treatments if treatments.nil? keys = treatments.keys @@ -59,40 +59,40 @@ def get_treatments(key, split_names, attributes = {}) Hash[keys.zip(treats)] end - def get_treatments_with_config(key, split_names, attributes = {}) - treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG) + def get_treatments_with_config(key, split_names, attributes = {}, options = nil) + treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG) end - def get_treatments_by_flag_set(key, flag_set, attributes = {}) + def get_treatments_by_flag_set(key, flag_set, attributes = {}, options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SET, [flag_set]) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SET) + treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SET) return treatments if treatments.nil? keys = treatments.keys treats = treatments.map { |_,t| t[:treatment] } Hash[keys.zip(treats)] end - def get_treatments_by_flag_sets(key, flag_sets, attributes = {}) + def get_treatments_by_flag_sets(key, flag_sets, attributes = {}, options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SETS, flag_sets) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments = treatments(key, split_names, attributes, GET_TREATMENTS_BY_FLAG_SETS) + treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SETS) return treatments if treatments.nil? keys = treatments.keys treats = treatments.map { |_,t| t[:treatment] } Hash[keys.zip(treats)] end - def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}) + def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}, options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [flag_set]) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET) + treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET) end - def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}) + def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}, options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, flag_sets) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments(key, split_names, attributes, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) + treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) end def destroy @@ -135,10 +135,7 @@ def track(key, traffic_type_name, event_type, value = nil, properties = nil) if !properties.nil? properties, size = validate_properties(properties) properties_size += size - if (properties_size > EVENTS_SIZE_THRESHOLD) - @config.logger.error("The maximum size allowed for the properties is #{EVENTS_SIZE_THRESHOLD}. Current is #{properties_size}. Event not queued") - return false - end + return false unless check_properties_size(properties_size) end if ready? && !@config.localhost_mode && !@splits_repository.traffic_type_exists(traffic_type_name) @@ -163,6 +160,14 @@ def block_until_ready(time = nil) private + def check_properties_size(properties_size, msg = "Event not queued") + if (properties_size > EVENTS_SIZE_THRESHOLD) + @config.logger.error("The maximum size allowed for the properties is #{EVENTS_SIZE_THRESHOLD}. Current is #{properties_size}. #{msg}") + return false + end + return true + end + def keys_from_key(key) case key when Hash @@ -206,7 +211,7 @@ def sanitize_split_names(calling_method, split_names) end end - def validate_properties(properties) + def validate_properties(properties, method = 'Event') properties_count = 0 size = 0 @@ -225,11 +230,25 @@ def validate_properties(properties) end } - @config.logger.warn('Event has more than 300 properties. Some of them will be trimmed when processed') if properties_count > 300 + @config.logger.warn("#{method} has more than 300 properties. Some of them will be trimmed when processed") if properties_count > 300 return fixed_properties, size end + def validate_options(options) + if !options.is_a?(Hash) + @config.logger.warn("Option #{options} should be a hash type. Setting value to nil") + return nil, 0 + end + options = options.transform_keys(&:to_sym) + if !options.key?(:properties) + @config.logger.warn("Option #{options} hash does not contain properties key. Setting value to nil") + return nil, 0 + end + options[:properties], size = validate_properties(options[:properties], method = 'Treatment') + return options, size + end + def valid_client if @destroyed @config.logger.error('Client has already been destroyed - no calls possible') @@ -238,8 +257,7 @@ def valid_client @config.valid_mode end - def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_treatments') - attributes = {} if attributes.nil? + def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_method = 'get_treatments') sanitized_feature_flag_names = sanitize_split_names(calling_method, feature_flag_names) if sanitized_feature_flag_names.nil? @@ -269,7 +287,9 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t to_return = Hash.new sanitized_feature_flag_names.each {|name| to_return[name.to_sym] = control_treatment_with_config - impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }), :disabled => false } + impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, + control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }, + options), :disabled => false } } @impressions_manager.track(impressions) return to_return @@ -291,7 +311,7 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t invalid_treatments[key] = control_treatment_with_config next end - treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method) + treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, options) treatments[key] = { treatment: treatments_labels_change_numbers[:treatment], @@ -314,7 +334,7 @@ def treatments(key, feature_flag_names, attributes = {}, calling_method = 'get_t # @param store_impressions [Boolean] impressions aren't stored if this flag is false # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true, - calling_method = 'get_treatment', multiple = false) + calling_method = 'get_treatment', multiple = false, options = nil) impressions = [] bucketing_key, matching_key = keys_from_key(key) @@ -332,13 +352,13 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i end feature_flag = @splits_repository.get_split(feature_flag_name) - treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple) + treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple, options) @impressions_manager.track(impressions_decorator) unless impressions_decorator.nil? treatments end - def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false) + def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, options = nil) impressions_decorator = [] begin start = Time.now @@ -359,18 +379,20 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ impressions_disabled = false end + options, size = validate_options(options) + options[:properties] = nil unless options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored") + record_latency(calling_method, start) - impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }), :disabled => impressions_disabled } + impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }, options), :disabled => impressions_disabled } impressions_decorator << impression_decorator unless impression_decorator.nil? rescue StandardError => e @config.log_found_exception(__method__.to_s, e) record_exception(calling_method) - impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }), :disabled => false } + impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }, options), :disabled => false } impressions_decorator << impression_decorator unless impression_decorator.nil? return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::EXCEPTION }), multiple), impressions_decorator end - return parsed_treatment(treatment_data, multiple), impressions_decorator end diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index 46efc3b2..a2b196cc 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -19,7 +19,8 @@ def initialize(config, end def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, - properties = nil) + options = nil) + properties = options.nil? ? nil : options[:properties] impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties) begin if @config.impressions_mode == :none || impressions_disabled diff --git a/spec/engine/common/impression_manager_spec.rb b/spec/engine/common/impression_manager_spec.rb index ea93d3ac..c23167c5 100644 --- a/spec/engine/common/impression_manager_spec.rb +++ b/spec/engine/common/impression_manager_spec.rb @@ -72,7 +72,7 @@ 'split_name_test', treatment, false, - params, {"prop":"val"}) + params, {"properties": {"prop":"val"}}) expect(impression).to match(expected) result_count = impression_counter.pop_all diff --git a/spec/splitclient/split_client_spec.rb b/spec/splitclient/split_client_spec.rb index fd19dc0c..9ff88c25 100644 --- a/spec/splitclient/split_client_spec.rb +++ b/spec/splitclient/split_client_spec.rb @@ -35,6 +35,24 @@ expect(split_client.get_treatments_by_flag_sets('key', ['set_2'])).to eq({:testing222 => 'off'}) expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1')).to eq({:testing222 => {:treatment => 'off', :config => nil}}) expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'])).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + imps = impressions_repository.batch + + expect(split_client.get_treatment('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq('off') + check_properties(impressions_repository.batch) + expect(split_client.get_treatments('key_prop', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatment_with_config('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq({:treatment => 'off', :config => nil}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatments_with_config('key', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatments_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatments_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + check_properties(impressions_repository.batch) + expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + check_properties(impressions_repository.batch) end it 'check track' do @@ -221,3 +239,7 @@ def mock_segment_changes(segment_name, segment_json, since) stub_request(:get, "https://sdk.split.io/api/segmentChanges/#{segment_name}?since=#{since}") .to_return(status: 200, body: segment_json) end + +def check_properties(imps) + expect(imps[0][:i][:properties]).to eq({:prop => "value"}) +end From 35fd494992ad918df4f51c00eb1a6000da4167fa Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Mon, 7 Jul 2025 12:33:01 -0700 Subject: [PATCH 04/10] Updated integration tests --- lib/splitclient-rb/clients/split_client.rb | 3 +- .../senders/impressions_formatter_spec.rb | 2 +- spec/cache/senders/impressions_sender_spec.rb | 2 +- spec/engine_spec.rb | 63 +++++++++++++++++-- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 69f7e906..4e6e17a7 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -273,6 +273,7 @@ def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_ bucketing_key, matching_key = keys_from_key(key) bucketing_key = bucketing_key ? bucketing_key.to_s : nil matching_key = matching_key ? matching_key.to_s : nil + attributes = parsed_attributes(attributes) if !@config.split_validator.valid_get_treatments_parameters(calling_method, key, sanitized_feature_flag_names, matching_key, bucketing_key, attributes) to_return = Hash.new @@ -379,7 +380,7 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ impressions_disabled = false end - options, size = validate_options(options) + options, size = validate_options(options) unless options.nil? options[:properties] = nil unless options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored") record_latency(calling_method, start) diff --git a/spec/cache/senders/impressions_formatter_spec.rb b/spec/cache/senders/impressions_formatter_spec.rb index 965665be..4e85dfeb 100644 --- a/spec/cache/senders/impressions_formatter_spec.rb +++ b/spec/cache/senders/impressions_formatter_spec.rb @@ -75,7 +75,7 @@ it 'formats multiple impressions for one key' do params = { attributes: {}, time: 1_478_113_518_900 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params, {"prop": "val"}), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params, {:properties => {:prop => "val"}}), :disabled => false } impressions_manager.track(impressions) expect(formatted_impressions.find { |i| i[:f] == :foo1 }[:i]).to match_array( diff --git a/spec/cache/senders/impressions_sender_spec.rb b/spec/cache/senders/impressions_sender_spec.rb index 219b789f..975d1a8b 100644 --- a/spec/cache/senders/impressions_sender_spec.rb +++ b/spec/cache/senders/impressions_sender_spec.rb @@ -46,7 +46,7 @@ params2 = { attributes: {}, time: 1_478_113_518_285 } impressions = [] impressions << { :impression => impressions_manager.build_impression('matching_key', 'foo1', 'foo1', treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2, {:prop=>"val"}), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2, {:properties => {:prop => "val"}}), :disabled => false } impressions_manager.track(impressions) end diff --git a/spec/engine_spec.rb b/spec/engine_spec.rb index 7909d64d..3501468c 100644 --- a/spec/engine_spec.rb +++ b/spec/engine_spec.rb @@ -77,6 +77,22 @@ treatment: 'off', config: nil ) + expect(subject.get_treatment_with_config('nicolas', 'mauro_test', {}, nil)).to eq( + treatment: 'off', + config: nil + ) + expect(subject.get_treatment_with_config('nicolas', 'mauro_test', {}, {})).to eq( + treatment: 'off', + config: nil + ) + expect(subject.get_treatment_with_config('nicolas', 'mauro_test', {}, "prop")).to eq( + treatment: 'off', + config: nil + ) + expect(subject.get_treatment_with_config('nicolas', 'mauro_test', {}, 123)).to eq( + treatment: 'off', + config: nil + ) close_redis end @@ -84,6 +100,10 @@ expect(subject.get_treatment('nicolas', 'mauro_test', nil)).to eq 'off' expect(subject.get_treatment('nicolas', 'mauro_test')).to eq 'off' expect(subject.get_treatment('nicolas', 'mauro_test', {})).to eq 'off' + expect(subject.get_treatment('nicolas', 'mauro_test', {}, nil)).to eq 'off' + expect(subject.get_treatment('nicolas', 'mauro_test', {}, {})).to eq 'off' + expect(subject.get_treatment('nicolas', 'mauro_test', {}, "prop")).to eq 'off' + expect(subject.get_treatment('nicolas', 'mauro_test', {}, 123)).to eq 'off' end it 'get_treatments returns off' do @@ -96,6 +116,18 @@ expect(subject.get_treatments('nicolas', ['mauro_test'], {})).to eq( mauro_test: 'off' ) + expect(subject.get_treatments('nicolas', ['mauro_test'], {}, nil)).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments('nicolas', ['mauro_test'], {}, {})).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments('nicolas', ['mauro_test'], {}, "prop")).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments('nicolas', ['mauro_test'], {}, 123)).to eq( + mauro_test: 'off' + ) close_redis end @@ -109,6 +141,18 @@ expect(subject.get_treatments_by_flag_set('nicolas', 'set_2', {})).to eq( mauro_test: 'off' ) + expect(subject.get_treatments_by_flag_set('nicolas', 'set_2', {}, {})).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_set('nicolas', 'set_2', {}, nil)).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_set('nicolas', 'set_2', {}, "prop")).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_set('nicolas', 'set_2', {}, 123)).to eq( + mauro_test: 'off' + ) close_redis end @@ -122,6 +166,18 @@ expect(subject.get_treatments_by_flag_sets('nicolas', ['set_2'], {})).to eq( mauro_test: 'off' ) + expect(subject.get_treatments_by_flag_sets('nicolas', ['set_2'], {}, {})).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_sets('nicolas', ['set_2'], {}, nil)).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_sets('nicolas', ['set_2'], {}, "prop")).to eq( + mauro_test: 'off' + ) + expect(subject.get_treatments_by_flag_sets('nicolas', ['set_2'], {}, 123)).to eq( + mauro_test: 'off' + ) close_redis end end @@ -141,8 +197,7 @@ end it 'returns CONTROL and label for incorrect feature name' do - treatment = subject.get_treatment('random_user_id', 'test_featur', nil, nil, false, true) - puts treatment + treatment = subject.get_treatment('random_user_id', 'test_featur', nil, nil, nil, false, true) expect(treatment).to eq( treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: SplitIoClient::Engine::Models::Label::NOT_FOUND, @@ -229,7 +284,7 @@ #TODO We will remove multiple param in the future. it 'returns CONTROL and label on nil key' do - expect(subject.get_treatment(nil, 'test_feature', nil, nil, false, true)).to eq( + expect(subject.get_treatment(nil, 'test_feature', nil, nil, nil, false, true)).to eq( treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: nil, change_number: nil @@ -289,7 +344,7 @@ #TODO We will remove multiple param in the future. it 'returns CONTROL and label on nil split_name' do - expect(subject.get_treatment('random_user_id', nil, nil, nil, false, true)).to eq( + expect(subject.get_treatment('random_user_id', nil, nil, nil, nil, false, true)).to eq( treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: nil, change_number: nil From d977a91a6a834764f82e57620d5c7738a4dbdc04 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 10 Jul 2025 14:27:59 -0700 Subject: [PATCH 05/10] Created EvaluationOptions class --- lib/splitclient-rb.rb | 1 + lib/splitclient-rb/clients/split_client.rb | 68 +++++++++---------- .../engine/common/impressions_manager.rb | 4 +- .../engine/models/evaluation_options.rb | 9 +++ .../senders/impressions_formatter_spec.rb | 2 +- spec/cache/senders/impressions_sender_spec.rb | 2 +- spec/engine/common/impression_manager_spec.rb | 2 +- spec/splitclient/split_client_spec.rb | 16 ++--- 8 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 lib/splitclient-rb/engine/models/evaluation_options.rb diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index b4ffcdeb..30e0a2b3 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -109,6 +109,7 @@ require 'splitclient-rb/engine/models/segment_type' require 'splitclient-rb/engine/models/treatment' require 'splitclient-rb/engine/models/split_http_response' +require 'splitclient-rb/engine/models/evaluation_options' require 'splitclient-rb/engine/auth_api_client' require 'splitclient-rb/engine/back_off' require 'splitclient-rb/engine/push_manager' diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 4e6e17a7..523c5d0d 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -35,23 +35,23 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage end def get_treatment( - key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) - result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, options) + result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, evaluation_options) return result.tap { |t| t.delete(:config) } if multiple result[:treatment] end def get_treatment_with_config( - key, split_name, attributes = {}, options = nil, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) - treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, options) + treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, evaluation_options) end - def get_treatments(key, split_names, attributes = {}, options = nil) - treatments = treatments(key, split_names, attributes, options) + def get_treatments(key, split_names, attributes = {}, evaluation_options = nil) + treatments = treatments(key, split_names, attributes, evaluation_options) return treatments if treatments.nil? keys = treatments.keys @@ -59,40 +59,40 @@ def get_treatments(key, split_names, attributes = {}, options = nil) Hash[keys.zip(treats)] end - def get_treatments_with_config(key, split_names, attributes = {}, options = nil) - treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG) + def get_treatments_with_config(key, split_names, attributes = {}, evaluation_options = nil) + treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG) end - def get_treatments_by_flag_set(key, flag_set, attributes = {}, options = nil) + def get_treatments_by_flag_set(key, flag_set, attributes = {}, evaluation_options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SET, [flag_set]) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SET) + treatments = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_BY_FLAG_SET) return treatments if treatments.nil? keys = treatments.keys treats = treatments.map { |_,t| t[:treatment] } Hash[keys.zip(treats)] end - def get_treatments_by_flag_sets(key, flag_sets, attributes = {}, options = nil) + def get_treatments_by_flag_sets(key, flag_sets, attributes = {}, evaluation_options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SETS, flag_sets) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments = treatments(key, split_names, attributes, options, GET_TREATMENTS_BY_FLAG_SETS) + treatments = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_BY_FLAG_SETS) return treatments if treatments.nil? keys = treatments.keys treats = treatments.map { |_,t| t[:treatment] } Hash[keys.zip(treats)] end - def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}, options = nil) + def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}, evaluation_options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [flag_set]) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET) + treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET) end - def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}, options = nil) + def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}, evaluation_options = nil) valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, flag_sets) split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set) - treatments(key, split_names, attributes, options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) + treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) end def destroy @@ -235,18 +235,14 @@ def validate_properties(properties, method = 'Event') return fixed_properties, size end - def validate_options(options) - if !options.is_a?(Hash) - @config.logger.warn("Option #{options} should be a hash type. Setting value to nil") + def validate_evaluation_options(evaluation_options) + if !evaluation_options.is_a?(SplitIoClient::Engine::Models::EvaluationOptions) + @config.logger.warn("Option #{evaluation_options} should be a EvaluationOptions type. Setting value to nil") return nil, 0 end - options = options.transform_keys(&:to_sym) - if !options.key?(:properties) - @config.logger.warn("Option #{options} hash does not contain properties key. Setting value to nil") - return nil, 0 - end - options[:properties], size = validate_properties(options[:properties], method = 'Treatment') - return options, size + evaluation_options.properties = evaluation_options.properties.transform_keys(&:to_sym) + evaluation_options.properties, size = validate_properties(evaluation_options.properties, method = 'Treatment') + return evaluation_options, size end def valid_client @@ -257,7 +253,7 @@ def valid_client @config.valid_mode end - def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_method = 'get_treatments') + def treatments(key, feature_flag_names, attributes = {}, evaluation_options = nil, calling_method = 'get_treatments') sanitized_feature_flag_names = sanitize_split_names(calling_method, feature_flag_names) if sanitized_feature_flag_names.nil? @@ -290,7 +286,7 @@ def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_ to_return[name.to_sym] = control_treatment_with_config impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym, control_treatment_with_config.merge({ :label => Engine::Models::Label::NOT_READY }), false, { attributes: attributes, time: nil }, - options), :disabled => false } + evaluation_options), :disabled => false } } @impressions_manager.track(impressions) return to_return @@ -312,7 +308,7 @@ def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_ invalid_treatments[key] = control_treatment_with_config next end - treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, options) + treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, evaluation_options) treatments[key] = { treatment: treatments_labels_change_numbers[:treatment], @@ -335,7 +331,7 @@ def treatments(key, feature_flag_names, attributes = {}, options = nil, calling_ # @param store_impressions [Boolean] impressions aren't stored if this flag is false # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true, - calling_method = 'get_treatment', multiple = false, options = nil) + calling_method = 'get_treatment', multiple = false, evaluation_options = nil) impressions = [] bucketing_key, matching_key = keys_from_key(key) @@ -353,13 +349,13 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i end feature_flag = @splits_repository.get_split(feature_flag_name) - treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple, options) + treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple, evaluation_options) @impressions_manager.track(impressions_decorator) unless impressions_decorator.nil? treatments end - def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, options = nil) + def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, evaluation_options = nil) impressions_decorator = [] begin start = Time.now @@ -380,16 +376,16 @@ def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_ impressions_disabled = false end - options, size = validate_options(options) unless options.nil? - options[:properties] = nil unless options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored") + evaluation_options, size = validate_evaluation_options(evaluation_options) unless evaluation_options.nil? + evaluation_options.properties = nil unless evaluation_options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored") record_latency(calling_method, start) - impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }, options), :disabled => impressions_disabled } + impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }, evaluation_options), :disabled => impressions_disabled } impressions_decorator << impression_decorator unless impression_decorator.nil? rescue StandardError => e @config.log_found_exception(__method__.to_s, e) record_exception(calling_method) - impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }, options), :disabled => false } + impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, control_treatment, false, { attributes: attributes, time: nil }, evaluation_options), :disabled => false } impressions_decorator << impression_decorator unless impression_decorator.nil? return parsed_treatment(control_treatment.merge({ :label => Engine::Models::Label::EXCEPTION }), multiple), impressions_decorator diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index a2b196cc..170b9f7f 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -19,8 +19,8 @@ def initialize(config, end def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, - options = nil) - properties = options.nil? ? nil : options[:properties] + evaluation_options = nil) + properties = evaluation_options.nil? ? nil : evaluation_options.properties impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties) begin if @config.impressions_mode == :none || impressions_disabled diff --git a/lib/splitclient-rb/engine/models/evaluation_options.rb b/lib/splitclient-rb/engine/models/evaluation_options.rb new file mode 100644 index 00000000..c79c710a --- /dev/null +++ b/lib/splitclient-rb/engine/models/evaluation_options.rb @@ -0,0 +1,9 @@ +module SplitIoClient::Engine::Models + class EvaluationOptions + attr_accessor :properties + + def initialize(properties) + @properties = properties + end + end +end diff --git a/spec/cache/senders/impressions_formatter_spec.rb b/spec/cache/senders/impressions_formatter_spec.rb index 4e85dfeb..6c649a48 100644 --- a/spec/cache/senders/impressions_formatter_spec.rb +++ b/spec/cache/senders/impressions_formatter_spec.rb @@ -75,7 +75,7 @@ it 'formats multiple impressions for one key' do params = { attributes: {}, time: 1_478_113_518_900 } impressions = [] - impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params, {:properties => {:prop => "val"}}), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key3', nil, 'foo2', treatment3, false, params, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "val"})), :disabled => false } impressions_manager.track(impressions) expect(formatted_impressions.find { |i| i[:f] == :foo1 }[:i]).to match_array( diff --git a/spec/cache/senders/impressions_sender_spec.rb b/spec/cache/senders/impressions_sender_spec.rb index 975d1a8b..ccb5b885 100644 --- a/spec/cache/senders/impressions_sender_spec.rb +++ b/spec/cache/senders/impressions_sender_spec.rb @@ -46,7 +46,7 @@ params2 = { attributes: {}, time: 1_478_113_518_285 } impressions = [] impressions << { :impression => impressions_manager.build_impression('matching_key', 'foo1', 'foo1', treatment1, false, params), :disabled => false } - impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2, {:properties => {:prop => "val"}}), :disabled => false } + impressions << { :impression => impressions_manager.build_impression('matching_key2', 'foo2', 'foo2', treatment2, false, params2, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "val"})), :disabled => false } impressions_manager.track(impressions) end diff --git a/spec/engine/common/impression_manager_spec.rb b/spec/engine/common/impression_manager_spec.rb index c23167c5..02aab198 100644 --- a/spec/engine/common/impression_manager_spec.rb +++ b/spec/engine/common/impression_manager_spec.rb @@ -72,7 +72,7 @@ 'split_name_test', treatment, false, - params, {"properties": {"prop":"val"}}) + params, SplitIoClient::Engine::Models::EvaluationOptions.new({"prop":"val"})) expect(impression).to match(expected) result_count = impression_counter.pop_all diff --git a/spec/splitclient/split_client_spec.rb b/spec/splitclient/split_client_spec.rb index 9ff88c25..6c49fffd 100644 --- a/spec/splitclient/split_client_spec.rb +++ b/spec/splitclient/split_client_spec.rb @@ -37,21 +37,21 @@ expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'])).to eq({:testing222 => {:treatment => 'off', :config => nil}}) imps = impressions_repository.batch - expect(split_client.get_treatment('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq('off') + expect(split_client.get_treatment('key', 'testing222', {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq('off') check_properties(impressions_repository.batch) - expect(split_client.get_treatments('key_prop', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + expect(split_client.get_treatments('key_prop', ['testing222'], {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => 'off'}) check_properties(impressions_repository.batch) - expect(split_client.get_treatment_with_config('key', 'testing222', {}, {:properties => {:prop => "value"}})).to eq({:treatment => 'off', :config => nil}) + expect(split_client.get_treatment_with_config('key', 'testing222', {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:treatment => 'off', :config => nil}) check_properties(impressions_repository.batch) - expect(split_client.get_treatments_with_config('key', ['testing222'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + expect(split_client.get_treatments_with_config('key', ['testing222'], {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => {:treatment => 'off', :config => nil}}) check_properties(impressions_repository.batch) - expect(split_client.get_treatments_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + expect(split_client.get_treatments_by_flag_set('key', 'set_1', {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => 'off'}) check_properties(impressions_repository.batch) - expect(split_client.get_treatments_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => 'off'}) + expect(split_client.get_treatments_by_flag_sets('key', ['set_2'], {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => 'off'}) check_properties(impressions_repository.batch) - expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1', {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + expect(split_client.get_treatments_with_config_by_flag_set('key', 'set_1', {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => {:treatment => 'off', :config => nil}}) check_properties(impressions_repository.batch) - expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'], {}, {:properties => {:prop => "value"}})).to eq({:testing222 => {:treatment => 'off', :config => nil}}) + expect(split_client.get_treatments_with_config_by_flag_sets('key', ['set_2'], {}, SplitIoClient::Engine::Models::EvaluationOptions.new({:prop => "value"}))).to eq({:testing222 => {:treatment => 'off', :config => nil}}) check_properties(impressions_repository.batch) end From 897c28fb0780fbdd2277adfe82e6a3707bdf55d8 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 11 Jul 2025 09:37:32 -0700 Subject: [PATCH 06/10] polish --- .../engine/common/impressions_manager.rb | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index 170b9f7f..d685dd7b 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -20,21 +20,22 @@ def initialize(config, def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {}, evaluation_options = nil) - properties = evaluation_options.nil? ? nil : evaluation_options.properties + properties = get_properties(evaluation_options) impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties) + return impression(impression_data, params[:attributes]) if check_return_conditions(properties) + begin - if @config.impressions_mode == :none || impressions_disabled + if check_none_mode(impressions_disabled) @impression_counter.inc(split_name, impression_data[:m]) @unique_keys_tracker.track(split_name, matching_key) - elsif @config.impressions_mode == :debug # In DEBUG mode we should calculate the pt only. - return impression(impression_data, params[:attributes]) unless properties.nil? - - impression_data[:pt] = @impression_observer.test_and_set(impression_data) - else # In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions. - return impression(impression_data, params[:attributes]) unless properties.nil? - + end + if check_observe_impressions + # In DEBUG mode we should calculate the pt only. impression_data[:pt] = @impression_observer.test_and_set(impression_data) - @impression_counter.inc(split_name, impression_data[:m]) unless impression_data[:pt].nil? + end + if check_impression_counter(impression_data) + # In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions. + @impression_counter.inc(split_name, impression_data[:m]) end rescue StandardError => e @config.log_found_exception(__method__.to_s, e) @@ -67,6 +68,26 @@ def track(impressions_decorator) private + def check_return_conditions(properties) + return (@config.impressions_mode == :debug || @config.impressions_mode == :optimized) && !properties.nil? + end + + def check_none_mode(impressions_disabled) + return @config.impressions_mode == :none || impressions_disabled + end + + def check_observe_impressions + return @config.impressions_mode == :debug || @config.impressions_mode == :optimized + end + + def check_impression_counter(impression_data) + return @config.impressions_mode == :optimized && !impression_data[:pt].nil? + end + + def get_properties(evaluation_options) + return evaluation_options.nil? ? nil : evaluation_options.properties + end + def impression_router @impression_router ||= SplitIoClient::ImpressionRouter.new(@config) rescue StandardError => e From 5a5c7246fcac2ffe2b9092b8fbe831d020e68b69 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 31 Jul 2025 11:06:20 -0700 Subject: [PATCH 07/10] Added deprecate warn and updated version and changes --- CHANGES.txt | 3 +++ lib/splitclient-rb/clients/split_client.rb | 11 +++++++++++ lib/splitclient-rb/version.rb | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 33219edd..be40f790 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,8 @@ CHANGES +8.7.0 (Aug 1, 2025) +- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. + 8.6.0 (Jun 17, 2025) - Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. - Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 523c5d0d..4979bede 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -38,6 +38,8 @@ def get_treatment( key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) + log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator') + result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, evaluation_options) return result.tap { |t| t.delete(:config) } if multiple result[:treatment] @@ -47,6 +49,7 @@ def get_treatment_with_config( key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, multiple = false, evaluator = nil ) + log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator') treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, evaluation_options) end @@ -332,6 +335,10 @@ def treatments(key, feature_flag_names, attributes = {}, evaluation_options = ni # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true, calling_method = 'get_treatment', multiple = false, evaluation_options = nil) + + log_deprecated_warning(calling_method, split_data, 'split_data') + log_deprecated_warning(calling_method, store_impressions'store_impressions') + impressions = [] bucketing_key, matching_key = keys_from_key(key) @@ -355,6 +362,10 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i treatments end + def log_deprecated_warning(calling_method, parameter, parameter_name) + @config.logger.warn("#{calling_method}: detected #{parameter_name} parameter used, this parameter is deprecated and its value is ignored.") unless parameter.nil? + end + def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, evaluation_options = nil) impressions_decorator = [] begin diff --git a/lib/splitclient-rb/version.rb b/lib/splitclient-rb/version.rb index d0431b85..c34677cc 100644 --- a/lib/splitclient-rb/version.rb +++ b/lib/splitclient-rb/version.rb @@ -1,3 +1,3 @@ module SplitIoClient - VERSION = '8.6.0' + VERSION = '8.7.0' end From d8584ec8ccf27b75500b9c37bf02a20ba260f268 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 31 Jul 2025 11:15:54 -0700 Subject: [PATCH 08/10] Fixed typo --- lib/splitclient-rb/clients/split_client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 4979bede..e478f596 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -337,7 +337,7 @@ def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_i calling_method = 'get_treatment', multiple = false, evaluation_options = nil) log_deprecated_warning(calling_method, split_data, 'split_data') - log_deprecated_warning(calling_method, store_impressions'store_impressions') + log_deprecated_warning(calling_method, store_impressions, 'store_impressions') impressions = [] bucketing_key, matching_key = keys_from_key(key) From 3b0e2ecfc6ce7698b89c9db1eacb1ae44839d184 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 31 Jul 2025 17:44:11 -0700 Subject: [PATCH 09/10] Fixed deprecated param value --- lib/splitclient-rb/clients/split_client.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index e478f596..6af46820 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -35,7 +35,7 @@ def initialize(sdk_key, repositories, status_manager, config, impressions_manage end def get_treatment( - key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = nil, multiple = false, evaluator = nil ) log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator') @@ -46,7 +46,7 @@ def get_treatment( end def get_treatment_with_config( - key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = true, + key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = nil, multiple = false, evaluator = nil ) log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator') @@ -333,7 +333,7 @@ def treatments(key, feature_flag_names, attributes = {}, evaluation_options = ni # @param split_data [Hash] split data, when provided this method doesn't fetch splits_repository for the data # @param store_impressions [Boolean] impressions aren't stored if this flag is false # @return [String/Hash] Treatment as String or Hash of treatments in case of array of features - def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = true, + def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = nil, calling_method = 'get_treatment', multiple = false, evaluation_options = nil) log_deprecated_warning(calling_method, split_data, 'split_data') From 76ac3f9158bc545177911e1b6409d31632bb9a83 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 31 Jul 2025 18:01:51 -0700 Subject: [PATCH 10/10] polishing --- lib/splitclient-rb/cache/senders/impressions_formatter.rb | 1 - lib/splitclient-rb/clients/split_client.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/splitclient-rb/cache/senders/impressions_formatter.rb b/lib/splitclient-rb/cache/senders/impressions_formatter.rb index eed8570e..a84522f5 100644 --- a/lib/splitclient-rb/cache/senders/impressions_formatter.rb +++ b/lib/splitclient-rb/cache/senders/impressions_formatter.rb @@ -36,7 +36,6 @@ def feature_impressions(filtered_impressions, feature) end def current_impressions(feature_impressions) - formatted = [] feature_impressions.map do |impression| if impression[:i][:properties].nil? impression = { diff --git a/lib/splitclient-rb/clients/split_client.rb b/lib/splitclient-rb/clients/split_client.rb index 6af46820..31a40742 100644 --- a/lib/splitclient-rb/clients/split_client.rb +++ b/lib/splitclient-rb/clients/split_client.rb @@ -244,7 +244,7 @@ def validate_evaluation_options(evaluation_options) return nil, 0 end evaluation_options.properties = evaluation_options.properties.transform_keys(&:to_sym) - evaluation_options.properties, size = validate_properties(evaluation_options.properties, method = 'Treatment') + evaluation_options.properties, size = validate_properties(evaluation_options.properties, 'Treatment') return evaluation_options, size end