kameleoon-client-ruby 2.3.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kameleoon/client_readiness.rb +40 -0
- data/lib/kameleoon/configuration/data_file.rb +41 -0
- data/lib/kameleoon/configuration/feature_flag.rb +3 -2
- data/lib/kameleoon/configuration/rule.rb +18 -3
- data/lib/kameleoon/configuration/settings.rb +3 -1
- data/lib/kameleoon/configuration/variable.rb +0 -2
- data/lib/kameleoon/configuration/variation_exposition.rb +0 -2
- data/lib/kameleoon/data/browser.rb +1 -1
- data/lib/kameleoon/data/conversion.rb +1 -1
- data/lib/kameleoon/data/custom_data.rb +10 -14
- data/lib/kameleoon/data/data.rb +22 -3
- data/lib/kameleoon/data/device.rb +2 -1
- data/lib/kameleoon/data/manager/assigned_variation.rb +38 -0
- data/lib/kameleoon/data/manager/data_array_storage.rb +43 -0
- data/lib/kameleoon/data/manager/data_map_storage.rb +43 -0
- data/lib/kameleoon/data/manager/page_view_visit.rb +19 -0
- data/lib/kameleoon/data/manager/visitor.rb +142 -0
- data/lib/kameleoon/data/manager/visitor_manager.rb +71 -0
- data/lib/kameleoon/data/page_view.rb +2 -1
- data/lib/kameleoon/exceptions.rb +30 -35
- data/lib/kameleoon/hybrid/manager.rb +13 -31
- data/lib/kameleoon/{client.rb → kameleoon_client.rb} +164 -336
- data/lib/kameleoon/kameleoon_client_config.rb +91 -0
- data/lib/kameleoon/kameleoon_client_factory.rb +42 -0
- data/lib/kameleoon/network/activity_event.rb +6 -3
- data/lib/kameleoon/network/cookie/cookie_manager.rb +84 -0
- data/lib/kameleoon/network/net_provider.rb +5 -37
- data/lib/kameleoon/network/network_manager.rb +8 -7
- data/lib/kameleoon/network/request.rb +3 -2
- data/lib/kameleoon/network/response.rb +0 -8
- data/lib/kameleoon/network/url_provider.rb +5 -3
- data/lib/kameleoon/targeting/conditions/browser_condition.rb +2 -3
- data/lib/kameleoon/targeting/conditions/conversion_condition.rb +12 -4
- data/lib/kameleoon/targeting/conditions/custom_datum.rb +19 -13
- data/lib/kameleoon/targeting/conditions/device_condition.rb +3 -4
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +2 -1
- data/lib/kameleoon/targeting/conditions/page_title_condition.rb +11 -4
- data/lib/kameleoon/targeting/conditions/page_url_condition.rb +18 -4
- data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -0
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +11 -6
- data/lib/kameleoon/utils.rb +41 -4
- data/lib/kameleoon/version.rb +1 -1
- data/lib/kameleoon.rb +4 -2
- metadata +14 -10
- data/lib/kameleoon/client_config.rb +0 -44
- data/lib/kameleoon/configuration/experiment.rb +0 -42
- data/lib/kameleoon/cookie.rb +0 -88
- data/lib/kameleoon/factory.rb +0 -43
- data/lib/kameleoon/network/experiment_event.rb +0 -35
- data/lib/kameleoon/storage/variation_storage.rb +0 -42
- data/lib/kameleoon/storage/visitor_variation.rb +0 -20
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kameleoon
|
4
|
+
# KameleoonClient configuration which can be used instead of an external configuration file
|
5
|
+
class KameleoonClientConfig
|
6
|
+
DEFAULT_REFRESH_INTERVAL_MINUTES = 60
|
7
|
+
DEFAULT_SESSION_DURATION_MINUTES = 30
|
8
|
+
DEFAULT_TIMEOUT_MILLISECONDS = 10_000
|
9
|
+
|
10
|
+
attr_reader :client_id, :client_secret, :refresh_interval_second, :session_duration_second,
|
11
|
+
:default_timeout_millisecond, :environment, :top_level_domain, :verbose_mode
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
client_id,
|
15
|
+
client_secret,
|
16
|
+
refresh_interval_minute: DEFAULT_REFRESH_INTERVAL_MINUTES,
|
17
|
+
session_duration_minute: DEFAULT_SESSION_DURATION_MINUTES,
|
18
|
+
default_timeout_millisecond: DEFAULT_TIMEOUT_MILLISECONDS,
|
19
|
+
environment: nil,
|
20
|
+
top_level_domain: nil,
|
21
|
+
verbose_mode: false
|
22
|
+
)
|
23
|
+
raise Exception::ConfigCredentialsInvalid, 'Client ID is not specified' if client_id&.empty? != false
|
24
|
+
raise Exception::ConfigCredentialsInvalid, 'Client secret is not specified' if client_secret&.empty? != false
|
25
|
+
|
26
|
+
@verbose_mode = verbose_mode || false
|
27
|
+
|
28
|
+
@client_id = client_id
|
29
|
+
@client_secret = client_secret
|
30
|
+
|
31
|
+
if refresh_interval_minute.nil?
|
32
|
+
refresh_interval_minute = DEFAULT_REFRESH_INTERVAL_MINUTES
|
33
|
+
elsif refresh_interval_minute <= 0
|
34
|
+
log('Configuration refresh interval must have positive value. ' \
|
35
|
+
"Default refresh interval (#{DEFAULT_REFRESH_INTERVAL_MINUTES} minutes) is applied.")
|
36
|
+
refresh_interval_minute = DEFAULT_REFRESH_INTERVAL_MINUTES
|
37
|
+
end
|
38
|
+
@refresh_interval_second = refresh_interval_minute * 60
|
39
|
+
|
40
|
+
if session_duration_minute.nil?
|
41
|
+
session_duration_minute = DEFAULT_SESSION_DURATION_MINUTES
|
42
|
+
elsif session_duration_minute <= 0
|
43
|
+
log('Session duration must have positive value. ' \
|
44
|
+
"Default session duration (#{DEFAULT_SESSION_DURATION_MINUTES} minutes) is applied.")
|
45
|
+
session_duration_minute = DEFAULT_SESSION_DURATION_MINUTES
|
46
|
+
end
|
47
|
+
@session_duration_second = session_duration_minute * 60
|
48
|
+
|
49
|
+
if default_timeout_millisecond.nil?
|
50
|
+
@default_timeout_millisecond = DEFAULT_TIMEOUT_MILLISECONDS
|
51
|
+
elsif default_timeout_millisecond <= 0
|
52
|
+
log('Default timeout must have positive value. ' \
|
53
|
+
"Default timeout (#{DEFAULT_TIMEOUT_MILLISECONDS} ms) is applied.")
|
54
|
+
@default_timeout_millisecond = DEFAULT_TIMEOUT_MILLISECONDS
|
55
|
+
else
|
56
|
+
@default_timeout_millisecond = default_timeout_millisecond
|
57
|
+
end
|
58
|
+
|
59
|
+
@environment = environment
|
60
|
+
|
61
|
+
if top_level_domain.nil?
|
62
|
+
log('Setting top level domain is strictly recommended, otherwise you may have problems when using subdomains.')
|
63
|
+
end
|
64
|
+
@top_level_domain = top_level_domain
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.read_from_yaml(path)
|
68
|
+
yaml = YAML.load_file(path) if File.exist?(path)
|
69
|
+
if yaml.nil?
|
70
|
+
warn "Kameleoon SDK: Configuration file with path #{path} does not exist"
|
71
|
+
yaml = {}
|
72
|
+
end
|
73
|
+
KameleoonClientConfig.new(
|
74
|
+
yaml['client_id'],
|
75
|
+
yaml['client_secret'],
|
76
|
+
refresh_interval_minute: yaml['refresh_interval_minute'],
|
77
|
+
session_duration_minute: yaml['session_duration_minute'],
|
78
|
+
default_timeout_millisecond: yaml['default_timeout_millisecond'],
|
79
|
+
environment: yaml['environment'],
|
80
|
+
top_level_domain: yaml['top_level_domain'],
|
81
|
+
verbose_mode: yaml['verbose_mode']
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def log(text)
|
88
|
+
print "Kameleoon SDK Log: #{text}\n" if @verbose_mode
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
require 'kameleoon/kameleoon_client'
|
5
|
+
require 'kameleoon/kameleoon_client_config'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
# A Factory class for creating kameleoon clients
|
9
|
+
module KameleoonClientFactory
|
10
|
+
CONFIG_PATH = '/etc/kameleoon/client-ruby.yaml'
|
11
|
+
|
12
|
+
@clients = Concurrent::Map.new
|
13
|
+
|
14
|
+
def self.create(site_code, config: nil, config_path: CONFIG_PATH)
|
15
|
+
unless config.is_a?(KameleoonClientConfig)
|
16
|
+
config_path = CONFIG_PATH unless config_path.is_a?(String)
|
17
|
+
config = KameleoonClientConfig.read_from_yaml(config_path)
|
18
|
+
end
|
19
|
+
key = get_client_key(site_code, config.environment)
|
20
|
+
@clients.compute_if_absent(key) do
|
21
|
+
client = KameleoonClient.new(site_code, config)
|
22
|
+
client.send(:log, "Client created with site code: #{site_code}")
|
23
|
+
client.send(:fetch_configuration_initially)
|
24
|
+
client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.forget(site_code, environment = nil)
|
29
|
+
key = get_client_key(site_code, environment)
|
30
|
+
@clients.compute_if_present(key) do |client|
|
31
|
+
client.send(:dispose)
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private_class_method
|
37
|
+
|
38
|
+
def self.get_client_key(site_code, environment)
|
39
|
+
environment.nil? ? site_code : "#{site_code}/#{environment}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -12,20 +12,23 @@ module Kameleoon
|
|
12
12
|
class ActivityEvent
|
13
13
|
EVENT_TYPE = 'activity'
|
14
14
|
|
15
|
-
|
15
|
+
attr_reader :sent
|
16
16
|
|
17
17
|
def initialize
|
18
18
|
@sent = false
|
19
|
-
@nonce = Kameleoon::Utils.generate_random_string(Kameleoon::NONCE_LENGTH)
|
20
19
|
end
|
21
20
|
|
22
21
|
def obtain_full_post_text_line
|
23
22
|
params = {
|
24
23
|
eventType: EVENT_TYPE,
|
25
|
-
nonce:
|
24
|
+
nonce: Kameleoon::Utils.generate_random_string(Kameleoon::NONCE_LENGTH)
|
26
25
|
}
|
27
26
|
UriHelper.encode_query(params)
|
28
27
|
end
|
28
|
+
|
29
|
+
def mark_as_sent
|
30
|
+
@sent = true
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/utils'
|
4
|
+
|
5
|
+
module Kameleoon
|
6
|
+
module Network
|
7
|
+
module Cookie
|
8
|
+
COOKIE_KEY_JS = '_js_'
|
9
|
+
VISITOR_CODE_COOKIE = 'kameleoonVisitorCode'
|
10
|
+
COOKIE_TTL_SECONDS = 380 * 86_400 # 380 days in seconds
|
11
|
+
|
12
|
+
class CookieManager
|
13
|
+
attr_accessor :consent_required
|
14
|
+
|
15
|
+
def initialize(top_level_domain)
|
16
|
+
@consent_required = false
|
17
|
+
@top_level_domain = top_level_domain
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_or_add(cookies, default_visitor_code = nil)
|
21
|
+
return if cookies.nil?
|
22
|
+
|
23
|
+
visitor_code = get_visitor_code_from_cookies(cookies)
|
24
|
+
unless visitor_code.nil?
|
25
|
+
Utils::VisitorCode.validate(visitor_code)
|
26
|
+
# Remove adding cookies when we will be sure that it doesn't break anything
|
27
|
+
add(visitor_code, cookies) unless @consent_required
|
28
|
+
return visitor_code
|
29
|
+
end
|
30
|
+
|
31
|
+
if default_visitor_code.nil?
|
32
|
+
visitor_code = Utils::VisitorCode.generate
|
33
|
+
add(visitor_code, cookies) unless @consent_required
|
34
|
+
return visitor_code
|
35
|
+
end
|
36
|
+
|
37
|
+
visitor_code = default_visitor_code
|
38
|
+
Utils::VisitorCode.validate(visitor_code)
|
39
|
+
add(visitor_code, cookies)
|
40
|
+
visitor_code
|
41
|
+
end
|
42
|
+
|
43
|
+
def update(visitor_code, consent, cookies)
|
44
|
+
return if cookies.nil?
|
45
|
+
|
46
|
+
if consent
|
47
|
+
add(visitor_code, cookies)
|
48
|
+
else
|
49
|
+
remove(cookies)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def add(visitor_code, cookies)
|
56
|
+
cookie = {
|
57
|
+
value: visitor_code,
|
58
|
+
expires: Time.now + COOKIE_TTL_SECONDS,
|
59
|
+
path: '/',
|
60
|
+
domain: @top_level_domain
|
61
|
+
}
|
62
|
+
cookies[VISITOR_CODE_COOKIE] = cookie
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove(cookies)
|
66
|
+
cookies[VISITOR_CODE_COOKIE] = nil if @consent_required
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_visitor_code_from_cookies(cookies)
|
70
|
+
cookie = cookies[VISITOR_CODE_COOKIE]
|
71
|
+
case cookie
|
72
|
+
when String
|
73
|
+
visitor_code = cookie
|
74
|
+
when Hash
|
75
|
+
visitor_code = cookie[:value]
|
76
|
+
end
|
77
|
+
visitor_code = visitor_code[COOKIE_KEY_JS.size..] if visitor_code&.start_with?(COOKIE_KEY_JS)
|
78
|
+
visitor_code = nil if visitor_code&.empty?
|
79
|
+
visitor_code
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'em-synchrony/em-http'
|
4
3
|
require 'net/http'
|
5
4
|
require 'kameleoon/version'
|
6
5
|
require 'kameleoon/network/response'
|
@@ -16,7 +15,8 @@ module Kameleoon
|
|
16
15
|
private
|
17
16
|
|
18
17
|
def collect_headers(request)
|
19
|
-
headers =
|
18
|
+
headers = request.extra_headers || {}
|
19
|
+
headers['Content-Type'] = request.content_type
|
20
20
|
headers['User-Agent'] = request.user_agent unless request.user_agent.nil?
|
21
21
|
headers
|
22
22
|
end
|
@@ -26,40 +26,6 @@ module Kameleoon
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
class EMNetProvider < NetProvider
|
30
|
-
def make_request(request)
|
31
|
-
connetion_options = {
|
32
|
-
tls: { verify_peer: false },
|
33
|
-
connect_timeout: request.timeout,
|
34
|
-
inactivity_timeout: request.timeout
|
35
|
-
}
|
36
|
-
headers = collect_headers(request)
|
37
|
-
request_options = { head: headers, body: request.data }
|
38
|
-
begin
|
39
|
-
case request.method
|
40
|
-
when Method::POST
|
41
|
-
EventMachine::HttpRequest.new(request.url, connetion_options).apost(request_options)
|
42
|
-
when Method::GET
|
43
|
-
EventMachine::HttpRequest.new(request.url, connetion_options).aget(request_options)
|
44
|
-
else
|
45
|
-
dfr = DeferrableResponse.new
|
46
|
-
dfr.response = unknown_method_response(request.method, request)
|
47
|
-
dfr
|
48
|
-
end
|
49
|
-
rescue => e
|
50
|
-
dfr = DeferrableResponse.new
|
51
|
-
dfr.response = Response.new(e, nil, nil, request)
|
52
|
-
dfr
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.em_resp_to_response(request, resp)
|
57
|
-
return resp if resp.is_a?(Response)
|
58
|
-
|
59
|
-
Response.new(nil, resp.response_header.status, resp.response, request)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
29
|
class SyncNetProvider < NetProvider
|
64
30
|
def make_request(request)
|
65
31
|
resp = nil
|
@@ -84,7 +50,9 @@ module Kameleoon
|
|
84
50
|
rescue => e
|
85
51
|
return Response.new(e, nil, nil, request)
|
86
52
|
end
|
87
|
-
|
53
|
+
body = resp.body
|
54
|
+
body = nil if body&.empty?
|
55
|
+
Response.new(nil, resp.code.to_i, body, request)
|
88
56
|
end
|
89
57
|
end
|
90
58
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'em-synchrony'
|
4
3
|
require 'kameleoon/network/content_type'
|
5
4
|
require 'kameleoon/network/method'
|
6
5
|
require 'kameleoon/network/request'
|
7
6
|
require 'kameleoon/network/net_provider'
|
7
|
+
require 'kameleoon/version'
|
8
8
|
|
9
9
|
module Kameleoon
|
10
10
|
module Network
|
@@ -14,6 +14,8 @@ module Kameleoon
|
|
14
14
|
FETCH_CONFIGURATION_ATTEMPT_NUMBER = 4
|
15
15
|
TRACKING_CALL_ATTEMPT_NUMBER = 4
|
16
16
|
TRACKING_CALL_RETRY_DELAY = 5.0 # in seconds
|
17
|
+
SDK_TYPE_HEADER = 'X-Kameleoon-SDK-Type'
|
18
|
+
SDK_VERSION_HEADER = 'X-Kameleoon-SDK-Version'
|
17
19
|
|
18
20
|
attr_reader :environment, :default_timeout, :url_provider
|
19
21
|
|
@@ -21,7 +23,6 @@ module Kameleoon
|
|
21
23
|
@environment = environment
|
22
24
|
@default_timeout = default_timeout
|
23
25
|
@url_provider = url_provider
|
24
|
-
@em_net_provider = EMNetProvider.new
|
25
26
|
@sync_net_provider = SyncNetProvider.new
|
26
27
|
@log_func = log_func
|
27
28
|
end
|
@@ -29,11 +30,11 @@ module Kameleoon
|
|
29
30
|
def fetch_configuration(timestamp = nil, timeout = nil)
|
30
31
|
url = @url_provider.make_configuration_url(@environment, timestamp)
|
31
32
|
timeout = ensure_timeout(timeout)
|
32
|
-
|
33
|
+
sdk_headers = { SDK_TYPE_HEADER => SDK_NAME, SDK_VERSION_HEADER => SDK_VERSION }
|
34
|
+
request = Request.new(Method::GET, url, ContentType::JSON, timeout, extra_headers: sdk_headers)
|
33
35
|
attempts_left = FETCH_CONFIGURATION_ATTEMPT_NUMBER
|
34
36
|
while attempts_left.positive?
|
35
|
-
|
36
|
-
response = EMNetProvider.em_resp_to_response(request, em_resp)
|
37
|
+
response = @sync_net_provider.make_request(request)
|
37
38
|
result = handle_response(response)
|
38
39
|
return result if result
|
39
40
|
|
@@ -64,13 +65,13 @@ module Kameleoon
|
|
64
65
|
url = @url_provider.make_tracking_url(visitor_code)
|
65
66
|
timeout = ensure_timeout(timeout)
|
66
67
|
data = (lines.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8')
|
67
|
-
request = Request.new(Method::POST, url, ContentType::TEXT, timeout, user_agent, data)
|
68
|
+
request = Request.new(Method::POST, url, ContentType::TEXT, timeout, user_agent: user_agent, data: data)
|
68
69
|
Thread.new do
|
69
70
|
attempts_left = TRACKING_CALL_ATTEMPT_NUMBER
|
70
71
|
loop do
|
71
72
|
response = @sync_net_provider.make_request(request)
|
72
73
|
if handle_response(response) != false
|
73
|
-
lines.each
|
74
|
+
lines.each(&:mark_as_sent)
|
74
75
|
break
|
75
76
|
end
|
76
77
|
attempts_left -= 1
|
@@ -5,14 +5,15 @@ module Kameleoon
|
|
5
5
|
##
|
6
6
|
# Request represents HTTP request.
|
7
7
|
class Request
|
8
|
-
attr_reader :method, :url, :content_type, :timeout, :user_agent, :data
|
8
|
+
attr_reader :method, :url, :content_type, :timeout, :user_agent, :extra_headers, :data
|
9
9
|
|
10
|
-
def initialize(method, url, content_type, timeout, user_agent
|
10
|
+
def initialize(method, url, content_type, timeout, user_agent: nil, extra_headers: nil, data: nil)
|
11
11
|
@method = method
|
12
12
|
@url = url
|
13
13
|
@content_type = content_type
|
14
14
|
@timeout = timeout
|
15
15
|
@user_agent = user_agent
|
16
|
+
@extra_headers = extra_headers
|
16
17
|
@data = !data.nil? && data.is_a?(String) ? data.encode('UTF-8') : data
|
17
18
|
end
|
18
19
|
end
|
@@ -12,7 +12,7 @@ module Kameleoon
|
|
12
12
|
VISITOR_DATA_PATH = '/visit/visitor'
|
13
13
|
GET_DATA_PATH = '/map/map'
|
14
14
|
POST_DATA_PATH = '/map/maps'
|
15
|
-
|
15
|
+
CONFIGURATION_API_URL_FORMAT = 'https://sdk-config.kameleoon.eu/%s'
|
16
16
|
RT_CONFIGURATION_URL = 'https://events.kameleoon.com:8110/sse'
|
17
17
|
|
18
18
|
DEFAULT_DATA_API_URL = 'https://data.kameleoon.io'
|
@@ -56,10 +56,12 @@ module Kameleoon
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def make_configuration_url(environment = nil, timestamp = nil)
|
59
|
-
|
59
|
+
url = format(CONFIGURATION_API_URL_FORMAT, @site_code)
|
60
|
+
params = {}
|
60
61
|
params[:environment] = environment unless environment.nil?
|
61
62
|
params[:ts] = timestamp unless timestamp.nil?
|
62
|
-
"#{
|
63
|
+
url = "#{url}?#{UriHelper.encode_query(params)}" unless params.empty?
|
64
|
+
url
|
63
65
|
end
|
64
66
|
|
65
67
|
def make_real_time_url
|
@@ -22,9 +22,8 @@ module Kameleoon
|
|
22
22
|
@version_match_type = json_condition['versionMatchType']
|
23
23
|
end
|
24
24
|
|
25
|
-
def check(
|
26
|
-
browser
|
27
|
-
browser && check_targeting(browser)
|
25
|
+
def check(browser)
|
26
|
+
browser.is_a?(Kameleoon::Browser) && check_targeting(browser)
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/data/
|
3
|
+
require 'kameleoon/data/conversion'
|
4
|
+
require 'kameleoon/data/manager/data_array_storage'
|
4
5
|
|
5
6
|
module Kameleoon
|
6
7
|
# @api private
|
@@ -12,9 +13,16 @@ module Kameleoon
|
|
12
13
|
@goal_id = json_condition['goalId']
|
13
14
|
end
|
14
15
|
|
15
|
-
def check(
|
16
|
-
|
17
|
-
|
16
|
+
def check(conversion_storage)
|
17
|
+
return false unless conversion_storage.is_a?(Kameleoon::DataManager::DataArrayStorage)
|
18
|
+
return true if @goal_id.nil?
|
19
|
+
|
20
|
+
is_targeted = false
|
21
|
+
conversion_storage.enumerate do |conversion|
|
22
|
+
is_targeted = conversion.is_a?(Kameleoon::Conversion) && (@goal_id == conversion.goal_id)
|
23
|
+
break if is_targeted
|
24
|
+
end
|
25
|
+
is_targeted
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
@@ -12,6 +12,10 @@ module Kameleoon
|
|
12
12
|
include Kameleoon::Exception
|
13
13
|
|
14
14
|
class << self
|
15
|
+
def op_undefined(values, value)
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
15
19
|
def op_match(values, value)
|
16
20
|
re = Regexp.new(value.to_s)
|
17
21
|
values.any? { |v| re.match(v) }
|
@@ -26,7 +30,8 @@ module Kameleoon
|
|
26
30
|
end
|
27
31
|
|
28
32
|
def op_equal(values, value)
|
29
|
-
|
33
|
+
epsilon = 1e-9
|
34
|
+
values.any? { |v| (v.to_f - value.to_f).abs < epsilon }
|
30
35
|
end
|
31
36
|
|
32
37
|
def op_greater(values, value)
|
@@ -52,6 +57,7 @@ module Kameleoon
|
|
52
57
|
end
|
53
58
|
|
54
59
|
@@op = {
|
60
|
+
Operator::UNDEFINED => method(:op_undefined),
|
55
61
|
Operator::REGULAR_EXPRESSION => method(:op_match),
|
56
62
|
Operator::CONTAINS => method(:op_contains),
|
57
63
|
Operator::EXACT => method(:op_exact),
|
@@ -71,24 +77,24 @@ module Kameleoon
|
|
71
77
|
end
|
72
78
|
|
73
79
|
@type = ConditionType::CUSTOM_DATUM
|
74
|
-
@index = json_condition['customDataIndex']
|
80
|
+
@index = json_condition['customDataIndex'].to_i
|
75
81
|
@operator = json_condition['valueMatchType']
|
82
|
+
@operation = @@op[@operator]
|
76
83
|
@value = json_condition['value']
|
77
84
|
end
|
78
85
|
|
79
|
-
def check(
|
80
|
-
is_targeted =
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
def check(custom_data_storage)
|
87
|
+
is_targeted = nil
|
88
|
+
if custom_data_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
|
89
|
+
custom_data = custom_data_storage.get(@index)
|
90
|
+
if custom_data.is_a?(Kameleoon::CustomData)
|
91
|
+
unless @operation
|
92
|
+
raise KameleoonError.new("Undefined operator #{@operator}"), "Undefined operator #{@operator}"
|
93
|
+
end
|
94
|
+
is_targeted = @operation.call(custom_data.values, @value)
|
88
95
|
end
|
89
|
-
|
90
|
-
is_targeted = @operation.call(custom_data.values, @value)
|
91
96
|
end
|
97
|
+
is_targeted = (@operator == Operator::UNDEFINED.to_s) if is_targeted.nil?
|
92
98
|
is_targeted
|
93
99
|
end
|
94
100
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/data/
|
3
|
+
require 'kameleoon/data/device'
|
4
4
|
|
5
5
|
module Kameleoon
|
6
6
|
# @api private
|
@@ -12,9 +12,8 @@ module Kameleoon
|
|
12
12
|
@device_type = json_condition['device']
|
13
13
|
end
|
14
14
|
|
15
|
-
def check(
|
16
|
-
device
|
17
|
-
device && @device_type == device.device_type
|
15
|
+
def check(device)
|
16
|
+
device.is_a?(Kameleoon::Device) && @device_type == device.device_type
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
@@ -10,7 +10,8 @@ module Kameleoon
|
|
10
10
|
def check(data)
|
11
11
|
experiment_id = data.experiment_id
|
12
12
|
storage = data.storage
|
13
|
-
storage.
|
13
|
+
storage.is_a?(Kameleoon::DataManager::DataMapStorage) &&
|
14
|
+
(storage.size.zero? || ((storage.size == 1) && !storage.get(experiment_id).nil?))
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/data/
|
3
|
+
require 'kameleoon/data/manager/data_map_storage'
|
4
|
+
require 'kameleoon/data/manager/page_view_visit'
|
4
5
|
require_relative 'string_value_condition'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -12,9 +13,15 @@ module Kameleoon
|
|
12
13
|
super(json_condition, json_condition['title'])
|
13
14
|
end
|
14
15
|
|
15
|
-
def check(
|
16
|
-
|
17
|
-
|
16
|
+
def check(page_view_visit_storage)
|
17
|
+
return false unless page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
|
18
|
+
|
19
|
+
is_targeted = false
|
20
|
+
page_view_visit_storage.enumerate do |visit|
|
21
|
+
is_targeted = visit.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(visit.page_view.title)
|
22
|
+
break if is_targeted
|
23
|
+
end
|
24
|
+
is_targeted
|
18
25
|
end
|
19
26
|
end
|
20
27
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kameleoon/data/
|
3
|
+
require 'kameleoon/data/manager/data_map_storage'
|
4
|
+
require 'kameleoon/data/manager/page_view_visit'
|
4
5
|
require_relative 'string_value_condition'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -12,9 +13,22 @@ module Kameleoon
|
|
12
13
|
super(json_condition, json_condition['url'])
|
13
14
|
end
|
14
15
|
|
15
|
-
def check(
|
16
|
-
|
17
|
-
|
16
|
+
def check(page_view_visit_storage)
|
17
|
+
return false unless page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
|
18
|
+
return !page_view_visit_storage.get(condition_value).nil? if operator == Operator::EXACT
|
19
|
+
|
20
|
+
is_targeted = false
|
21
|
+
page_view_visit_storage.enumerate do |visit|
|
22
|
+
is_targeted = check_page_view_visit(visit)
|
23
|
+
break if is_targeted
|
24
|
+
end
|
25
|
+
is_targeted
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def check_page_view_visit(visit)
|
31
|
+
visit.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(visit.page_view.url)
|
18
32
|
end
|
19
33
|
end
|
20
34
|
end
|
@@ -24,16 +24,21 @@ module Kameleoon
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def check(variation_storage)
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
return false unless variation_storage.is_a?(Kameleoon::DataManager::DataMapStorage) &&
|
28
|
+
variation_storage.size.positive?
|
29
|
+
|
30
|
+
variation = variation_storage.get(@experiment)
|
31
|
+
return false unless variation.is_a?(Kameleoon::DataManager::AssignedVariation)
|
32
|
+
|
30
33
|
case @operator
|
31
34
|
when Operator::EXACT
|
32
|
-
|
35
|
+
variation = variation_storage.get(@experiment)
|
36
|
+
variation.variation_id == @variation
|
33
37
|
when Operator::ANY
|
34
|
-
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
35
41
|
end
|
36
|
-
is_targeted
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|