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
@@ -1,16 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fiber'
4
+ require 'bigdecimal'
5
+ require 'kameleoon/utils'
6
+ require 'kameleoon/exceptions'
7
+
3
8
  module Kameleoon
4
9
  # Utils is a helper module for project
5
10
  module Utils
6
11
  ALPHA_NUMERIC_CHARS = 'ABCDEF0123456789'
7
12
 
8
- def self.in_seconds(days)
9
- days * 60 * 60 * 24
10
- end
11
-
12
13
  def self.generate_random_string(length)
13
14
  (1..length).map { ALPHA_NUMERIC_CHARS[rand(ALPHA_NUMERIC_CHARS.length)] }.join
14
15
  end
16
+
17
+ module VisitorCode
18
+ VISITOR_CODE_MAX_LENGTH = 255
19
+ VISITOR_CODE_LENGTH = 16
20
+
21
+ def self.validate(visitor_code)
22
+ raise Kameleoon::Exception::VisitorCodeInvalid, 'Empty visitor Code' if visitor_code&.empty?
23
+ if visitor_code.size > VISITOR_CODE_MAX_LENGTH
24
+ raise Kameleoon::Exception::VisitorCodeInvalid, "Visitor Code is longer than #{VISITOR_CODE_MAX_LENGTH} chars"
25
+ end
26
+ end
27
+
28
+ def self.generate
29
+ Utils.generate_random_string(VISITOR_CODE_LENGTH)
30
+ end
31
+ end
32
+
33
+ module HashDouble
34
+ def self.obtain(visitor_code, respool_times = {}, container_id = '')
35
+ obtain_helper(visitor_code, respool_times, container_id, '')
36
+ end
37
+
38
+ def self.obtain_rule(visitor_code, container_id = '', suffix = '')
39
+ obtain_helper(visitor_code, {}, container_id, suffix)
40
+ end
41
+
42
+ private
43
+
44
+ def self.obtain_helper(visitor_code, respool_times, container_id, suffix)
45
+ identifier = visitor_code.to_s
46
+ identifier += container_id.to_s
47
+ identifier += suffix.to_s
48
+ identifier += respool_times.sort.to_h.values.join.to_s if !respool_times.nil? && !respool_times.empty?
49
+ (Digest::SHA256.hexdigest(identifier.encode('UTF-8')).to_i(16) / (BigDecimal('2')**BigDecimal('256'))).round(16)
50
+ end
51
+ end
15
52
  end
16
53
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Kameleoon
4
4
  SDK_NAME = 'RUBY'
5
- SDK_VERSION = '2.3.0'
5
+ SDK_VERSION = '3.0.0'
6
6
 
7
7
  # SdkManager is a helper method for fetching / obtaining version of SDK from string
8
8
  class SdkVersion
data/lib/kameleoon.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Kameleoon Ruby Client SDK
3
5
  #
4
- require "kameleoon/factory"
5
- require "kameleoon/client"
6
+ require 'kameleoon/kameleoon_client_factory'
7
+ require 'kameleoon/kameleoon_client'
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: 2.3.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-02 00:00:00.000000000 Z
11
+ date: 2023-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request
@@ -75,29 +75,35 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - README.md
77
77
  - lib/kameleoon.rb
78
- - lib/kameleoon/client.rb
79
- - lib/kameleoon/client_config.rb
80
- - lib/kameleoon/configuration/experiment.rb
78
+ - lib/kameleoon/client_readiness.rb
79
+ - lib/kameleoon/configuration/data_file.rb
81
80
  - lib/kameleoon/configuration/feature_flag.rb
82
81
  - lib/kameleoon/configuration/rule.rb
83
82
  - lib/kameleoon/configuration/settings.rb
84
83
  - lib/kameleoon/configuration/variable.rb
85
84
  - lib/kameleoon/configuration/variation.rb
86
85
  - lib/kameleoon/configuration/variation_exposition.rb
87
- - lib/kameleoon/cookie.rb
88
86
  - lib/kameleoon/data/browser.rb
89
87
  - lib/kameleoon/data/conversion.rb
90
88
  - lib/kameleoon/data/custom_data.rb
91
89
  - lib/kameleoon/data/data.rb
92
90
  - lib/kameleoon/data/device.rb
91
+ - lib/kameleoon/data/manager/assigned_variation.rb
92
+ - lib/kameleoon/data/manager/data_array_storage.rb
93
+ - lib/kameleoon/data/manager/data_map_storage.rb
94
+ - lib/kameleoon/data/manager/page_view_visit.rb
95
+ - lib/kameleoon/data/manager/visitor.rb
96
+ - lib/kameleoon/data/manager/visitor_manager.rb
93
97
  - lib/kameleoon/data/page_view.rb
