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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kameleoon/client_readiness.rb +40 -0
  3. data/lib/kameleoon/configuration/data_file.rb +41 -0
  4. data/lib/kameleoon/configuration/feature_flag.rb +3 -2
  5. data/lib/kameleoon/configuration/rule.rb +18 -3
  6. data/lib/kameleoon/configuration/settings.rb +3 -1
  7. data/lib/kameleoon/configuration/variable.rb +0 -2
  8. data/lib/kameleoon/configuration/variation_exposition.rb +0 -2
  9. data/lib/kameleoon/data/browser.rb +1 -1
  10. data/lib/kameleoon/data/conversion.rb +1 -1
  11. data/lib/kameleoon/data/custom_data.rb +10 -14
  12. data/lib/kameleoon/data/data.rb +22 -3
  13. data/lib/kameleoon/data/device.rb +2 -1
  14. data/lib/kameleoon/data/manager/assigned_variation.rb +38 -0
  15. data/lib/kameleoon/data/manager/data_array_storage.rb +43 -0
  16. data/lib/kameleoon/data/manager/data_map_storage.rb +43 -0
  17. data/lib/kameleoon/data/manager/page_view_visit.rb +19 -0
  18. data/lib/kameleoon/data/manager/visitor.rb +142 -0
  19. data/lib/kameleoon/data/manager/visitor_manager.rb +71 -0
  20. data/lib/kameleoon/data/page_view.rb +2 -1
  21. data/lib/kameleoon/exceptions.rb +30 -35
  22. data/lib/kameleoon/hybrid/manager.rb +13 -31
  23. data/lib/kameleoon/{client.rb → kameleoon_client.rb} +164 -336
  24. data/lib/kameleoon/kameleoon_client_config.rb +91 -0
  25. data/lib/kameleoon/kameleoon_client_factory.rb +42 -0
  26. data/lib/kameleoon/network/activity_event.rb +6 -3
  27. data/lib/kameleoon/network/cookie/cookie_manager.rb +84 -0
  28. data/lib/kameleoon/network/net_provider.rb +5 -37
  29. data/lib/kameleoon/network/network_manager.rb +8 -7
  30. data/lib/kameleoon/network/request.rb +3 -2
  31. data/lib/kameleoon/network/response.rb +0 -8
  32. data/lib/kameleoon/network/url_provider.rb +5 -3
  33. data/lib/kameleoon/targeting/conditions/browser_condition.rb +2 -3
  34. data/lib/kameleoon/targeting/conditions/conversion_condition.rb +12 -4
  35. data/lib/kameleoon/targeting/conditions/custom_datum.rb +19 -13
  36. data/lib/kameleoon/targeting/conditions/device_condition.rb +3 -4
  37. data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +2 -1
  38. data/lib/kameleoon/targeting/conditions/page_title_condition.rb +11 -4
  39. data/lib/kameleoon/targeting/conditions/page_url_condition.rb +18 -4
  40. data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -0
  41. data/lib/kameleoon/targeting/conditions/target_experiment.rb +11 -6
  42. data/lib/kameleoon/utils.rb +41 -4
  43. data/lib/kameleoon/version.rb +1 -1
  44. data/lib/kameleoon.rb +4 -2
  45. metadata +14 -10
  46. data/lib/kameleoon/client_config.rb +0 -44
  47. data/lib/kameleoon/configuration/experiment.rb +0 -42
  48. data/lib/kameleoon/cookie.rb +0 -88
  49. data/lib/kameleoon/factory.rb +0 -43
  50. data/lib/kameleoon/network/experiment_event.rb +0 -35
  51. data/lib/kameleoon/storage/variation_storage.rb +0 -42
  52. 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
- attr_accessor :sent
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: @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 = { 'Content-Type': request.content_type }
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
- Response.new(nil, resp.code.to_i, resp.body, request)
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
- request = Request.new(Method::GET, url, ContentType::JSON, timeout)
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
- em_resp = EM::Synchrony.sync(@em_net_provider.make_request(request))
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 { |line| line.sent = true }
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 = nil, data = nil)
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
@@ -18,13 +18,5 @@ module Kameleoon
18
18
  @error.nil? && (@code / 100 == 2)
19
19
  end
20
20
  end
21
-
22
- class DeferrableResponse < EventMachine::DefaultDeferrable
23
- attr_writer :response
24
-
25
- def callback(&blk)
26
- blk.call(@response)
27
- end
28
- end
29
21
  end
30
22
  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
- CONFIGURATION_API_URL = 'https://client-config.kameleoon.com/mobile'
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
- params = { siteCode: @site_code }
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
- "#{CONFIGURATION_API_URL}?#{UriHelper.encode_query(params)}"
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(list_data)
26
- browser = get_last_targeting_data(list_data, Kameleoon::DataType::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/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(list_data)
16
- conversion = get_last_targeting_data(list_data, Kameleoon::DataType::CONVERSION)
17
- conversion && (@goal_id.nil? || @goal_id == conversion.goal_id)
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
- values.any? { |v| v.to_f == value.to_f }
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(list_data)
80
- is_targeted = false
81
- custom_data = list_data.select { |data| data.instance == DataType::CUSTOM && data.id == @index }.last
82
- if custom_data.nil?
83
- is_targeted = (@operator == Operator::UNDEFINED.to_s)
84
- elsif @operator != Operator::UNDEFINED.to_s
85
- @operation = @@op[@operator]
86
- unless @operation
87
- raise KameleoonError.new("Undefined operator #{@operator}"), "Undefined operator #{@operator}"
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/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(list_data)
16
- device = get_last_targeting_data(list_data, Kameleoon::DataType::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.nil? || storage.empty? || (storage.length == 1 && !storage[experiment_id].nil?)
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/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(list_data)
16
- page_view = get_last_targeting_data(list_data, Kameleoon::DataType::PAGE_VIEW)
17
- page_view && check_targeting(page_view.title)
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/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(list_data)
16
- page_view = get_last_targeting_data(list_data, Kameleoon::DataType::PAGE_VIEW)
17
- page_view && check_targeting(page_view.url)
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
@@ -19,6 +19,8 @@ module Kameleoon
19
19
 
20
20
  protected
21
21
 
22
+ attr_reader :operator, :condition_value
23
+
22
24
  def check_targeting(value)
23
25
  return false if value.nil?
24
26
 
@@ -24,16 +24,21 @@ module Kameleoon
24
24
  end
25
25
 
26
26
  def check(variation_storage)
27
- is_targeted = false
28
- variation_storage_exist = !variation_storage.nil? && !variation_storage.empty?
29
- saved_variation = variation_storage[@experiment] unless variation_storage.nil?
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
- is_targeted = variation_storage_exist && saved_variation == @variation
35
+ variation = variation_storage.get(@experiment)
36
+ variation.variation_id == @variation
33
37
  when Operator::ANY
34
- is_targeted = variation_storage_exist
38
+ true
39
+ else
40
+ false
35
41
  end
36
- is_targeted
37
42
  end
38
43
  end
39
44
  end