kameleoon-client-ruby 3.6.1 → 3.7.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 +12 -1
- data/lib/kameleoon/configuration/rule.rb +12 -1
- data/lib/kameleoon/data/data.rb +2 -0
- data/lib/kameleoon/data/manager/forced_experiment_variation.rb +25 -0
- data/lib/kameleoon/data/manager/forced_feature_variation.rb +28 -0
- data/lib/kameleoon/data/manager/forced_variation.rb +19 -0
- data/lib/kameleoon/data/manager/visitor.rb +99 -23
- data/lib/kameleoon/exceptions.rb +11 -4
- data/lib/kameleoon/kameleoon_client.rb +115 -27
- data/lib/kameleoon/network/cookie/cookie_manager.rb +123 -48
- data/lib/kameleoon/types/variation.rb +3 -1
- data/lib/kameleoon/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e0001c490d29beefab2f33c416affe19f6ccbc05d83e0aaad70be85070986e3
|
4
|
+
data.tar.gz: 6959eb4d8f421b9e14e62f3c490d5e1c6f038a25d3e5cb785c1832d7908c291d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfe9e75f3563287b6f187a38066a623145ba7ed916d12b9c7ec03dfe7173600ddd16d6a49ca304c4c46ec28794d5372bf29534f36ebaea18fa7eb8c2977dd981
|
7
|
+
data.tar.gz: faf2abe7ed35a211fc210a3e72f90a2bf79a8affde8a35c96af2363fabb50091b1069aaea0074458bff1d56156d80e81cd8cb427e3c772b99dc81d318093f7a9
|
@@ -9,7 +9,7 @@ module Kameleoon
|
|
9
9
|
module Configuration
|
10
10
|
class DataFile
|
11
11
|
attr_reader :settings, :feature_flags, :has_any_targeted_delivery_rule, :feature_flag_by_id, :rule_by_segment_id,
|
12
|
-
:variation_by_id, :custom_data_info, :experiment_ids_with_js_css_variable
|
12
|
+
:rule_info_by_exp_id, :variation_by_id, :custom_data_info, :experiment_ids_with_js_css_variable
|
13
13
|
|
14
14
|
def to_s
|
15
15
|
'DataFile{' \
|
@@ -74,6 +74,7 @@ module Kameleoon
|
|
74
74
|
def collect_indices
|
75
75
|
@feature_flag_by_id = {}
|
76
76
|
@rule_by_segment_id = {}
|
77
|
+
@rule_info_by_exp_id = {}
|
77
78
|
@variation_by_id = {}
|
78
79
|
@experiment_ids_with_js_css_variable = Set.new
|
79
80
|
|
@@ -84,6 +85,7 @@ module Kameleoon
|
|
84
85
|
has_feature_flag_variable_js_css = feature_flag_variable_js_css?(feature_flag)
|
85
86
|
feature_flag.rules.each do |rule|
|
86
87
|
@rule_by_segment_id[rule.segment_id] = rule
|
88
|
+
@rule_info_by_exp_id[rule.experiment_id || 0] = RuleInfo.new(feature_flag, rule)
|
87
89
|
rule.variation_by_exposition.each do |variation|
|
88
90
|
@variation_by_id[variation.variation_id] = variation
|
89
91
|
end
|
@@ -102,5 +104,14 @@ module Kameleoon
|
|
102
104
|
end
|
103
105
|
end
|
104
106
|
end
|
107
|
+
|
108
|
+
class RuleInfo
|
109
|
+
attr_reader :feature_flag, :rule
|
110
|
+
|
111
|
+
def initialize(feature_flag, rule)
|
112
|
+
@feature_flag = feature_flag
|
113
|
+
@rule = rule
|
114
|
+
end
|
115
|
+
end
|
105
116
|
end
|
106
117
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'variation_exposition'
|
4
|
+
require 'kameleoon/exceptions'
|
4
5
|
require 'kameleoon/targeting/models'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -29,7 +30,8 @@ module Kameleoon
|
|
29
30
|
|
30
31
|
# Rule is a class for new rules of feature flags
|
31
32
|
class Rule
|
32
|
-
attr_reader :id, :order, :type, :exposition, :experiment_id, :variation_by_exposition, :respool_time, :segment_id,
|
33
|
+
attr_reader :id, :order, :type, :exposition, :experiment_id, :variation_by_exposition, :respool_time, :segment_id,
|
34
|
+
:first_variation
|
33
35
|
attr_accessor :targeting_segment
|
34
36
|
|
35
37
|
def self.create_from_array(array)
|
@@ -63,6 +65,15 @@ module Kameleoon
|
|
63
65
|
nil
|
64
66
|
end
|
65
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
|
+
|
66
77
|
def get_variation_id_by_key(key)
|
67
78
|
variation_by_exposition.select { |v| v.variation_key == key }.first&.variation_id
|
68
79
|
end
|
data/lib/kameleoon/data/data.rb
CHANGED
@@ -14,6 +14,8 @@ module Kameleoon
|
|
14
14
|
DEVICE = 'DEVICE'
|
15
15
|
PAGE_VIEW = 'PAGE_VIEW'
|
16
16
|
ASSIGNED_VARIATION = 'ASSIGNED_VARIATION'
|
17
|
+
FORCED_EXPERIMENT_VARIATION = 'FORCED_EXPERIMENT_VARIATION'
|
18
|
+
FORCED_FEATURE_VARIATION = 'FORCED_FEATURE_VARIATION'
|
17
19
|
OPERATING_SYSTEM = 'OPERATING_SYSTEM'
|
18
20
|
GEOLOCATION = 'GEOLOCATION'
|
19
21
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/data/data'
|
4
|
+
require 'kameleoon/data/manager/forced_variation'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
module DataManager
|
8
|
+
class ForcedExperimentVariation < ForcedVariation
|
9
|
+
attr_reader :force_targeting
|
10
|
+
|
11
|
+
def initialize(rule, var_by_exp, force_targeting)
|
12
|
+
super(DataType::FORCED_EXPERIMENT_VARIATION, rule, var_by_exp)
|
13
|
+
@force_targeting = force_targeting
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
super(other) && other.is_a?(ForcedExperimentVariation) && (@force_targeting == other.force_targeting)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"ForcedExperimentVariation{rule:#{@rule},var_by_exp:#{@var_by_exp},force_targeting:#{@force_targeting}}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/data/data'
|
4
|
+
require 'kameleoon/data/manager/forced_variation'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
module DataManager
|
8
|
+
class ForcedFeatureVariation < ForcedVariation
|
9
|
+
attr_reader :feature_key, :simulated
|
10
|
+
|
11
|
+
def initialize(feature_key, rule, var_by_exp, simulated)
|
12
|
+
super(DataType::FORCED_FEATURE_VARIATION, rule, var_by_exp)
|
13
|
+
@feature_key = feature_key
|
14
|
+
@simulated = simulated
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
super(other) && other.is_a?(ForcedFeatureVariation) && \
|
19
|
+
(@feature_key == other.feature_key) && (@simulated == other.simulated)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"ForcedFeatureVariation{feature_key:'#{@feature_key}',rule:#{@rule}," \
|
24
|
+
"var_by_exp:#{@var_by_exp},simulated:#{@simulated}}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kameleoon
|
4
|
+
module DataManager
|
5
|
+
class ForcedVariation
|
6
|
+
attr_reader :instance, :rule, :var_by_exp, :rule_type, :assignment_time
|
7
|
+
|
8
|
+
def initialize(data_type, rule, var_by_exp)
|
9
|
+
@instance = data_type
|
10
|
+
@rule = rule
|
11
|
+
@var_by_exp = var_by_exp
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
(@rule == other.rule) && (@var_by_exp == other.var_by_exp)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -10,9 +10,11 @@ require 'kameleoon/data/page_view'
|
|
10
10
|
require 'kameleoon/data/unique_identifier'
|
11
11
|
require 'kameleoon/data/user_agent'
|
12
12
|
require 'kameleoon/data/manager/assigned_variation'
|
13
|
-
require 'kameleoon/data/manager/page_view_visit'
|
14
|
-
require 'kameleoon/data/manager/data_map_storage'
|
15
13
|
require 'kameleoon/data/manager/data_array_storage'
|
14
|
+
require 'kameleoon/data/manager/data_map_storage'
|
15
|
+
require 'kameleoon/data/manager/forced_experiment_variation'
|
16
|
+
require 'kameleoon/data/manager/forced_feature_variation'
|
17
|
+
require 'kameleoon/data/manager/page_view_visit'
|
16
18
|
require 'kameleoon/logging/kameleoon_logger'
|
17
19
|
|
18
20
|
module Kameleoon
|
@@ -163,38 +165,86 @@ module Kameleoon
|
|
163
165
|
variations
|
164
166
|
end
|
165
167
|
|
168
|
+
def get_forced_feature_variation(feature_key)
|
169
|
+
Logging::KameleoonLogger.debug("CALL: Visitor.get_forced_feature_variation(feature_key: '%s')", feature_key)
|
170
|
+
variation = @data.get_from_map(@data.simulated_variations, feature_key)
|
171
|
+
Logging::KameleoonLogger.debug(
|
172
|
+
"RETURN: Visitor.get_forced_feature_variation(feature_key: '%s') -> (variation: %s)",
|
173
|
+
feature_key, variation
|
174
|
+
)
|
175
|
+
variation
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_forced_experiment_variation(experiment_id)
|
179
|
+
Logging::KameleoonLogger.debug(
|
180
|
+
'CALL: Visitor.get_forced_experiment_variation(experiment_id: %d)', experiment_id
|
181
|
+
)
|
182
|
+
variation = @data.get_from_map(@data.forced_variations, experiment_id)
|
183
|
+
Logging::KameleoonLogger.debug(
|
184
|
+
'RETURN: Visitor.get_forced_experiment_variation(experiment_id: %d) -> (variation: %s)',
|
185
|
+
experiment_id, variation
|
186
|
+
)
|
187
|
+
variation
|
188
|
+
end
|
189
|
+
|
190
|
+
def reset_forced_experiment_variation(experiment_id)
|
191
|
+
Logging::KameleoonLogger.debug(
|
192
|
+
'CALL: Visitor.reset_forced_experiment_variation(experiment_id: %d)', experiment_id
|
193
|
+
)
|
194
|
+
@data.remove_from_map(@data.forced_variations, experiment_id)
|
195
|
+
Logging::KameleoonLogger.debug(
|
196
|
+
'RETURN: Visitor.reset_forced_experiment_variation(experiment_id: %d)', experiment_id
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
def update_simulated_variations(variations)
|
201
|
+
return if (@data.simulated_variations.nil? || @data.simulated_variations.empty?) && variations.empty?
|
202
|
+
|
203
|
+
Logging::KameleoonLogger.debug('CALL: Visitor.update_simulated_variations(variations: %s)', variations)
|
204
|
+
new_simulated_variations = {}
|
205
|
+
variations.each { |sv| new_simulated_variations[sv.feature_key] = sv }
|
206
|
+
@data.mutex.with_write_lock do
|
207
|
+
@data.simulated_variations = new_simulated_variations
|
208
|
+
end
|
209
|
+
Logging::KameleoonLogger.debug('RETURN: Visitor.update_simulated_variations(variations: %s)', variations)
|
210
|
+
end
|
211
|
+
|
166
212
|
def add_data(*args, overwrite: true)
|
167
213
|
Logging::KameleoonLogger.debug('CALL: Visitor.add_data(args: %s, overwrite: %s)', args, overwrite)
|
168
214
|
@data.mutex.with_write_lock do
|
169
215
|
args.each do |data|
|
170
216
|
case data
|
171
|
-
when
|
217
|
+
when UserAgent
|
172
218
|
@data.user_agent = data.value
|
173
|
-
when
|
219
|
+
when AssignedVariation
|
174
220
|
@data.add_variation(data, overwrite)
|
175
|
-
when
|
221
|
+
when ForcedFeatureVariation
|
222
|
+
@data.add_forced_feature_variation(data)
|
223
|
+
when ForcedExperimentVariation
|
224
|
+
@data.add_forced_experiment_variation(data)
|
225
|
+
when Device
|
176
226
|
@data.set_device(data, overwrite)
|
177
|
-
when
|
227
|
+
when Browser
|
178
228
|
@data.set_browser(data, overwrite)
|
179
|
-
when
|
229
|
+
when CustomData
|
180
230
|
@data.add_custom_data(data, overwrite)
|
181
|
-
when
|
231
|
+
when PageView
|
182
232
|
@data.add_page_view(data)
|
183
|
-
when
|
233
|
+
when PageViewVisit
|
184
234
|
@data.add_page_view_visit(data)
|
185
|
-
when
|
235
|
+
when Conversion
|
186
236
|
@data.add_conversion(data)
|
187
|
-
when
|
237
|
+
when Cookie
|
188
238
|
@data.cookie = data
|
189
|
-
when
|
239
|
+
when OperatingSystem
|
190
240
|
@data.set_operating_system(data, overwrite)
|
191
|
-
when
|
241
|
+
when Geolocation
|
192
242
|
@data.set_geolocation(data, overwrite)
|
193
|
-
when
|
243
|
+
when KcsHeat
|
194
244
|
@data.kcs_heat = data
|
195
|
-
when
|
245
|
+
when VisitorVisits
|
196
246
|
@data.visitor_visits = data
|
197
|
-
when
|
247
|
+
when UniqueIdentifier
|
198
248
|
@is_unique_identifier = data.value
|
199
249
|
else
|
200
250
|
Logging::KameleoonLogger.warning("Data has unsupported type '%s'", data.class)
|
@@ -218,7 +268,7 @@ module Kameleoon
|
|
218
268
|
class VisitorData
|
219
269
|
attr_reader :mutex, :device, :browser, :geolocation, :operating_system
|
220
270
|
attr_accessor :last_activity_time, :legal_consent, :user_agent, :cookie, :kcs_heat, :visitor_visits,
|
221
|
-
:mapping_identifier
|
271
|
+
:mapping_identifier, :forced_variations, :simulated_variations
|
222
272
|
|
223
273
|
def initialize
|
224
274
|
Logging::KameleoonLogger.debug('CALL: VisitorData.new')
|
@@ -227,6 +277,22 @@ module Kameleoon
|
|
227
277
|
Logging::KameleoonLogger.debug('RETURN: VisitorData.new')
|
228
278
|
end
|
229
279
|
|
280
|
+
def get_from_map(map, key)
|
281
|
+
return nil if map.nil?
|
282
|
+
|
283
|
+
@mutex.with_read_lock do
|
284
|
+
return map[key]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def remove_from_map(map, key)
|
289
|
+
return false if map.nil?
|
290
|
+
|
291
|
+
@mutex.with_write_lock do
|
292
|
+
return map.delete(key).nil?
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
230
296
|
def enumerate_sendable_data(&blk)
|
231
297
|
blk.call(@device) unless @device.nil?
|
232
298
|
blk.call(@browser) unless @browser.nil?
|
@@ -278,7 +344,7 @@ module Kameleoon
|
|
278
344
|
end
|
279
345
|
|
280
346
|
def add_variation(variation, overwrite)
|
281
|
-
@variations
|
347
|
+
@variations ||= {}
|
282
348
|
if overwrite || !@variations.include?(variation.experiment_id)
|
283
349
|
@variations[variation.experiment_id] = variation
|
284
350
|
end
|
@@ -289,7 +355,7 @@ module Kameleoon
|
|
289
355
|
end
|
290
356
|
|
291
357
|
def add_custom_data(custom_data, overwrite)
|
292
|
-
@custom_data_map
|
358
|
+
@custom_data_map ||= {}
|
293
359
|
if overwrite || !@custom_data_map.include?(custom_data.id)
|
294
360
|
@custom_data_map[custom_data.id] = custom_data
|
295
361
|
end
|
@@ -298,10 +364,10 @@ module Kameleoon
|
|
298
364
|
def add_page_view(page_view)
|
299
365
|
return if page_view.url.nil? || page_view.url.empty?
|
300
366
|
|
301
|
-
@page_view_visits
|
367
|
+
@page_view_visits ||= {}
|
302
368
|
visit = @page_view_visits[page_view.url]
|
303
369
|
if visit.nil?
|
304
|
-
visit =
|
370
|
+
visit = PageViewVisit.new(page_view)
|
305
371
|
else
|
306
372
|
visit.overwrite(page_view)
|
307
373
|
end
|
@@ -309,7 +375,7 @@ module Kameleoon
|
|
309
375
|
end
|
310
376
|
|
311
377
|
def add_page_view_visit(page_view_visit)
|
312
|
-
@page_view_visits
|
378
|
+
@page_view_visits ||= {}
|
313
379
|
visit = @page_view_visits[page_view_visit.page_view.url]
|
314
380
|
if visit.nil?
|
315
381
|
visit = page_view_visit
|
@@ -320,7 +386,7 @@ module Kameleoon
|
|
320
386
|
end
|
321
387
|
|
322
388
|
def add_conversion(conversion)
|
323
|
-
@conversions
|
389
|
+
@conversions ||= []
|
324
390
|
@conversions.push(conversion)
|
325
391
|
end
|
326
392
|
|
@@ -331,6 +397,16 @@ module Kameleoon
|
|
331
397
|
def set_operating_system(operating_system, overwrite)
|
332
398
|
@operating_system = operating_system if overwrite || @operating_system.nil?
|
333
399
|
end
|
400
|
+
|
401
|
+
def add_forced_feature_variation(forced_variation)
|
402
|
+
@simulated_variations ||= {}
|
403
|
+
@simulated_variations[forced_variation.feature_key] = forced_variation
|
404
|
+
end
|
405
|
+
|
406
|
+
def add_forced_experiment_variation(forced_variation)
|
407
|
+
@forced_variations ||= {}
|
408
|
+
@forced_variations[forced_variation.rule.experiment_id || 0] = forced_variation
|
409
|
+
end
|
334
410
|
end
|
335
411
|
end
|
336
412
|
end
|
data/lib/kameleoon/exceptions.rb
CHANGED
@@ -30,10 +30,10 @@ module Kameleoon
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
# Feature
|
34
|
-
class
|
35
|
-
def initialize(
|
36
|
-
super("
|
33
|
+
# Feature Experiment Not Found
|
34
|
+
class FeatureExperimentNotFound < FeatureError
|
35
|
+
def initialize(id = '')
|
36
|
+
super("Experiment #{id}")
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -44,6 +44,13 @@ module Kameleoon
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
# Feature Variable Not Found
|
48
|
+
class FeatureVariableNotFound < FeatureError
|
49
|
+
def initialize(key = '')
|
50
|
+
super("Feature variable #{key}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
47
54
|
# Feature Environment Disabled
|
48
55
|
class FeatureEnvironmentDisabled < FeatureError
|
49
56
|
def initialize(feature_key, environment = nil)
|
@@ -7,6 +7,7 @@ require 'kameleoon/configuration/data_file'
|
|
7
7
|
require 'kameleoon/data/custom_data'
|
8
8
|
require 'kameleoon/data/user_agent'
|
9
9
|
require 'kameleoon/data/manager/assigned_variation'
|
10
|
+
require 'kameleoon/data/manager/forced_experiment_variation'
|
10
11
|
require 'kameleoon/data/manager/visitor_manager'
|
11
12
|
require 'kameleoon/exceptions'
|
12
13
|
require 'kameleoon/hybrid/manager'
|
@@ -75,7 +76,7 @@ module Kameleoon
|
|
75
76
|
@remote_data_manager = Managers::RemoteData::RemoteDataManager.new(
|
76
77
|
@data_manager, @network_manager, @visitor_manager
|
77
78
|
)
|
78
|
-
@cookie_manager = Network::Cookie::CookieManager.new(@data_manager, config.top_level_domain)
|
79
|
+
@cookie_manager = Network::Cookie::CookieManager.new(@data_manager, @visitor_manager, config.top_level_domain)
|
79
80
|
@readiness = ClientReadiness.new
|
80
81
|
@targeting_manager = Targeting::TargetingManager.new(@data_manager, @visitor_manager)
|
81
82
|
|
@@ -611,16 +612,25 @@ module Kameleoon
|
|
611
612
|
#
|
612
613
|
# DEPRECATED. Please use `get_active_features` instead.
|
613
614
|
def get_active_feature_list_for_visitor(visitor_code)
|
614
|
-
Logging::KameleoonLogger.info(
|
615
|
-
|
616
|
-
|
617
|
-
|
615
|
+
Logging::KameleoonLogger.info(
|
616
|
+
'[DEPRECATION] `get_active_feature_list_for_visitor` is deprecated. Please use `get_active_features` instead.'
|
617
|
+
)
|
618
|
+
Logging::KameleoonLogger.info(
|
619
|
+
"CALL: KameleoonClient.get_active_feature_list_for_visitor(visitor_code: '%s')", visitor_code
|
620
|
+
)
|
618
621
|
Utils::VisitorCode.validate(visitor_code)
|
622
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
619
623
|
list_keys = []
|
620
624
|
@data_manager.data_file.feature_flags.each do |feature_key, feature_flag|
|
621
625
|
next unless feature_flag.environment_enabled
|
622
626
|
|
623
|
-
|
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
|
624
634
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
625
635
|
list_keys.push(feature_key) if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
626
636
|
end
|
@@ -649,12 +659,19 @@ module Kameleoon
|
|
649
659
|
)
|
650
660
|
Logging::KameleoonLogger.info("CALL: KameleoonClient.get_active_features(visitor_code: '%s')", visitor_code)
|
651
661
|
Utils::VisitorCode.validate(visitor_code)
|
662
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
652
663
|
map_active_features = {}
|
653
664
|
|
654
665
|
@data_manager.data_file.feature_flags.each_value do |feature_flag|
|
655
666
|
next unless feature_flag.environment_enabled
|
656
667
|
|
657
|
-
|
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
|
658
675
|
variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
|
659
676
|
|
660
677
|
next if variation_key == Configuration::VariationType::VARIATION_OFF
|
@@ -701,6 +718,52 @@ module Kameleoon
|
|
701
718
|
engine_tracking_code
|
702
719
|
end
|
703
720
|
|
721
|
+
##
|
722
|
+
# Sets or resets a forced variation for a visitor in a specific experiment,
|
723
|
+
# so the experiment will be evaluated to the variation for the visitor.
|
724
|
+
#
|
725
|
+
# In order to reset the forced variation set the `variation_key` parameter to `nil`.
|
726
|
+
# If the forced variation you want to reset does not exist, the method will have no effect.
|
727
|
+
#
|
728
|
+
# @param [String] visitor_code The unique visitor code identifying the visitor.
|
729
|
+
# @param [Integer] experiment_id The identifier of the experiment you want to set/reset the forced variation for.
|
730
|
+
# @param [String | NilClass] variation_key The identifier of the variation you want the experiment to be evaluated
|
731
|
+
# to. Set to `nil` to reset the forced variation.
|
732
|
+
# @param [Bool] force_targeting If `true`, the visitor will be targeted to the experiment regardless its
|
733
|
+
# conditions. Otherwise, the normal targeting logic will be preserved. Optional (defaults to `true`).
|
734
|
+
#
|
735
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] The provided **visitor code** is invalid.
|
736
|
+
# @raise [Kameleoon::Exception::FeatureExperimentNotFound] The provided **experiment id** does not exist in
|
737
|
+
# the feature flag.
|
738
|
+
# @raise [Kameleoon::Exception::FeatureVariationNotFound] The provided **variation key** does not belong to
|
739
|
+
# the experiment.
|
740
|
+
def set_forced_variation(visitor_code, experiment_id, variation_key, force_targeting: true)
|
741
|
+
Logging::KameleoonLogger.info(
|
742
|
+
"CALL: KameleoonClient.set_forced_variation(visitor_code: '%s', experiment_id: %d, variation_key: %s, " \
|
743
|
+
'force_targeting: %s)',
|
744
|
+
visitor_code, experiment_id, variation_key.nil? ? 'nil' : "'#{variation_key}'", force_targeting
|
745
|
+
)
|
746
|
+
Utils::VisitorCode.validate(visitor_code)
|
747
|
+
if variation_key.nil?
|
748
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
749
|
+
visitor&.reset_forced_experiment_variation(experiment_id)
|
750
|
+
else
|
751
|
+
rule_info = @data_manager.data_file.rule_info_by_exp_id[experiment_id]
|
752
|
+
if rule_info.nil?
|
753
|
+
raise Exception::FeatureExperimentNotFound.new(experiment_id), "Experiment #{experiment_id} is not found"
|
754
|
+
end
|
755
|
+
|
756
|
+
var_by_exp = rule_info.rule.get_variation_by_key(variation_key)
|
757
|
+
forced_variation = DataManager::ForcedExperimentVariation.new(rule_info.rule, var_by_exp, force_targeting)
|
758
|
+
@visitor_manager.add_data(visitor_code, forced_variation)
|
759
|
+
end
|
760
|
+
Logging::KameleoonLogger.info(
|
761
|
+
"RETURN: KameleoonClient.set_forced_variation(visitor_code: '%s', experiment_id: %d, variation_key: %s, " \
|
762
|
+
'force_targeting: %s)',
|
763
|
+
visitor_code, experiment_id, variation_key.nil? ? 'nil' : "'#{variation_key}'", force_targeting
|
764
|
+
)
|
765
|
+
end
|
766
|
+
|
704
767
|
private
|
705
768
|
|
706
769
|
HYBRID_EXPIRATION_TIME = 5
|
@@ -853,9 +916,18 @@ module Kameleoon
|
|
853
916
|
"CALL: KameleoonClient.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)",
|
854
917
|
visitor_code, feature_flag, track
|
855
918
|
)
|
856
|
-
|
919
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
920
|
+
forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
|
921
|
+
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)
|
926
|
+
end
|
927
|
+
unless forced_variation&.simulated
|
928
|
+
save_variation(visitor_code, rule, var_by_exp, track: track)
|
929
|
+
end
|
857
930
|
variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
|
858
|
-
save_variation(visitor_code, rule, var_by_exp, track: track)
|
859
931
|
Logging::KameleoonLogger.debug(
|
860
932
|
"RETURN: KameleoonClient.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)" \
|
861
933
|
' -> (variation_key: %s, variation_by_exposition: %s, rule: %s)',
|
@@ -872,9 +944,16 @@ module Kameleoon
|
|
872
944
|
visitor_code, feature_key
|
873
945
|
)
|
874
946
|
feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
|
875
|
-
|
947
|
+
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
|
876
956
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
877
|
-
save_variation(visitor_code, rule, variation)
|
878
957
|
@tracking_manager.add_visitor_code(visitor_code)
|
879
958
|
Logging::KameleoonLogger.debug(
|
880
959
|
"RETURN: KameleoonClient._get_feature_variation_key(visitor_code: '%s', feature_key: '%s')" \
|
@@ -910,28 +989,39 @@ module Kameleoon
|
|
910
989
|
"CALL: KameleoonClient._calculate_variation_key_for_feature(visitor_code: '%s', feature_flag: %s)",
|
911
990
|
visitor_code, feature_flag
|
912
991
|
)
|
992
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
913
993
|
# no rules -> return default_variation_key
|
994
|
+
selected_variation = nil
|
995
|
+
selected_rule = nil
|
914
996
|
feature_flag.rules.each do |rule|
|
997
|
+
forced_variation = visitor&.get_forced_experiment_variation(rule.experiment_id || 0)
|
998
|
+
if forced_variation&.force_targeting
|
999
|
+
# Forcing experiment variation in force-targeting mode
|
1000
|
+
selected_variation = forced_variation.var_by_exp
|
1001
|
+
selected_rule = rule
|
1002
|
+
break
|
1003
|
+
end
|
915
1004
|
# check if visitor is targeted for rule, else next rule
|
916
1005
|
next unless check_targeting(visitor_code, rule.experiment_id, rule)
|
917
1006
|
|
918
|
-
|
1007
|
+
unless forced_variation.nil?
|
1008
|
+
# Forcing experiment variation in targeting-only mode
|
1009
|
+
selected_variation = forced_variation.var_by_exp
|
1010
|
+
selected_rule = rule
|
1011
|
+
break
|
1012
|
+
end
|
919
1013
|
# use mappingIdentifier instead of visitorCode if it was set up
|
920
|
-
code_for_hash =
|
1014
|
+
code_for_hash = visitor&.mapping_identifier || visitor_code
|
921
1015
|
# uses for rule exposition
|
922
1016
|
hash_rule = Utils::HashDouble.obtain_rule(code_for_hash, rule.id, rule.respool_time)
|
923
1017
|
Logging::KameleoonLogger.debug("Calculated hash_rule: %s for visitor_code: '%s'", hash_rule, code_for_hash)
|
924
1018
|
# check main expostion for rule with hashRule
|
925
1019
|
if hash_rule <= rule.exposition
|
926
1020
|
if rule.targeted_delivery_type?
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
visitor_code, feature_flag, rule.first_variation, rule
|
931
|
-
)
|
932
|
-
return [rule.first_variation, rule]
|
1021
|
+
selected_variation = rule.first_variation
|
1022
|
+
selected_rule = rule
|
1023
|
+
break
|
933
1024
|
end
|
934
|
-
|
935
1025
|
# uses for variation's expositions
|
936
1026
|
hash_variation = Utils::HashDouble.obtain_rule(code_for_hash, rule.experiment_id, rule.respool_time)
|
937
1027
|
Logging::KameleoonLogger.debug(
|
@@ -940,11 +1030,9 @@ module Kameleoon
|
|
940
1030
|
# get variation key with new hashVariation
|
941
1031
|
variation = rule.get_variation(hash_variation)
|
942
1032
|
unless variation.nil?
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
)
|
947
|
-
return [variation, rule]
|
1033
|
+
selected_variation = variation
|
1034
|
+
selected_rule = rule
|
1035
|
+
break
|
948
1036
|
end
|
949
1037
|
# if visitor is targeted for targeted rule then break cycle -> return default
|
950
1038
|
elsif rule.targeted_delivery_type?
|
@@ -953,9 +1041,9 @@ module Kameleoon
|
|
953
1041
|
end
|
954
1042
|
Logging::KameleoonLogger.debug(
|
955
1043
|
"RETURN: KameleoonClient._calculate_variation_key_for_feature(visitor_code: '%s', feature_flag: %s) " \
|
956
|
-
'-> (variation:
|
1044
|
+
'-> (variation: %s, rule: %s)', visitor_code, feature_flag, selected_variation, selected_rule
|
957
1045
|
)
|
958
|
-
[
|
1046
|
+
[selected_variation, selected_rule]
|
959
1047
|
end
|
960
1048
|
|
961
1049
|
def _get_variation_key(var_by_exp, rule, feature_flag)
|
@@ -1,21 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/
|
3
|
+
require 'kameleoon/data/manager/forced_feature_variation'
|
4
4
|
require 'kameleoon/logging/kameleoon_logger'
|
5
|
+
require 'kameleoon/utils'
|
5
6
|
|
6
7
|
module Kameleoon
|
7
8
|
module Network
|
8
9
|
module Cookie
|
9
|
-
COOKIE_KEY_JS = '_js_'
|
10
10
|
VISITOR_CODE_COOKIE = 'kameleoonVisitorCode'
|
11
|
+
KAMELEOON_SIMULATION_FF_DATA = 'kameleoonSimulationFFData'
|
12
|
+
EXPERIMENT_ID_KEY = 'expId'
|
13
|
+
VARIATION_ID_KEY = 'varId'
|
11
14
|
COOKIE_TTL_SECONDS = 380 * 86_400 # 380 days in seconds
|
12
15
|
|
13
16
|
class CookieManager
|
14
|
-
def initialize(data_manager, top_level_domain)
|
15
|
-
Logging::KameleoonLogger.debug(
|
17
|
+
def initialize(data_manager, visitor_manager, top_level_domain)
|
18
|
+
Logging::KameleoonLogger.debug(
|
19
|
+
"CALL: CookieManager.new(data_manager, visitor_manager, top_level_domain: '%s')", top_level_domain
|
20
|
+
)
|
16
21
|
@data_manager = data_manager
|
22
|
+
@visitor_manager = visitor_manager
|
17
23
|
@top_level_domain = top_level_domain
|
18
|
-
Logging::KameleoonLogger.debug(
|
24
|
+
Logging::KameleoonLogger.debug(
|
25
|
+
"RETURN: CookieManager.new(data_manager, visitor_manager, top_level_domain: '%s')", top_level_domain
|
26
|
+
)
|
19
27
|
end
|
20
28
|
|
21
29
|
def get_or_add(cookies, default_visitor_code = nil)
|
@@ -25,35 +33,8 @@ module Kameleoon
|
|
25
33
|
"CALL: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s')",
|
26
34
|
cookies, default_visitor_code
|
27
35
|
)
|
28
|
-
|
29
|
-
|
30
|
-
unless visitor_code.nil?
|
31
|
-
Utils::VisitorCode.validate(visitor_code)
|
32
|
-
Logging::KameleoonLogger.debug("Read visitor code '%s' from cookies %s", visitor_code, cookies)
|
33
|
-
# Remove adding cookies when we will be sure that it doesn't break anything
|
34
|
-
add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
|
35
|
-
Logging::KameleoonLogger.debug(
|
36
|
-
"RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
|
37
|
-
cookies, default_visitor_code, visitor_code
|
38
|
-
)
|
39
|
-
return visitor_code
|
40
|
-
end
|
41
|
-
|
42
|
-
if default_visitor_code.nil?
|
43
|
-
visitor_code = Utils::VisitorCode.generate
|
44
|
-
Logging::KameleoonLogger.debug("Generated new visitor code '%s'", visitor_code)
|
45
|
-
add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
|
46
|
-
Logging::KameleoonLogger.debug(
|
47
|
-
"RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
|
48
|
-
cookies, default_visitor_code, visitor_code
|
49
|
-
)
|
50
|
-
return visitor_code
|
51
|
-
end
|
52
|
-
|
53
|
-
visitor_code = default_visitor_code
|
54
|
-
Utils::VisitorCode.validate(visitor_code)
|
55
|
-
Logging::KameleoonLogger.debug("Used default visitor code '{%s}'", default_visitor_code)
|
56
|
-
add(visitor_code, cookies)
|
36
|
+
visitor_code = get_or_add_visitor_code(cookies, default_visitor_code)
|
37
|
+
process_simulated_variations(cookies, visitor_code)
|
57
38
|
Logging::KameleoonLogger.debug(
|
58
39
|
"RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
|
59
40
|
cookies, default_visitor_code, visitor_code
|
@@ -78,9 +59,34 @@ module Kameleoon
|
|
78
59
|
|
79
60
|
private
|
80
61
|
|
62
|
+
def get_or_add_visitor_code(cookies, default_visitor_code)
|
63
|
+
visitor_code = get_value_from_cookies(cookies, VISITOR_CODE_COOKIE)
|
64
|
+
unless visitor_code.nil?
|
65
|
+
Utils::VisitorCode.validate(visitor_code)
|
66
|
+
Logging::KameleoonLogger.debug("Read visitor code '%s' from cookies %s", visitor_code, cookies)
|
67
|
+
# Remove adding cookies when we will be sure that it doesn't break anything
|
68
|
+
add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
|
69
|
+
return visitor_code
|
70
|
+
end
|
71
|
+
|
72
|
+
if default_visitor_code.nil?
|
73
|
+
visitor_code = Utils::VisitorCode.generate
|
74
|
+
Logging::KameleoonLogger.debug("Generated new visitor code '%s'", visitor_code)
|
75
|
+
add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
|
76
|
+
return visitor_code
|
77
|
+
end
|
78
|
+
|
79
|
+
visitor_code = default_visitor_code
|
80
|
+
Utils::VisitorCode.validate(visitor_code)
|
81
|
+
Logging::KameleoonLogger.debug("Used default visitor code '{%s}'", default_visitor_code)
|
82
|
+
add(visitor_code, cookies)
|
83
|
+
visitor_code
|
84
|
+
end
|
85
|
+
|
81
86
|
def add(visitor_code, cookies)
|
82
|
-
Logging::KameleoonLogger.debug(
|
83
|
-
|
87
|
+
Logging::KameleoonLogger.debug(
|
88
|
+
"CALL: CookieManager.add(visitor_code: '%s', cookies: %s)", visitor_code, cookies
|
89
|
+
)
|
84
90
|
cookie = {
|
85
91
|
value: visitor_code,
|
86
92
|
expires: Time.now + COOKIE_TTL_SECONDS,
|
@@ -88,8 +94,9 @@ module Kameleoon
|
|
88
94
|
domain: @top_level_domain
|
89
95
|
}
|
90
96
|
cookies[VISITOR_CODE_COOKIE] = cookie
|
91
|
-
Logging::KameleoonLogger.debug(
|
92
|
-
|
97
|
+
Logging::KameleoonLogger.debug(
|
98
|
+
"RETURN: CookieManager.add(visitor_code: '%s', cookies: %s)", visitor_code, cookies
|
99
|
+
)
|
93
100
|
cookies
|
94
101
|
end
|
95
102
|
|
@@ -100,22 +107,90 @@ module Kameleoon
|
|
100
107
|
cookies
|
101
108
|
end
|
102
109
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
110
|
+
def process_simulated_variations(cookies, visitor_code)
|
111
|
+
raw = get_value_from_cookies(cookies, KAMELEOON_SIMULATION_FF_DATA)
|
112
|
+
return if raw.nil?
|
113
|
+
|
114
|
+
variations = parse_simulated_variations(raw)
|
115
|
+
visitor = @visitor_manager.get_or_create_visitor(visitor_code)
|
116
|
+
visitor.update_simulated_variations(variations)
|
117
|
+
rescue StandardError => e
|
118
|
+
Logging::KameleoonLogger.error('Failed to process simulated variations cookie: %s', e)
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_simulated_variations(raw)
|
122
|
+
data_file = @data_manager.data_file
|
123
|
+
jobj = JSON.parse(raw)
|
124
|
+
return nil unless jobj.is_a?(Hash)
|
125
|
+
|
126
|
+
variations = []
|
127
|
+
jobj.each do |feature_key, value|
|
128
|
+
unless feature_key.is_a?(String) && value.is_a?(Hash)
|
129
|
+
log_malformed_simulated_variations_cookie(raw)
|
130
|
+
next
|
131
|
+
end
|
132
|
+
|
133
|
+
experiment_id = value[EXPERIMENT_ID_KEY]
|
134
|
+
if !experiment_id.is_a?(Integer) || experiment_id.negative?
|
135
|
+
log_malformed_simulated_variations_cookie(raw)
|
136
|
+
next
|
137
|
+
end
|
138
|
+
|
139
|
+
unless experiment_id.zero?
|
140
|
+
variation_id = value[VARIATION_ID_KEY]
|
141
|
+
if !variation_id.is_a?(Integer) || variation_id.negative?
|
142
|
+
log_malformed_simulated_variations_cookie(raw)
|
143
|
+
next
|
144
|
+
end
|
145
|
+
end
|
146
|
+
simulated_variation = simulated_variation_from_data_file(
|
147
|
+
data_file, feature_key, experiment_id, variation_id
|
148
|
+
)
|
149
|
+
variations.push(simulated_variation) unless simulated_variation.nil?
|
150
|
+
end
|
151
|
+
variations
|
152
|
+
end
|
153
|
+
|
154
|
+
def log_malformed_simulated_variations_cookie(raw)
|
155
|
+
Logging::KameleoonLogger.error('Malformed simulated variations cookie: %s', raw)
|
156
|
+
end
|
157
|
+
|
158
|
+
def simulated_variation_from_data_file(data_file, feature_key, experiment_id, variation_id)
|
159
|
+
feature_flag = data_file.feature_flags[feature_key]
|
160
|
+
unless feature_flag
|
161
|
+
Logging::KameleoonLogger.error("Simulated feature flag '%s' is not found", feature_key)
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
return DataManager::ForcedFeatureVariation.new(feature_key, nil, nil, true) if experiment_id.zero?
|
165
|
+
|
166
|
+
rule = feature_flag.rules.find { |r| r.experiment_id == experiment_id }
|
167
|
+
unless rule
|
168
|
+
Logging::KameleoonLogger.error('Simulated experiment %d is not found', experiment_id)
|
169
|
+
return nil
|
170
|
+
end
|
171
|
+
var_by_exp = rule.variation_by_exposition.find { |v| v.variation_id == variation_id }
|
172
|
+
unless var_by_exp
|
173
|
+
Logging::KameleoonLogger.error('Simulated variation %d is not found', variation_id)
|
174
|
+
return nil
|
175
|
+
end
|
176
|
+
DataManager::ForcedFeatureVariation.new(feature_key, rule, var_by_exp, true)
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_value_from_cookies(cookies, key)
|
180
|
+
Logging::KameleoonLogger.debug('CALL: CookieManager.get_value_from_cookies(cookies: %s)', cookies)
|
181
|
+
cookie = cookies[key]
|
106
182
|
case cookie
|
107
183
|
when String
|
108
|
-
|
184
|
+
value = cookie
|
109
185
|
when Hash
|
110
|
-
|
186
|
+
value = cookie[:value]
|
111
187
|
end
|
112
|
-
|
113
|
-
visitor_code = nil if visitor_code&.empty?
|
188
|
+
value = nil if value&.empty?
|
114
189
|
Logging::KameleoonLogger.debug(
|
115
|
-
"RETURN: CookieManager.
|
116
|
-
cookies,
|
190
|
+
"RETURN: CookieManager.get_value_from_cookies(cookies: %s) -> (value: '%s')",
|
191
|
+
cookies, value
|
117
192
|
)
|
118
|
-
|
193
|
+
value
|
119
194
|
end
|
120
195
|
end
|
121
196
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'kameleoon/configuration/variation'
|
4
|
+
|
3
5
|
module Kameleoon
|
4
6
|
# Module which contains all internal data of SDK
|
5
7
|
module Types
|
@@ -19,7 +21,7 @@ module Kameleoon
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def active?
|
22
|
-
|
24
|
+
@key != Configuration::VariationType::VARIATION_OFF
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
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.7.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-
|
11
|
+
date: 2024-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -95,6 +95,9 @@ files:
|
|
95
95
|
- lib/kameleoon/data/manager/assigned_variation.rb
|
96
96
|
- lib/kameleoon/data/manager/data_array_storage.rb
|
97
97
|
- lib/kameleoon/data/manager/data_map_storage.rb
|
98
|
+
- lib/kameleoon/data/manager/forced_experiment_variation.rb
|
99
|
+
- lib/kameleoon/data/manager/forced_feature_variation.rb
|
100
|
+
- lib/kameleoon/data/manager/forced_variation.rb
|
98
101
|
- lib/kameleoon/data/manager/page_view_visit.rb
|
99
102
|
- lib/kameleoon/data/manager/visitor.rb
|
100
103
|
- lib/kameleoon/data/manager/visitor_manager.rb
|