94
98
  - lib/kameleoon/data/user_agent.rb
95
99
  - lib/kameleoon/exceptions.rb
96
- - lib/kameleoon/factory.rb
97
100
  - lib/kameleoon/hybrid/manager.rb
101
+ - lib/kameleoon/kameleoon_client.rb
102
+ - lib/kameleoon/kameleoon_client_config.rb
103
+ - lib/kameleoon/kameleoon_client_factory.rb
98
104
  - lib/kameleoon/network/activity_event.rb
99
105
  - lib/kameleoon/network/content_type.rb
100
- - lib/kameleoon/network/experiment_event.rb
106
+ - lib/kameleoon/network/cookie/cookie_manager.rb
101
107
  - lib/kameleoon/network/method.rb
102
108
  - lib/kameleoon/network/net_provider.rb
103
109
  - lib/kameleoon/network/network_manager.rb
@@ -112,8 +118,6 @@ files:
112
118
  - lib/kameleoon/real_time/sse_request.rb
113
119
  - lib/kameleoon/storage/cache.rb
114
120
  - lib/kameleoon/storage/cache_factory.rb
115
- - lib/kameleoon/storage/variation_storage.rb
116
- - lib/kameleoon/storage/visitor_variation.rb
117
121
  - lib/kameleoon/targeting/condition.rb
118
122
  - lib/kameleoon/targeting/condition_factory.rb
