kameleoon-client-ruby 2.3.0 → 3.0.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/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
|