kameleoon-client-ruby 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kameleoon/configuration/data_file.rb +9 -6
- data/lib/kameleoon/configuration/experiment.rb +46 -0
- data/lib/kameleoon/configuration/rule.rb +3 -30
- data/lib/kameleoon/data/manager/visitor.rb +1 -1
- data/lib/kameleoon/kameleoon_client.rb +141 -93
- data/lib/kameleoon/network/cookie/cookie_manager.rb +2 -2
- data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +2 -3
- data/lib/kameleoon/targeting/targeting_manager.rb +3 -3
- data/lib/kameleoon/utils.rb +9 -16
- data/lib/kameleoon/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a64d8edfee07c22561b166de49f9185d89a6fbdec9b49f6fd65860125bd54eab
|
4
|
+
data.tar.gz: ee7ccf78d0d11dec198b930e77e496c39a220b87471d724015c78114b85368f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccfba38b35f50d2b1dda0ef2bf31406f74fd92663276f4297ae867bc7b88b8606b345a8a4234f24ef71c71fdc3555cc9852d7324c1a6a668ed8b6cbc98cfeedb
|
7
|
+
data.tar.gz: 38543c67b7c8949ccba5c8e0f08eae86d2755a737bc25a5dc82e94dd92011c7678cb4b32647ffae0edb606913bff537455f2f42295fe9f421554726a57675808
|
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/configuration/settings'
|
4
|
-
require 'kameleoon/configuration/feature_flag'
|
5
3
|
require 'kameleoon/configuration/custom_data_info'
|
4
|
+
require 'kameleoon/configuration/experiment'
|
5
|
+
require 'kameleoon/configuration/feature_flag'
|
6
|
+
require 'kameleoon/configuration/settings'
|
6
7
|
require 'kameleoon/logging/kameleoon_logger'
|
7
8
|
|
8
9
|
module Kameleoon
|
9
10
|
module Configuration
|
10
11
|
class DataFile
|
11
12
|
attr_reader :settings, :feature_flags, :has_any_targeted_delivery_rule, :feature_flag_by_id, :rule_by_segment_id,
|
12
|
-
:rule_info_by_exp_id, :variation_by_id, :custom_data_info, :experiment_ids_with_js_css_variable
|
13
|
+
:rule_info_by_exp_id, :variation_by_id, :custom_data_info, :experiment_ids_with_js_css_variable,
|
14
|
+
:holdout
|
13
15
|
|
14
16
|
def to_s
|
15
17
|
'DataFile{' \
|
@@ -64,6 +66,7 @@ module Kameleoon
|
|
64
66
|
end
|
65
67
|
@has_any_targeted_delivery_rule = any_targeted_delivery_rule?
|
66
68
|
@custom_data_info = CustomDataInfo.new(configuration['customData'])
|
69
|
+
@holdout = Experiment.from_json(configuration['holdout']) if configuration.include?('holdout')
|
67
70
|
Logging::KameleoonLogger.debug('RETURN: DataFile.init(configuration: %s)', configuration)
|
68
71
|
end
|
69
72
|
|
@@ -85,11 +88,11 @@ module Kameleoon
|
|
85
88
|
has_feature_flag_variable_js_css = feature_flag_variable_js_css?(feature_flag)
|
86
89
|
feature_flag.rules.each do |rule|
|
87
90
|
@rule_by_segment_id[rule.segment_id] = rule
|
88
|
-
@rule_info_by_exp_id[rule.
|
89
|
-
rule.
|
91
|
+
@rule_info_by_exp_id[rule.experiment.id] = RuleInfo.new(feature_flag, rule)
|
92
|
+
rule.experiment.variations_by_exposition.each do |variation|
|
90
93
|
@variation_by_id[variation.variation_id] = variation
|
91
94
|
end
|
92
|
-
@experiment_ids_with_js_css_variable.add(rule.
|
95
|
+
@experiment_ids_with_js_css_variable.add(rule.experiment.id) if has_feature_flag_variable_js_css
|
93
96
|
end
|
94
97
|
end
|
95
98
|
@feature_flag_by_id.freeze
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'variation_exposition'
|
4
|
+
require 'kameleoon/exceptions'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
module Configuration
|
8
|
+
class Experiment
|
9
|
+
attr_reader :id, :variations_by_exposition
|
10
|
+
|
11
|
+
def initialize(id, variations_by_exposition)
|
12
|
+
@id = id
|
13
|
+
@variations_by_exposition = variations_by_exposition
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_json(hash)
|
17
|
+
id = hash['experimentId'] || 0
|
18
|
+
variations_by_exposition = VariationByExposition.create_from_array(hash['variationByExposition'])
|
19
|
+
variations_by_exposition.freeze
|
20
|
+
Experiment.new(id, variations_by_exposition)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"Experiment{id:#{@id}}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_variation(hash_double)
|
28
|
+
total = 0.0
|
29
|
+
@variations_by_exposition.each do |var_by_exp|
|
30
|
+
total += var_by_exp.exposition
|
31
|
+
return var_by_exp if total >= hash_double
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_variation_by_key(variation_key)
|
37
|
+
var_by_exp = @variations_by_exposition.find { |v| v.variation_key == variation_key }
|
38
|
+
unless var_by_exp
|
39
|
+
raise Exception::FeatureVariationNotFound.new(variation_key),
|
40
|
+
"#{self} does not contain variation '#{variation_key}'"
|
41
|
+
end
|
42
|
+
var_by_exp
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require 'kameleoon/exceptions'
|
3
|
+
require_relative 'experiment'
|
5
4
|
require 'kameleoon/targeting/models'
|
6
5
|
|
7
6
|
module Kameleoon
|
@@ -30,8 +29,7 @@ module Kameleoon
|
|
30
29
|
|
31
30
|
# Rule is a class for new rules of feature flags
|
32
31
|
class Rule
|
33
|
-
attr_reader :id, :order, :type, :exposition, :
|
34
|
-
:first_variation
|
32
|
+
attr_reader :id, :order, :type, :exposition, :experiment, :respool_time, :segment_id
|
35
33
|
attr_accessor :targeting_segment
|
36
34
|
|
37
35
|
def self.create_from_array(array)
|
@@ -47,35 +45,10 @@ module Kameleoon
|
|
47
45
|
@order = hash['order']
|
48
46
|
@type = RuleType.from_literal(hash['type'])
|
49
47
|
@exposition = hash['exposition']
|
50
|
-
@experiment_id = hash['experimentId']
|
51
48
|
@respool_time = hash['respoolTime']
|
52
|
-
@variation_by_exposition = VariationByExposition.create_from_array(hash['variationByExposition'])
|
53
|
-
@variation_by_exposition.freeze
|
54
|
-
@first_variation = @variation_by_exposition.first
|
55
49
|
@targeting_segment = Kameleoon::Targeting::Segment.new((hash['segment'])) if hash['segment']
|
56
50
|
@segment_id = @targeting_segment != nil ? targeting_segment.id : -1
|
57
|
-
|
58
|
-
|
59
|
-
def get_variation(hash_double)
|
60
|
-
total = 0.0
|
61
|
-
variation_by_exposition.each do |var_by_exp|
|
62
|
-
total += var_by_exp.exposition
|
63
|
-
return var_by_exp if total >= hash_double
|
64
|
-
end
|
65
|
-
nil
|
66
|
-
end
|
67
|
-
|
68
|
-
def get_variation_by_key(variation_key)
|
69
|
-
var_by_exp = variation_by_exposition.find { |v| v.variation_key == variation_key }
|
70
|
-
unless var_by_exp
|
71
|
-
raise Exception::FeatureVariationNotFound.new(variation_key),
|
72
|
-
"#{self} does not contain variation '#{variation_key}'"
|
73
|
-
end
|
74
|
-
var_by_exp
|
75
|
-
end
|
76
|
-
|
77
|
-
def get_variation_id_by_key(key)
|
78
|
-
variation_by_exposition.select { |v| v.variation_key == key }.first&.variation_id
|
51
|
+
@experiment = Experiment.from_json(hash)
|
79
52
|
end
|
80
53
|
|
81
54
|
def experimentation_type?
|
@@ -405,7 +405,7 @@ module Kameleoon
|
|
405
405
|
|
406
406
|
def add_forced_experiment_variation(forced_variation)
|
407
407
|
@forced_variations ||= {}
|
408
|
-
@forced_variations[forced_variation.rule.
|
408
|
+
@forced_variations[forced_variation.rule.experiment.id] = forced_variation
|
409
409
|
end
|
410
410
|
end
|
411
411
|
end
|
@@ -321,9 +321,9 @@ module Kameleoon
|
|
321
321
|
)
|
322
322
|
Utils::VisitorCode.validate(visitor_code)
|
323
323
|
feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
|
324
|
-
variation_key,
|
324
|
+
variation_key, eval_exp = get_variation_info(visitor_code, feature_flag, track)
|
325
325
|
variation = feature_flag.get_variation_by_key(variation_key)
|
326
|
-
external_variation =
|
326
|
+
external_variation = create_external_variation(variation, eval_exp)
|
327
327
|
@tracking_manager.add_visitor_code(visitor_code) if track
|
328
328
|
Logging::KameleoonLogger.info(
|
329
329
|
"RETURN: KameleoonClient.get_variation(visitor_code: '%s', feature_key: '%s', track: %s)" \
|
@@ -358,12 +358,11 @@ module Kameleoon
|
|
358
358
|
@data_manager.data_file.feature_flags.each_value do |feature_flag|
|
359
359
|
next unless feature_flag.environment_enabled
|
360
360
|
|
361
|
-
variation_key,
|
361
|
+
variation_key, eval_exp = get_variation_info(visitor_code, feature_flag, track)
|
362
362
|
next if only_active && (variation_key == Configuration::VariationType::VARIATION_OFF)
|
363
363
|
|
364
364
|
variation = feature_flag.get_variation_by_key(variation_key)
|
365
|
-
variations[feature_flag.feature_key] =
|
366
|
-
make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
|
365
|
+
variations[feature_flag.feature_key] = create_external_variation(variation, eval_exp)
|
367
366
|
end
|
368
367
|
variations.freeze
|
369
368
|
@tracking_manager.add_visitor_code(visitor_code) if track
|
@@ -624,14 +623,8 @@ module Kameleoon
|
|
624
623
|
@data_manager.data_file.feature_flags.each do |feature_key, feature_flag|
|
625
624
|
next unless feature_flag.environment_enabled
|
626
625
|
|
627
|
-
|
628
|
-
|
629
|
-
variation = forced_variation.var_by_exp
|
630
|
-
rule = forced_variation.rule
|
631
|
-
else
|
632
|
-
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
633
|
-
end
|
634
|
-
variation_key = _get_variation_key(variation, rule, feature_flag)
|
626
|
+
eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
|
627
|
+
variation_key = calculate_variation_key(eval_exp, feature_flag.default_variation_key)
|
635
628
|
list_keys.push(feature_key) if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
636
629
|
end
|
637
630
|
Logging::KameleoonLogger.info(
|
@@ -665,20 +658,13 @@ module Kameleoon
|
|
665
658
|
@data_manager.data_file.feature_flags.each_value do |feature_flag|
|
666
659
|
next unless feature_flag.environment_enabled
|
667
660
|
|
668
|
-
|
669
|
-
|
670
|
-
var_by_exp = forced_variation.var_by_exp
|
671
|
-
rule = forced_variation.rule
|
672
|
-
else
|
673
|
-
var_by_exp, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
674
|
-
end
|
675
|
-
variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
|
661
|
+
eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
|
662
|
+
variation_key = calculate_variation_key(eval_exp, feature_flag.default_variation_key)
|
676
663
|
|
677
664
|
next if variation_key == Configuration::VariationType::VARIATION_OFF
|
678
665
|
|
679
666
|
variation = feature_flag.get_variation_by_key(variation_key)
|
680
|
-
map_active_features[feature_flag.feature_key] =
|
681
|
-
make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
|
667
|
+
map_active_features[feature_flag.feature_key] = create_external_variation(variation, eval_exp)
|
682
668
|
end
|
683
669
|
|
684
670
|
map_active_features.freeze
|
@@ -753,7 +739,7 @@ module Kameleoon
|
|
753
739
|
raise Exception::FeatureExperimentNotFound.new(experiment_id), "Experiment #{experiment_id} is not found"
|
754
740
|
end
|
755
741
|
|
756
|
-
var_by_exp = rule_info.rule.get_variation_by_key(variation_key)
|
742
|
+
var_by_exp = rule_info.rule.experiment.get_variation_by_key(variation_key)
|
757
743
|
forced_variation = DataManager::ForcedExperimentVariation.new(rule_info.rule, var_by_exp, force_targeting)
|
758
744
|
@visitor_manager.add_data(visitor_code, forced_variation)
|
759
745
|
end
|
@@ -917,23 +903,64 @@ module Kameleoon
|
|
917
903
|
visitor_code, feature_flag, track
|
918
904
|
)
|
919
905
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
906
|
+
eval_exp = evaluate(visitor, visitor_code, feature_flag, track, true)
|
907
|
+
variation_key = calculate_variation_key(eval_exp, feature_flag.default_variation_key)
|
908
|
+
Logging::KameleoonLogger.debug(
|
909
|
+
"RETURN: KameleoonClient.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)" \
|
910
|
+
' -> (variation_key: %s, eval_exp: %s)',
|
911
|
+
visitor_code, feature_flag, track, variation_key, eval_exp
|
912
|
+
)
|
913
|
+
[variation_key, eval_exp]
|
914
|
+
end
|
915
|
+
|
916
|
+
def evaluate(visitor, visitor_code, feature_flag, track, save)
|
917
|
+
Logging::KameleoonLogger.debug(
|
918
|
+
"CALL: KameleoonClient.evaluate(visitor, visitor_code: '%s', feature_flag: %s, track: %s, save: %s)",
|
919
|
+
visitor_code, feature_flag, track, save
|
920
|
+
)
|
921
|
+
eval_exp = nil
|
920
922
|
forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
|
921
923
|
if forced_variation
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
var_by_exp, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
924
|
+
eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
|
925
|
+
elsif visitor_not_in_holdout?(visitor, visitor_code, track, save)
|
926
|
+
eval_exp = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
926
927
|
end
|
927
|
-
|
928
|
-
|
928
|
+
save_variation(visitor_code, eval_exp, track: track) if save && !forced_variation&.simulated
|
929
|
+
Logging::KameleoonLogger.debug(
|
930
|
+
"RETURN: KameleoonClient.evaluate(visitor, visitor_code: '%s', feature_flag: %s, track: %s, save: %s)" \
|
931
|
+
' -> (eval_exp: %s)',
|
932
|
+
visitor_code, feature_flag, track, save, eval_exp
|
933
|
+
)
|
934
|
+
eval_exp
|
935
|
+
end
|
936
|
+
|
937
|
+
def visitor_not_in_holdout?(visitor, visitor_code, track, save)
|
938
|
+
in_holdout_variation_key = 'in-holdout'
|
939
|
+
Logging::KameleoonLogger.debug(
|
940
|
+
"CALL: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s)",
|
941
|
+
visitor_code, track, save
|
942
|
+
)
|
943
|
+
holdout = @data_manager.data_file.holdout
|
944
|
+
is_not_in_holdout = true
|
945
|
+
unless holdout.nil?
|
946
|
+
code_for_hash = get_code_for_hash(visitor, visitor_code)
|
947
|
+
variation_hash = Utils::Hasher.obtain(code_for_hash, holdout.id)
|
948
|
+
Logging::KameleoonLogger.debug("Calculated holdout hash %s for code '%s'", variation_hash, code_for_hash)
|
949
|
+
var_by_exp = holdout.get_variation(variation_hash)
|
950
|
+
unless var_by_exp.nil?
|
951
|
+
is_not_in_holdout = var_by_exp.variation_key != in_holdout_variation_key
|
952
|
+
if save
|
953
|
+
eval_exp = EvaluatedExperiment.new(var_by_exp, holdout, Configuration::RuleType::EXPERIMENTATION)
|
954
|
+
save_variation(visitor_code, eval_exp, track: track)
|
955
|
+
end
|
956
|
+
end
|
929
957
|
end
|
930
|
-
variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
|
931
958
|
Logging::KameleoonLogger.debug(
|
932
|
-
"RETURN: KameleoonClient.
|
933
|
-
' -> (
|
934
|
-
visitor_code,
|
959
|
+
"RETURN: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s)" \
|
960
|
+
' -> (is_not_in_holdout: %s)',
|
961
|
+
visitor_code, track, save, is_not_in_holdout
|
935
962
|
)
|
936
|
-
|
963
|
+
is_not_in_holdout
|
937
964
|
end
|
938
965
|
|
939
966
|
##
|
@@ -945,15 +972,8 @@ module Kameleoon
|
|
945
972
|
)
|
946
973
|
feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
|
947
974
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
948
|
-
|
949
|
-
|
950
|
-
variation = forced_variation.var_by_exp
|
951
|
-
rule = forced_variation.rule
|
952
|
-
else
|
953
|
-
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
954
|
-
end
|
955
|
-
save_variation(visitor_code, rule, variation) unless forced_variation&.simulated
|
956
|
-
variation_key = _get_variation_key(variation, rule, feature_flag)
|
975
|
+
eval_exp = evaluate(visitor, visitor_code, feature_flag, true, true)
|
976
|
+
variation_key = calculate_variation_key(eval_exp, feature_flag.default_variation_key)
|
957
977
|
@tracking_manager.add_visitor_code(visitor_code)
|
958
978
|
Logging::KameleoonLogger.debug(
|
959
979
|
"RETURN: KameleoonClient._get_feature_variation_key(visitor_code: '%s', feature_key: '%s')" \
|
@@ -963,25 +983,30 @@ module Kameleoon
|
|
963
983
|
[feature_flag, variation_key]
|
964
984
|
end
|
965
985
|
|
966
|
-
def save_variation(visitor_code,
|
967
|
-
|
968
|
-
variation_id = var_by_exp&.variation_id
|
969
|
-
return if experiment_id.nil? || variation_id.nil?
|
986
|
+
def save_variation(visitor_code, eval_exp, track: true)
|
987
|
+
return if eval_exp.nil? || eval_exp.experiment.id.zero? || eval_exp.var_by_exp.variation_id.nil?
|
970
988
|
|
971
989
|
Logging::KameleoonLogger.debug(
|
972
|
-
"CALL: KameleoonClient.save_variation(visitor_code: '%s',
|
973
|
-
visitor_code,
|
990
|
+
"CALL: KameleoonClient.save_variation(visitor_code: '%s', eval_exp: %s, track: %s)",
|
991
|
+
visitor_code, eval_exp, track
|
974
992
|
)
|
975
993
|
visitor = @visitor_manager.get_or_create_visitor(visitor_code)
|
976
|
-
as_variation = Kameleoon::DataManager::AssignedVariation.new(
|
994
|
+
as_variation = Kameleoon::DataManager::AssignedVariation.new(
|
995
|
+
eval_exp.experiment.id, eval_exp.var_by_exp.variation_id, eval_exp.rule_type
|
996
|
+
)
|
977
997
|
as_variation.mark_as_sent unless track
|
978
998
|
visitor.assign_variation(as_variation)
|
979
999
|
Logging::KameleoonLogger.debug(
|
980
|
-
"RETURN: KameleoonClient.save_variation(visitor_code: '%s',
|
981
|
-
visitor_code,
|
1000
|
+
"RETURN: KameleoonClient.save_variation(visitor_code: '%s', eval_exp: %s, track: %s)",
|
1001
|
+
visitor_code, eval_exp, track
|
982
1002
|
)
|
983
1003
|
end
|
984
1004
|
|
1005
|
+
def get_code_for_hash(visitor, visitor_code)
|
1006
|
+
# use mappingIdentifier instead of visitor_code if it was set up
|
1007
|
+
visitor&.mapping_identifier || visitor_code
|
1008
|
+
end
|
1009
|
+
|
985
1010
|
##
|
986
1011
|
# helper method for calculate variation key for feature flag
|
987
1012
|
def _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
@@ -990,48 +1015,40 @@ module Kameleoon
|
|
990
1015
|
visitor_code, feature_flag
|
991
1016
|
)
|
992
1017
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
1018
|
+
code_for_hash = get_code_for_hash(visitor, visitor_code)
|
993
1019
|
# no rules -> return default_variation_key
|
994
|
-
|
995
|
-
selected_rule = nil
|
1020
|
+
selected = nil
|
996
1021
|
feature_flag.rules.each do |rule|
|
997
|
-
forced_variation = visitor&.get_forced_experiment_variation(rule.
|
1022
|
+
forced_variation = visitor&.get_forced_experiment_variation(rule.experiment.id)
|
998
1023
|
if forced_variation&.force_targeting
|
999
1024
|
# Forcing experiment variation in force-targeting mode
|
1000
|
-
|
1001
|
-
selected_rule = rule
|
1025
|
+
selected = EvaluatedExperiment.from_var_by_exp_rule(forced_variation.var_by_exp, rule)
|
1002
1026
|
break
|
1003
1027
|
end
|
1004
1028
|
# check if visitor is targeted for rule, else next rule
|
1005
|
-
next unless check_targeting(visitor_code, rule.
|
1029
|
+
next unless check_targeting(visitor_code, rule.experiment.id, rule)
|
1006
1030
|
|
1007
1031
|
unless forced_variation.nil?
|
1008
1032
|
# Forcing experiment variation in targeting-only mode
|
1009
|
-
|
1010
|
-
selected_rule = rule
|
1033
|
+
selected = EvaluatedExperiment.from_var_by_exp_rule(forced_variation.var_by_exp, rule)
|
1011
1034
|
break
|
1012
1035
|
end
|
1013
|
-
# use mappingIdentifier instead of visitorCode if it was set up
|
1014
|
-
code_for_hash = visitor&.mapping_identifier || visitor_code
|
1015
1036
|
# uses for rule exposition
|
1016
|
-
hash_rule = Utils::
|
1017
|
-
Logging::KameleoonLogger.debug("Calculated
|
1037
|
+
hash_rule = Utils::Hasher.obtain(code_for_hash, rule.id, rule.respool_time)
|
1038
|
+
Logging::KameleoonLogger.debug("Calculated rule hash %s for code '%s'", hash_rule, code_for_hash)
|
1018
1039
|
# check main expostion for rule with hashRule
|
1019
1040
|
if hash_rule <= rule.exposition
|
1020
1041
|
if rule.targeted_delivery_type?
|
1021
|
-
|
1022
|
-
selected_rule = rule
|
1042
|
+
selected = EvaluatedExperiment.from_var_by_exp_rule(rule.experiment.variations_by_exposition.first, rule)
|
1023
1043
|
break
|
1024
1044
|
end
|
1025
1045
|
# uses for variation's expositions
|
1026
|
-
hash_variation = Utils::
|
1027
|
-
Logging::KameleoonLogger.debug(
|
1028
|
-
"Calculated hash_variation: %s for visitor_code: '%s'", hash_variation, code_for_hash
|
1029
|
-
)
|
1046
|
+
hash_variation = Utils::Hasher.obtain(code_for_hash, rule.experiment.id, rule.respool_time)
|
1047
|
+
Logging::KameleoonLogger.debug("Calculated variation hash %s for code '%s'", hash_variation, code_for_hash)
|
1030
1048
|
# get variation key with new hashVariation
|
1031
|
-
variation = rule.get_variation(hash_variation)
|
1049
|
+
variation = rule.experiment.get_variation(hash_variation)
|
1032
1050
|
unless variation.nil?
|
1033
|
-
|
1034
|
-
selected_rule = rule
|
1051
|
+
selected = EvaluatedExperiment.from_var_by_exp_rule(variation, rule)
|
1035
1052
|
break
|
1036
1053
|
end
|
1037
1054
|
# if visitor is targeted for targeted rule then break cycle -> return default
|
@@ -1041,39 +1058,44 @@ module Kameleoon
|
|
1041
1058
|
end
|
1042
1059
|
Logging::KameleoonLogger.debug(
|
1043
1060
|
"RETURN: KameleoonClient._calculate_variation_key_for_feature(visitor_code: '%s', feature_flag: %s) " \
|
1044
|
-
'-> (
|
1061
|
+
'-> (eval_exp: %s)', visitor_code, feature_flag, selected
|
1045
1062
|
)
|
1046
|
-
|
1063
|
+
selected
|
1047
1064
|
end
|
1048
1065
|
|
1049
|
-
def
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1066
|
+
def calculate_variation_key(eval_exp, default_value)
|
1067
|
+
Logging::KameleoonLogger.debug(
|
1068
|
+
"CALL: KameleoonClient.calculate_variation_key(eval_exp: %s, default_value: '%s') ", eval_exp, default_value
|
1069
|
+
)
|
1070
|
+
variation_key = eval_exp ? eval_exp.var_by_exp.variation_key : default_value
|
1071
|
+
Logging::KameleoonLogger.debug(
|
1072
|
+
"RETURN: KameleoonClient.calculate_variation_key(eval_exp: %s, default_value: '%s') -> (variation_key: '%s')",
|
1073
|
+
eval_exp, default_value, variation_key
|
1074
|
+
)
|
1075
|
+
variation_key
|
1054
1076
|
end
|
1055
1077
|
|
1056
|
-
def
|
1078
|
+
def create_external_variation(variation, eval_exp)
|
1057
1079
|
Logging::KameleoonLogger.debug(
|
1058
|
-
'CALL: KameleoonClient.
|
1059
|
-
internal_variation, variation_id, experiment_id
|
1080
|
+
'CALL: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s)', variation, eval_exp
|
1060
1081
|
)
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1082
|
+
ext_variables = {}
|
1083
|
+
variation&.variables&.each do |variable|
|
1084
|
+
ext_variables[variable.key] = Types::Variable.new(
|
1064
1085
|
variable.key,
|
1065
1086
|
variable.type,
|
1066
1087
|
_parse_feature_variable(variable)
|
1067
1088
|
)
|
1068
1089
|
end
|
1069
|
-
|
1070
|
-
|
1090
|
+
ext_variables.freeze
|
1091
|
+
ext_variation = Types::Variation.new(
|
1092
|
+
variation&.key, eval_exp&.var_by_exp&.variation_id, eval_exp&.experiment&.id, ext_variables
|
1093
|
+
)
|
1071
1094
|
Logging::KameleoonLogger.debug(
|
1072
|
-
'RETURN: KameleoonClient.
|
1073
|
-
|
1074
|
-
internal_variation, variation_id, experiment_id, variation
|
1095
|
+
'RETURN: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s) -> (ext_variation: %s)',
|
1096
|
+
variation, eval_exp, ext_variation
|
1075
1097
|
)
|
1076
|
-
|
1098
|
+
ext_variation
|
1077
1099
|
end
|
1078
1100
|
|
1079
1101
|
##
|
@@ -1096,5 +1118,31 @@ module Kameleoon
|
|
1096
1118
|
)
|
1097
1119
|
@visitor_manager.add_data(visitor_code, UniqueIdentifier.new(is_unique_identifier))
|
1098
1120
|
end
|
1121
|
+
|
1122
|
+
class EvaluatedExperiment
|
1123
|
+
attr_reader :var_by_exp, :experiment, :rule_type
|
1124
|
+
|
1125
|
+
def initialize(var_by_exp, experiment, rule_type)
|
1126
|
+
@var_by_exp = var_by_exp
|
1127
|
+
@experiment = experiment
|
1128
|
+
@rule_type = rule_type
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
def self.from_var_by_exp_rule(var_by_exp, rule)
|
1132
|
+
EvaluatedExperiment.new(var_by_exp, rule.experiment, rule.type)
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
def self.from_forced_variation(forced_variation)
|
1136
|
+
if forced_variation.var_by_exp.nil? || forced_variation.rule.nil?
|
1137
|
+
nil
|
1138
|
+
else
|
1139
|
+
EvaluatedExperiment.from_var_by_exp_rule(forced_variation.var_by_exp, forced_variation.rule)
|
1140
|
+
end
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def self.from_forced_experiment_variation(forced_variation)
|
1144
|
+
EvaluatedExperiment.from_var_by_exp_rule(forced_variation.var_by_exp, forced_variation.rule)
|
1145
|
+
end
|
1146
|
+
end
|
1099
1147
|
end
|
1100
1148
|
end
|
@@ -163,12 +163,12 @@ module Kameleoon
|
|
163
163
|
end
|
164
164
|
return DataManager::ForcedFeatureVariation.new(feature_key, nil, nil, true) if experiment_id.zero?
|
165
165
|
|
166
|
-
rule = feature_flag.rules.find { |r| r.
|
166
|
+
rule = feature_flag.rules.find { |r| r.experiment.id == experiment_id }
|
167
167
|
unless rule
|
168
168
|
Logging::KameleoonLogger.error('Simulated experiment %d is not found', experiment_id)
|
169
169
|
return nil
|
170
170
|
end
|
171
|
-
var_by_exp = rule.
|
171
|
+
var_by_exp = rule.experiment.variations_by_exposition.find { |v| v.variation_id == variation_id }
|
172
172
|
unless var_by_exp
|
173
173
|
Logging::KameleoonLogger.error('Simulated variation %d is not found', variation_id)
|
174
174
|
return nil
|
@@ -25,11 +25,10 @@ module Kameleoon
|
|
25
25
|
end
|
26
26
|
|
27
27
|
private def check_rule(data, rule)
|
28
|
-
return false
|
29
|
-
|
28
|
+
return false unless rule.is_a?(Kameleoon::Configuration::Rule)
|
30
29
|
return false if !@condition_rule_id.nil? && (@condition_rule_id != rule.id)
|
31
30
|
|
32
|
-
variation = data.variations_storage.get(rule.
|
31
|
+
variation = data.variations_storage.get(rule.experiment.id)
|
33
32
|
return false if variation.nil?
|
34
33
|
|
35
34
|
return true if @condition_variation_key.nil?
|
@@ -14,13 +14,13 @@ module Kameleoon
|
|
14
14
|
|
15
15
|
def check_targeting(visitor_code, campaign_id, exp_ff_rule)
|
16
16
|
Logging::KameleoonLogger.debug(
|
17
|
-
"CALL: TargetingManager.check_targeting(
|
17
|
+
"CALL: TargetingManager.check_targeting(visitor_code: '%s', campaign_id: %s, exp_ff_rule: %s)",
|
18
18
|
visitor_code, campaign_id, exp_ff_rule
|
19
19
|
)
|
20
20
|
segment = exp_ff_rule.targeting_segment
|
21
21
|
if segment.nil?
|
22
22
|
Logging::KameleoonLogger.debug(
|
23
|
-
"RETURN: TargetingManager.check_targeting(
|
23
|
+
"RETURN: TargetingManager.check_targeting(visitor_code: '%s', campaign_id: %s, exp_ff_rule: %s) -> " \
|
24
24
|
'(targeting: true)', visitor_code, campaign_id, exp_ff_rule
|
25
25
|
)
|
26
26
|
return true
|
@@ -29,7 +29,7 @@ module Kameleoon
|
|
29
29
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
30
30
|
targeting = segment.check_tree(->(type) { get_condition_data(type, visitor, visitor_code, campaign_id) })
|
31
31
|
Logging::KameleoonLogger.debug(
|
32
|
-
"RETURN: TargetingManager.check_targeting(
|
32
|
+
"RETURN: TargetingManager.check_targeting(visitor_code: '%s', campaign_id: %s, exp_ff_rule: %s) -> " \
|
33
33
|
'(targeting: %s)', visitor_code, campaign_id, exp_ff_rule, targeting
|
34
34
|
)
|
35
35
|
targeting
|
data/lib/kameleoon/utils.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'fiber'
|
4
3
|
require 'bigdecimal'
|
4
|
+
require 'digest'
|
5
|
+
require 'fiber'
|
5
6
|
require 'kameleoon/utils'
|
6
7
|
require 'kameleoon/exceptions'
|
7
8
|
require 'kameleoon/logging/kameleoon_logger'
|
@@ -32,23 +33,15 @@ module Kameleoon
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
module
|
36
|
-
def self.obtain(visitor_code,
|
37
|
-
|
36
|
+
module Hasher
|
37
|
+
def self.obtain(visitor_code, container_id, suffix = nil)
|
38
|
+
calculate("#{visitor_code}#{container_id}#{suffix}")
|
38
39
|
end
|
39
40
|
|
40
|
-
def self.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def self.obtain_helper(visitor_code, respool_times, container_id, suffix)
|
47
|
-
identifier = visitor_code.to_s
|
48
|
-
identifier += container_id.to_s
|
49
|
-
identifier += suffix.to_s
|
50
|
-
identifier += respool_times.sort.to_h.values.join.to_s if !respool_times.nil? && !respool_times.empty?
|
51
|
-
(Digest::SHA256.hexdigest(identifier.encode('UTF-8')).to_i(16) / (BigDecimal('2')**BigDecimal('256'))).round(16)
|
41
|
+
def self.calculate(string_to_hash)
|
42
|
+
parsed_value = Digest::SHA256.hexdigest(string_to_hash.encode('UTF-8')).to_i(16)
|
43
|
+
max_value = BigDecimal('2')**BigDecimal('256')
|
44
|
+
(parsed_value / max_value).round(16)
|
52
45
|
end
|
53
46
|
end
|
54
47
|
|
data/lib/kameleoon/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kameleoon-client-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kameleoon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/kameleoon/client_readiness.rb
|
79
79
|
- lib/kameleoon/configuration/custom_data_info.rb
|
80
80
|
- lib/kameleoon/configuration/data_file.rb
|
81
|
+
- lib/kameleoon/configuration/experiment.rb
|
81
82
|
- lib/kameleoon/configuration/feature_flag.rb
|
82
83
|
- lib/kameleoon/configuration/rule.rb
|
83
84
|
- lib/kameleoon/configuration/settings.rb
|