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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e0001c490d29beefab2f33c416affe19f6ccbc05d83e0aaad70be85070986e3
4
- data.tar.gz: 6959eb4d8f421b9e14e62f3c490d5e1c6f038a25d3e5cb785c1832d7908c291d
3
+ metadata.gz: a64d8edfee07c22561b166de49f9185d89a6fbdec9b49f6fd65860125bd54eab
4
+ data.tar.gz: ee7ccf78d0d11dec198b930e77e496c39a220b87471d724015c78114b85368f8
5
5
  SHA512:
6
- metadata.gz: dfe9e75f3563287b6f187a38066a623145ba7ed916d12b9c7ec03dfe7173600ddd16d6a49ca304c4c46ec28794d5372bf29534f36ebaea18fa7eb8c2977dd981
7
- data.tar.gz: faf2abe7ed35a211fc210a3e72f90a2bf79a8affde8a35c96af2363fabb50091b1069aaea0074458bff1d56156d80e81cd8cb427e3c772b99dc81d318093f7a9
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.experiment_id || 0] = RuleInfo.new(feature_flag, rule)
89
- rule.variation_by_exposition.each do |variation|
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.experiment_id) if has_feature_flag_variable_js_css
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 'variation_exposition'
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, :experiment_id, :variation_by_exposition, :respool_time, :segment_id,
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
- end
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.experiment_id || 0] = forced_variation
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, var_by_exp, rule = get_variation_info(visitor_code, feature_flag, track)
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 = make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
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, var_by_exp, rule = get_variation_info(visitor_code, feature_flag, track)
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
- forced_variation = visitor&.get_forced_feature_variation(feature_key)
628
- if forced_variation
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
- forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
669
- if forced_variation
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
- var_by_exp = forced_variation.var_by_exp
923
- rule = forced_variation.rule
924
- else
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
- unless forced_variation&.simulated
928
- save_variation(visitor_code, rule, var_by_exp, track: track)
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.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)" \
933
- ' -> (variation_key: %s, variation_by_exposition: %s, rule: %s)',
934
- visitor_code, feature_flag, track, variation_key, var_by_exp, rule
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
- [variation_key, var_by_exp, rule]
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
- forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
949
- if forced_variation
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, rule, var_by_exp, track: true)
967
- experiment_id = rule&.experiment_id
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', rule: %s, var_by_exp: %s, track: %s)",
973
- visitor_code, rule, var_by_exp, track
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(experiment_id, variation_id, rule.type)
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', rule: %s, var_by_exp: %s, track: %s)",
981
- visitor_code, rule, var_by_exp, track
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
- selected_variation = nil
995
- selected_rule = nil
1020
+ selected = nil
996
1021
  feature_flag.rules.each do |rule|
997
- forced_variation = visitor&.get_forced_experiment_variation(rule.experiment_id || 0)
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
- selected_variation = forced_variation.var_by_exp
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.experiment_id, 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
- selected_variation = forced_variation.var_by_exp
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::HashDouble.obtain_rule(code_for_hash, rule.id, rule.respool_time)
1017
- Logging::KameleoonLogger.debug("Calculated hash_rule: %s for visitor_code: '%s'", hash_rule, code_for_hash)
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
- selected_variation = rule.first_variation
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::HashDouble.obtain_rule(code_for_hash, rule.experiment_id, rule.respool_time)
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
- selected_variation = variation
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
- '-> (variation: %s, rule: %s)', visitor_code, feature_flag, selected_variation, selected_rule
1061
+ '-> (eval_exp: %s)', visitor_code, feature_flag, selected
1045
1062
  )
1046
- [selected_variation, selected_rule]
1063
+ selected
1047
1064
  end
1048
1065
 
1049
- def _get_variation_key(var_by_exp, rule, feature_flag)
1050
- return var_by_exp.variation_key unless var_by_exp.nil?
1051
- return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.experimentation_type?
1052
-
1053
- feature_flag.default_variation_key
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 make_external_variation(internal_variation, variation_id, experiment_id)
1078
+ def create_external_variation(variation, eval_exp)
1057
1079
  Logging::KameleoonLogger.debug(
1058
- 'CALL: KameleoonClient.make_external_variation(internal_variation: %s, variation_id: %s, experiment_id: %s)',
1059
- internal_variation, variation_id, experiment_id
1080
+ 'CALL: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s)', variation, eval_exp
1060
1081
  )
1061
- variables = {}
1062
- internal_variation&.variables&.each do |variable|
1063
- variables[variable.key] = Types::Variable.new(
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
- variables.freeze
1070
- variation = Types::Variation.new(internal_variation&.key, variation_id, experiment_id, variables)
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.make_external_variation(internal_variation: %s, variation_id: %s, experiment_id: %s)' \
1073
- ' -> (variation: %s)',
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
- variation
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.experiment_id == experiment_id }
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.variation_by_exposition.find { |v| v.variation_id == variation_id }
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 if !rule.is_a?(Kameleoon::Configuration::Rule) || rule.experiment_id.nil?
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.experiment_id)
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(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s)",
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(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s) -> " \
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(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s) -> " \
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
@@ -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 HashDouble
36
- def self.obtain(visitor_code, respool_times = {}, container_id = '')
37
- obtain_helper(visitor_code, respool_times, container_id, '')
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.obtain_rule(visitor_code, container_id = '', suffix = '')
41
- obtain_helper(visitor_code, {}, container_id, suffix)
42
- end
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
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kameleoon
4
- SDK_VERSION = '3.7.0'
4
+ SDK_VERSION = '3.8.0'
5
5
  SDK_NAME = 'RUBY'
6
6
  end
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.7.0
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: 2024-12-16 00:00:00.000000000 Z
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