119
123
  - lib/kameleoon/targeting/conditions/browser_condition.rb
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kameleoon
4
- # Client configuration which can be used instead of an external configuration file
5
- class ClientConfig
6
- CONFIGURATION_UPDATE_INTERVAL = 60
7
- VISITOR_DATA_MAXIMUM_SIZE = 500
8
- DEFAULT_TIMEOUT = 2000 # milli-seconds
9
-
10
- attr_reader :client_id, :client_secret, :data_refresh_interval, :default_timeout, :configuration_refresh_interval,
11
- :visitor_data_maximum_size, :environment, :verbose_mode
12
-
13
- def initialize(
14
- client_id: nil,
15
- client_secret: nil,
16
- configuration_refresh_interval: CONFIGURATION_UPDATE_INTERVAL,
17
- default_timeout: DEFAULT_TIMEOUT,
18
- visitor_data_maximum_size: VISITOR_DATA_MAXIMUM_SIZE,
19
- environment: nil,
20
- verbose_mode: false
21
- )
22
- @client_id = client_id
23
- @client_secret = client_secret
24
- @configuration_refresh_interval = configuration_refresh_interval || CONFIGURATION_UPDATE_INTERVAL
25
- @default_timeout = default_timeout || DEFAULT_TIMEOUT
26
- @visitor_data_maximum_size = visitor_data_maximum_size || VISITOR_DATA_MAXIMUM_SIZE
27
- @environment = environment
28
- @verbose_mode = verbose_mode || false
29
- end
30
-
31
- def self.make_from_yaml(yaml)
32
- yaml ||= {}
33
- ClientConfig.new(
34
- client_id: yaml['client_id'],
35
- client_secret: yaml['client_secret'],
36
- configuration_refresh_interval: yaml['actions_configuration_refresh_interval'],
37
- default_timeout: yaml['default_timeout'],
38
- visitor_data_maximum_size: yaml['visitor_data_maximum_size'],
39
- environment: yaml['environment'],
40
- verbose_mode: yaml['verbose_mode']
41
- )
42
- end
43
- end
44
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'kameleoon/targeting/models'
4
-
5
- module Kameleoon
6
- # Module which contains all internal data of SDK
7
- module Configuration
8
- # Class for manage all experiments and old feature flags
9
- class Experiment
10
- attr_accessor :id, :status, :site_enabled, :deviations, :respool_time, :variations, :targeting_segment
11
-
12
- def self.create_from_array(array)
13
- array&.map { |it| Experiment.new(it) }
14
- end
15
-
16
- def initialize(experiment_hash)
17
- @id = experiment_hash['id']
18
- @status = experiment_hash['status']
19
- @site_enabled = experiment_hash['siteEnabled']
20
- unless experiment_hash['deviations'].nil?
21
- @deviations =
22
- Hash[*experiment_hash['deviations'].map do |it|
23
- [it['variationId'] == 'origin' ? '0' : it['variationId'], it['value']]
24
- end.flatten]
25
- end
26
- unless experiment_hash['respoolTime'].nil?
27
- @respool_time =
28
- Hash[*experiment_hash['respoolTime'].map do |it|
29
- [it['variationId'] == 'origin' ? '0' : it['variationId'], it['value']]
30
- end.flatten]
31
- end
32
- unless experiment_hash['variations'].nil?
33
- @variations =
34
- experiment_hash['variations'].map do |it|
35
- { 'id' => it['id'].to_i, 'customJson' => it['customJson'] }
36
- end
37
- end
38
- @targeting_segment = Kameleoon::Targeting::Segment.new((experiment_hash['segment'])) if experiment_hash['segment']
39
- end
40
- end
41
- end
42
- end
@@ -1,88 +0,0 @@
1
- require 'fiber'
2
- require 'em-http'
3
- require 'eventmachine'
4
- require 'bigdecimal'
5
- require 'kameleoon/utils'
6
- require 'kameleoon/exceptions'
7
-
8
- module Kameleoon
9
- # @api private
10
- module Cookie
11
- # @return [String] visitor code
12
- def read_and_write(request_cookies, top_level_domain, response_cookies, default_visitor_code = nil)
13
- kameleoon_visitor_code = read(request_cookies)
14
- if kameleoon_visitor_code.nil?
15
- kameleoon_visitor_code = check_default_visitor_code(default_visitor_code) || Kameleoon::Utils.generate_random_string(KAMELEOON_COOKIE_VALUE_LENGTH)
16
- end
17
- cookie = { :value => kameleoon_visitor_code, :expires => Time.now + Kameleoon::Utils.in_seconds(EXPIRE_DAYS), :path => '/' }
18
- unless top_level_domain.nil?
19
- cookie[:domain] = top_level_domain
20
- end
21
- response_cookies[KAMELEOON_COOKIE_NAME] = cookie
22
- kameleoon_visitor_code
23
- end
24
-
25
- def obtain_hash_double(visitor_code, respool_times = {}, container_id = '')
26
- obtain_hash_double_helper(visitor_code, respool_times, container_id, '')
27
- end
28
-
29
- def obtain_hash_double_rule(visitor_code, container_id = '', suffix = '')
30
- obtain_hash_double_helper(visitor_code, {}, container_id, suffix)
31
- end
32
-
33
- def obtain_hash_double_helper(visitor_code, respool_times, container_id, suffix)
34
- identifier = visitor_code.to_s
35
- identifier += container_id.to_s
36
- identifier += suffix.to_s
37
- identifier += respool_times.sort.to_h.values.join.to_s if !respool_times.nil? && !respool_times.empty?
38
- (Digest::SHA256.hexdigest(identifier.encode('UTF-8')).to_i(16) / (BigDecimal('2')**BigDecimal('256'))).round(16)
39
- end
40
-
41
- def check_visitor_code(visitor_code)
42
- if visitor_code.nil?
43
- check_default_visitor_code('')
44
- elsif
45
- check_default_visitor_code(visitor_code)
46
- end
47
- end
48
-
49
- private
50
-
51
- KAMELEOON_KEY_JS_COOKIE = "_js_"
52
- KAMELEOON_VISITOR_CODE_LENGTH = 255
53
- KAMELEOON_COOKIE_VALUE_LENGTH = 16
54
- KAMELEOON_COOKIE_NAME = 'kameleoonVisitorCode'
55
- EXPIRE_DAYS = 380
56
-
57
- def check_default_visitor_code(default_visitor_code)
58
- if default_visitor_code.nil?
59
- return
60
- end
61
- default_visitor_code = default_visitor_code.to_s
62
- if default_visitor_code.length == 0
63
- raise Kameleoon::Exception::VisitorCodeNotValid.new("Empty visitor Code")
64
- end
65
- if default_visitor_code.length > KAMELEOON_VISITOR_CODE_LENGTH
66
- raise Kameleoon::Exception::VisitorCodeNotValid.new("Visitor Code is longer than 255 chars.")
67
- end
68
- default_visitor_code
69
- end
70
-
71
- def read(cookies)
72
- value = cookies[KAMELEOON_COOKIE_NAME]
73
- if value.nil?
74
- return
75
- end
76
- if value.start_with?(KAMELEOON_KEY_JS_COOKIE)
77
- start_index = KAMELEOON_KEY_JS_COOKIE.length
78
- value = value[start_index..-1]
79
- end
80
- if value.length == 0
81
- nil
82
- end
83
- value[0..(KAMELEOON_VISITOR_CODE_LENGTH - 1)]
84
- end
85
- end
86
- end
87
-
88
-
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'kameleoon/client'
4
- require 'kameleoon/client_config'
5
-
6
- module Kameleoon
7
- # A Factory class for creating kameleoon clients
8
- module ClientFactory
9
- CONFIG_PATH = '/etc/kameleoon/client-ruby.yaml'
10
-
11
- @clients = {}
12
-
13
- def self.create(site_code, config_path = CONFIG_PATH, config: nil)
14
- if config.nil?
15
- config_yaml = YAML.load_file(config_path) if File.exist?(config_path)
16
- if config_yaml.nil?
17
- warn "Kameleoon SDK: Configuration file with path #{config_path} does not exist" if config_yaml.nil?
18
- config_yaml = {}
19
- end
20
- end
21
- environment = config&.environment || (config_yaml || {})['environment']
22
- client = @clients[get_client_key(site_code, environment)]
23
- if client.nil?
24
- config = ClientConfig.make_from_yaml(config_yaml) if config.nil?
25
- client = Client.new(site_code, config)
26
- client.send(:log, "Client created with site code: #{site_code}")
27
- client.send(:fetch_configuration)
28
- @clients.store(site_code, client)
29
- end
30
- client
31
- end
32
-
33
- def self.forget(site_code, environment = '')
34
- @clients.delete(get_client_key(site_code, environment))
35
- end
36
-
37
- private_class_method
38
-
39
- def self.get_client_key(site_code, environment)
40
- site_code + (environment || '')
41
- end
42
- end
43
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'uri'
4
- require 'kameleoon/data/data'
5
- require 'kameleoon/utils'
6
- require 'kameleoon/network/uri_helper'
7
-
8
- module Kameleoon
9
- module Network
10
- ##
11
- # ExperimentEvent represents an experiment tracking event.
12
- class ExperimentEvent
13
- EVENT_TYPE = 'experiment'
14
-
15
- attr_accessor :sent
16
-
17
- def initialize(experiment_id, variation_id)
18
- @sent = false
19
- @experiment_id = experiment_id
20
- @variation_id = variation_id
21
- @nonce = Kameleoon::Utils.generate_random_string(Kameleoon::NONCE_LENGTH)
22
- end
23
-
24
- def obtain_full_post_text_line
25
- params = {
26
- eventType: EVENT_TYPE,
27
- id: @experiment_id,
28
- variationId: @variation_id,
29
- nonce: @nonce
30
- }
31
- UriHelper.encode_query(params)
32
- end
33
- end
34
- end
35
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'kameleoon/storage/visitor_variation'
4
-
5
- module Kameleoon
6
- module Storage
7
- # VariationStorage is a container for saved variations associated with a visitor
8
- class VariationStorage
9
- def initialize
10
- @storage = {}
11
- end
12
-
13
- def get_variation_id(visitor_code, experiment_id)
14
- variation_valid?(visitor_code, experiment_id, nil)
15
- end
16
-
17
- def variation_valid?(visitor_code, experiment_id, respool_time)
18
- return nil if @storage[visitor_code].nil? || @storage[visitor_code][experiment_id].nil?
19
-
20
- variation = @storage[visitor_code][experiment_id]
21
- return nil unless variation.valid?(respool_time)
22
-
23
- variation.variation_id
24
- end
25
-
26
- def update_variation(visitor_code, experiment_id, variation_id)
27
- @storage[visitor_code] = {} if @storage[visitor_code].nil?
28
- @storage[visitor_code][experiment_id] = Kameleoon::Storage::VisitorVariation.new(variation_id)
29
- end
30
-
31
- def get_hash_saved_variation_id(visitor_code)
32
- return nil if @storage[visitor_code].nil?
33
-
34
- map_variations = {}
35
- @storage[visitor_code].each do |key, value|
36
- map_variations[key] = value.variation_id
37
- end
38
- map_variations
39
- end
40
- end
41
- end
42
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kameleoon
4
- module Storage
5
- # VisitorVariation contains a saved variation id associated with a visitor
6
- # and time when it was associated.
7
- class VisitorVariation
8
- attr_accessor :variation_id
9
-
10
- def initialize(variation_id)
11
- @variation_id = variation_id
12
- @assignment_date = Time.now.to_i
13
- end
14
-
15
- def valid?(respool_time)
16
- respool_time.nil? || @assignment_date > respool_time
17
- end
18
- end
19
- end
20
- end