kameleoon-client-ruby 3.2.0 → 3.4.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kameleoon/configuration/custom_data_info.rb +16 -8
  3. data/lib/kameleoon/configuration/data_file.rb +40 -17
  4. data/lib/kameleoon/configuration/feature_flag.rb +10 -0
  5. data/lib/kameleoon/configuration/rule.rb +4 -0
  6. data/lib/kameleoon/configuration/settings.rb +13 -8
  7. data/lib/kameleoon/configuration/variation_exposition.rb +4 -0
  8. data/lib/kameleoon/data/browser.rb +4 -0
  9. data/lib/kameleoon/data/conversion.rb +4 -0
  10. data/lib/kameleoon/data/cookie.rb +4 -0
  11. data/lib/kameleoon/data/custom_data.rb +11 -3
  12. data/lib/kameleoon/data/data.rb +30 -4
  13. data/lib/kameleoon/data/device.rb +4 -0
  14. data/lib/kameleoon/data/geolocation.rb +5 -0
  15. data/lib/kameleoon/data/kcs_heat.rb +16 -0
  16. data/lib/kameleoon/data/manager/assigned_variation.rb +5 -0
  17. data/lib/kameleoon/data/manager/data_array_storage.rb +7 -0
  18. data/lib/kameleoon/data/manager/data_map_storage.rb +7 -0
  19. data/lib/kameleoon/data/manager/page_view_visit.rb +4 -0
  20. data/lib/kameleoon/data/manager/visitor.rb +198 -67
  21. data/lib/kameleoon/data/manager/visitor_manager.rb +54 -17
  22. data/lib/kameleoon/data/mapping_identifier.rb +33 -0
  23. data/lib/kameleoon/data/operating_system.rb +4 -0
  24. data/lib/kameleoon/data/page_view.rb +6 -1
  25. data/lib/kameleoon/data/unique_identifier.rb +11 -0
  26. data/lib/kameleoon/data/user_agent.rb +4 -0
  27. data/lib/kameleoon/data/visitor_visits.rb +12 -1
  28. data/lib/kameleoon/hybrid/manager.rb +13 -4
  29. data/lib/kameleoon/kameleoon_client.rb +348 -146
  30. data/lib/kameleoon/kameleoon_client_config.rb +64 -17
  31. data/lib/kameleoon/kameleoon_client_factory.rb +15 -2
  32. data/lib/kameleoon/logging/default_logger.rb +20 -0
  33. data/lib/kameleoon/logging/kameleoon_logger.rb +77 -0
  34. data/lib/kameleoon/logging/logger.rb +12 -0
  35. data/lib/kameleoon/managers/data/data_manager.rb +36 -0
  36. data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +33 -18
  37. data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +42 -16
  38. data/lib/kameleoon/managers/tracking/tracking_builder.rb +149 -0
  39. data/lib/kameleoon/managers/tracking/tracking_manager.rb +97 -0
  40. data/lib/kameleoon/managers/tracking/visitor_tracking_registry.rb +94 -0
  41. data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +22 -5
  42. data/lib/kameleoon/network/access_token_source.rb +46 -14
  43. data/lib/kameleoon/network/cookie/cookie_manager.rb +45 -7
  44. data/lib/kameleoon/network/net_provider.rb +2 -3
  45. data/lib/kameleoon/network/network_manager.rb +16 -21
  46. data/lib/kameleoon/network/request.rb +14 -3
  47. data/lib/kameleoon/network/response.rb +4 -0
  48. data/lib/kameleoon/network/url_provider.rb +11 -10
  49. data/lib/kameleoon/real_time/real_time_configuration_service.rb +10 -11
  50. data/lib/kameleoon/sdk_version.rb +31 -0
  51. data/lib/kameleoon/targeting/condition.rb +6 -2
  52. data/lib/kameleoon/targeting/condition_factory.rb +3 -0
  53. data/lib/kameleoon/targeting/conditions/browser_condition.rb +3 -3
  54. data/lib/kameleoon/targeting/conditions/cookie_condition.rb +10 -10
  55. data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +0 -1
  56. data/lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb +37 -0
  57. data/lib/kameleoon/targeting/conditions/number_condition.rb +4 -4
  58. data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +1 -2
  59. data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +2 -1
  60. data/lib/kameleoon/targeting/conditions/segment_condition.rb +3 -3
  61. data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -1
  62. data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +7 -11
  63. data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +1 -2
  64. data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +4 -4
  65. data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +5 -3
  66. data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +7 -6
  67. data/lib/kameleoon/targeting/models.rb +0 -14
  68. data/lib/kameleoon/targeting/targeting_manager.rb +37 -7
  69. data/lib/kameleoon/targeting/tree_builder.rb +10 -5
  70. data/lib/kameleoon/types/remote_visitor_data_filter.rb +21 -3
  71. data/lib/kameleoon/types/variable.rb +21 -0
  72. data/lib/kameleoon/types/variation.rb +22 -0
  73. data/lib/kameleoon/utils.rb +18 -0
  74. data/lib/kameleoon/version.rb +1 -27
  75. metadata +16 -2
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/data/visitor_visits'
3
4
  require 'kameleoon/targeting/conditions/number_condition'
4
5
 
5
6
  module Kameleoon
6
7
  # @api private
7
8
  module Targeting
8
-
9
9
  class VisitNumberTotalCondition < NumberCondition
10
10
  def initialize(json_condition)
11
11
  visit_count = json_condition['visitCount']
@@ -13,8 +13,10 @@ module Kameleoon
13
13
  end
14
14
 
15
15
  def check(data)
16
- return false unless data.is_a?(Kameleoon::VisitorVisits) && @condition_value != nil
17
- check_targeting(data.previous_visit_timestamps.size + 1) # +1 for current visit
16
+ return false unless VisitorVisits.visitor_visits?(data) && !@condition_value.nil?
17
+
18
+ previous_visit_timestamps = VisitorVisits.get_previous_visit_timestamps(data)
19
+ check_targeting(previous_visit_timestamps.size + 1) # +1 for current visit
18
20
  end
19
21
  end
20
22
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kameleoon/data/cookie'
4
- require 'kameleoon/version'
3
+ require 'kameleoon/data/visitor_visits'
5
4
 
6
5
  module Kameleoon
7
6
  # @api private
@@ -18,14 +17,16 @@ module Kameleoon
18
17
  end
19
18
 
20
19
  def check(data)
21
- return false unless data.is_a?(Kameleoon::VisitorVisits)
20
+ return false unless VisitorVisits.visitor_visits?(data)
21
+
22
+ previous_visit_timestamps = VisitorVisits.get_previous_visit_timestamps(data)
22
23
  case @visitor_type
23
24
  when VisitorType::NEW
24
- return data.previous_visit_timestamps.size == 0
25
+ previous_visit_timestamps.empty?
25
26
  when VisitorType::RETURNING
26
- return data.previous_visit_timestamps.size > 0
27
+ !previous_visit_timestamps.empty?
27
28
  else
28
- return false
29
+ false
29
30
  end
30
31
  end
31
32
  end
@@ -10,7 +10,6 @@ module Kameleoon
10
10
  include TreeBuilder
11
11
  attr_accessor :id, :tree
12
12
  def to_s
13
- print("\nSegment id: #{@id}\n")
14
13
  @tree.to_s
15
14
  end
16
15
 
@@ -49,19 +48,6 @@ module Kameleoon
49
48
  class Tree
50
49
  attr_accessor :or_operator, :left_child, :right_child, :condition
51
50
 
52
- def to_s
53
- print("or_operator: #{@or_operator}\n")
54
- print("condition: #{@condition}")
55
- unless @left_child.nil?
56
- print('\nLeft child:\n ')
57
- @left_child.to_s
58
- end
59
- unless @right_child.nil?
60
- print('\nright child:\n ')
61
- @right_child.to_s
62
- end
63
- end
64
-
65
51
  def initialize(or_operator = nil, left_child = nil, right_child = nil, condition = nil)
66
52
  @or_operator = Marshal.load(Marshal.dump(or_operator))
67
53
  @left_child = left_child
@@ -1,25 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/logging/kameleoon_logger'
3
4
  require 'kameleoon/targeting/conditions/segment_condition'
4
5
 
5
6
  module Kameleoon
6
7
  # @api private
7
8
  module Targeting
8
9
  class TargetingManager
9
- def initialize(visitor_manager, data_file)
10
+ def initialize(data_manager, visitor_manager)
11
+ @data_manager = data_manager
10
12
  @visitor_manager = visitor_manager
11
- @data_file = data_file
12
13
  end
13
14
 
14
15
  def check_targeting(visitor_code, campaign_id, exp_ff_rule)
16
+ Logging::KameleoonLogger.debug(
17
+ "CALL: TargetingManager.check_targeting(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s)",
18
+ visitor_code, campaign_id, exp_ff_rule
19
+ )
15
20
  segment = exp_ff_rule.targeting_segment
16
- return true if segment.nil?
21
+ if segment.nil?
22
+ Logging::KameleoonLogger.debug(
23
+ "RETURN: TargetingManager.check_targeting(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s) -> " \
24
+ '(targeting: true)', visitor_code, campaign_id, exp_ff_rule
25
+ )
26
+ return true
27
+ end
17
28
 
18
29
  visitor = @visitor_manager.get_visitor(visitor_code)
19
- segment.check_tree(->(type) { get_condition_data(type, visitor, visitor_code, campaign_id) })
30
+ targeting = segment.check_tree(->(type) { get_condition_data(type, visitor, visitor_code, campaign_id) })
31
+ Logging::KameleoonLogger.debug(
32
+ "RETURN: TargetingManager.check_targeting(visitorCode: '%s', campaignId: %s, exp_ff_rule: %s) -> " \
33
+ '(targeting: %s)', visitor_code, campaign_id, exp_ff_rule, targeting
34
+ )
35
+ targeting
20
36
  end
21
37
 
22
38
  def get_condition_data(type, visitor, visitor_code, campaign_id)
39
+ Logging::KameleoonLogger.debug(
40
+ "CALL: TargetingManager.get_condition_data(type: %s, visitor, visitorCode: '%s', campaignId: %s)",
41
+ type, visitor_code, campaign_id
42
+ )
23
43
  condition_data = nil
24
44
  case type
25
45
  when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM
@@ -41,7 +61,7 @@ module Kameleoon
41
61
  condition_data = visitor.operating_system unless visitor.nil?
42
62
  when Kameleoon::Targeting::ConditionType::SEGMENT
43
63
  condition_data = SegmentInfo.new(
44
- @data_file,
64
+ @data_manager.data_file,
45
65
  lambda { |type|
46
66
  get_condition_data(type, visitor, visitor_code, campaign_id)
47
67
  }
@@ -53,12 +73,22 @@ module Kameleoon
53
73
  when Kameleoon::Targeting::ConditionType::VISITOR_CODE
54
74
  condition_data = visitor_code
55
75
  when Kameleoon::Targeting::ConditionType::TARGET_FEATURE_FLAG
56
- condition_data = TargetFeatureFlagInfo.new(@data_file, visitor.variations) unless visitor.nil?
76
+ condition_data = TargetFeatureFlagInfo.new(@data_manager.data_file, visitor.variations) unless visitor.nil?
57
77
  when Kameleoon::Targeting::ConditionType::EXCLUSIVE_FEATURE_FLAG
58
78
  condition_data = ExclusiveFeatureFlagInfo.new(campaign_id, visitor&.variations)
59
- when ConditionType::FIRST_VISIT, ConditionType::LAST_VISIT, ConditionType::VISITS, ConditionType::SAME_DAY_VISITS, ConditionType::NEW_VISITORS
79
+ when ConditionType::FIRST_VISIT,
80
+ ConditionType::LAST_VISIT,
81
+ ConditionType::VISITS,
82
+ ConditionType::SAME_DAY_VISITS,
83
+ ConditionType::NEW_VISITORS
60
84
  condition_data = visitor.visitor_visits unless visitor.nil?
85
+ when Kameleoon::Targeting::ConditionType::HEAT_SLICE
86
+ condition_data = visitor&.kcs_heat
61
87
  end
88
+ Logging::KameleoonLogger.debug(
89
+ "RETURN: TargetingManager.get_condition_data(type: %s, visitor, visitor_code: '%s', campaign_id: %s) -> " \
90
+ '(condition_data: %s)', type, visitor_code, campaign_id, condition_data
91
+ )
62
92
  condition_data
63
93
  end
64
94
  end
@@ -1,4 +1,5 @@
1
1
  require 'kameleoon/targeting/condition_factory'
2
+ require 'kameleoon/logging/kameleoon_logger'
2
3
 
3
4
  module Kameleoon
4
5
  #@api private
@@ -6,13 +7,17 @@ module Kameleoon
6
7
  module TreeBuilder
7
8
 
8
9
  def create_tree(conditions_data_json)
9
- if conditions_data_json.nil?
10
- return nil
11
- end
10
+ return nil if conditions_data_json.nil?
11
+
12
+ Logging::KameleoonLogger.debug('CALL: TreeBuilder.create_tree(conditions_data_json: %s)',
13
+ conditions_data_json)
12
14
  if conditions_data_json['firstLevel'].empty?
13
15
  conditions_data_json['firstLevelOrOperators'] = []
14
16
  end
15
- create_first_level(conditions_data_json)
17
+ tree = create_first_level(conditions_data_json)
18
+ Logging::KameleoonLogger.debug('RETURN: TreeBuilder.create_tree(conditions_data_json: %s) -> (tree)',
19
+ conditions_data_json)
20
+ tree
16
21
  end
17
22
 
18
23
  private
@@ -69,4 +74,4 @@ module Kameleoon
69
74
  end
70
75
  end
71
76
  end
72
- end
77
+ end
@@ -3,11 +3,28 @@
3
3
  module Kameleoon
4
4
  # Module which contains all internal data of SDK
5
5
  module Types
6
-
7
6
  class RemoteVisitorDataFilter
8
- attr_reader :previous_visit_amount, :current_visit, :custom_data, :page_views, :geolocation, :device, :browser, :operating_system, :conversions, :experiments
7
+ attr_reader :previous_visit_amount, :current_visit, :custom_data, :page_views, :geolocation, :device, :browser,
8
+ :operating_system, :conversions, :experiments, :kcs
9
+
10
+ def to_s
11
+ "RemoteVisitorDataFilter{previous_visit_amount:#{@previous_visit_amount}," \
12
+ "current_visit:#{@current_visit}," \
13
+ "custom_data:#{@custom_data}," \
14
+ "page_views:#{@page_views}," \
15
+ "geolocation:#{@geolocation}," \
16
+ "device:#{@device}," \
17
+ "browser:#{@browser}," \
18
+ "operating_system:#{@operating_system}," \
19
+ "conversions:#{@conversions}," \
20
+ "experiments:#{@experiments},kcs:#{@kcs}}"
21
+ end
9
22
 
10
- def initialize(previous_visit_amount: 1, current_visit: true, custom_data: true, page_views: false, geolocation: false, device: false, browser: false, operating_system: false, conversions: false, experiments: false)
23
+ def initialize(
24
+ previous_visit_amount: 1, current_visit: true, custom_data: true, page_views: false,
25
+ geolocation: false, device: false, browser: false, operating_system: false, conversions: false,
26
+ experiments: false, kcs: false
27
+ )
11
28
  @previous_visit_amount = previous_visit_amount
12
29
  @current_visit = current_visit
13
30
  @custom_data = custom_data
@@ -18,6 +35,7 @@ module Kameleoon
18
35
  @operating_system = operating_system
19
36
  @conversions = conversions
20
37
  @experiments = experiments
38
+ @kcs = kcs
21
39
  end
22
40
  end
23
41
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kameleoon
4
+ # Module which contains all internal data of SDK
5
+ module Types
6
+ # Variable
7
+ class Variable
8
+ attr_reader :key, :type, :value
9
+
10
+ def to_s
11
+ "Variable{key:'#{@key}',type:'#{@type}',value:#{@value}}"
12
+ end
13
+
14
+ def initialize(key, type, value)
15
+ @key = key
16
+ @type = type
17
+ @value = value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kameleoon
4
+ # Module which contains all internal data of SDK
5
+ module Types
6
+ # Variation
7
+ class Variation
8
+ attr_reader :key, :id, :experiment_id, :variables
9
+
10
+ def to_s
11
+ "Variation{key:'#{@key}',id:#{@id},experiment_id:#{@experiment_id},variables:#{@variables}}"
12
+ end
13
+
14
+ def initialize(key, id, experiment_id, variables)
15
+ @key = key
16
+ @id = id
17
+ @experiment_id = experiment_id
18
+ @variables = variables
19
+ end
20
+ end
21
+ end
22
+ end
@@ -50,5 +50,23 @@ module Kameleoon
50
50
  (Digest::SHA256.hexdigest(identifier.encode('UTF-8')).to_i(16) / (BigDecimal('2')**BigDecimal('256'))).round(16)
51
51
  end
52
52
  end
53
+
54
+ module Strval
55
+
56
+ def self.secret(secret)
57
+ hid_ch = '*'
58
+ vis_count = 4
59
+
60
+ return 'nil' if secret.nil?
61
+
62
+ length = secret.length
63
+
64
+ return hid_ch * length if length <= vis_count
65
+
66
+ hidden_length = [length - vis_count, vis_count].max
67
+
68
+ secret[0, length - hidden_length] + hid_ch * hidden_length
69
+ end
70
+ end
53
71
  end
54
72
  end
@@ -1,32 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kameleoon
4
+ SDK_VERSION = '3.4.0'
4
5
  SDK_NAME = 'RUBY'
5
- SDK_VERSION = '3.2.0'
6
-
7
- # SdkManager is a helper method for fetching / obtaining version of SDK from string
8
- class SdkVersion
9
- def self.get_version_components(version_string)
10
- versions = [0, 0, 0]
11
-
12
- version_parts = version_string.split('.')
13
- version_parts.each_with_index do |part, i|
14
- versions[i] = Integer(part)
15
- rescue ArgumentError
16
- puts "Invalid version component, index: #{i}, value: #{part}"
17
- return nil
18
- end
19
- versions
20
- end
21
-
22
- def self.get_float_version(version_string)
23
- version_components = get_version_components(version_string)
24
-
25
- return Float::NAN if version_components.nil?
26
-
27
- major = version_components[0]
28
- minor = version_components[1]
29
- "#{major}.#{minor}".to_f
30
- end
31
- end
32
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kameleoon-client-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-22 00:00:00.000000000 Z
11
+ date: 2024-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request
@@ -91,14 +91,17 @@ files:
91
91
  - lib/kameleoon/data/data.rb
92
92
  - lib/kameleoon/data/device.rb
93
93
  - lib/kameleoon/data/geolocation.rb
94
+ - lib/kameleoon/data/kcs_heat.rb
94
95
  - lib/kameleoon/data/manager/assigned_variation.rb
95
96
  - lib/kameleoon/data/manager/data_array_storage.rb
96
97
  - lib/kameleoon/data/manager/data_map_storage.rb
97
98
  - lib/kameleoon/data/manager/page_view_visit.rb
98
99
  - lib/kameleoon/data/manager/visitor.rb
99
100
  - lib/kameleoon/data/manager/visitor_manager.rb
101
+ - lib/kameleoon/data/mapping_identifier.rb
100
102
  - lib/kameleoon/data/operating_system.rb
101
103
  - lib/kameleoon/data/page_view.rb
104
+ - lib/kameleoon/data/unique_identifier.rb
102
105
  - lib/kameleoon/data/user_agent.rb
103
106
  - lib/kameleoon/data/visitor_visits.rb
104
107
  - lib/kameleoon/exceptions.rb
@@ -106,8 +109,15 @@ files:
106
109
  - lib/kameleoon/kameleoon_client.rb
107
110
  - lib/kameleoon/kameleoon_client_config.rb
108
111
  - lib/kameleoon/kameleoon_client_factory.rb
112
+ - lib/kameleoon/logging/default_logger.rb
113
+ - lib/kameleoon/logging/kameleoon_logger.rb
114
+ - lib/kameleoon/logging/logger.rb
115
+ - lib/kameleoon/managers/data/data_manager.rb
109
116
  - lib/kameleoon/managers/remote_data/remote_data_manager.rb
110
117
  - lib/kameleoon/managers/remote_data/remote_visitor_data.rb
118
+ - lib/kameleoon/managers/tracking/tracking_builder.rb
119
+ - lib/kameleoon/managers/tracking/tracking_manager.rb
120
+ - lib/kameleoon/managers/tracking/visitor_tracking_registry.rb
111
121
  - lib/kameleoon/managers/warehouse/warehouse_manager.rb
112
122
  - lib/kameleoon/network/access_token_source.rb
113
123
  - lib/kameleoon/network/activity_event.rb
@@ -125,6 +135,7 @@ files:
125
135
  - lib/kameleoon/real_time/sse_client.rb
126
136
  - lib/kameleoon/real_time/sse_message.rb
127
137
  - lib/kameleoon/real_time/sse_request.rb
138
+ - lib/kameleoon/sdk_version.rb
128
139
  - lib/kameleoon/storage/cache.rb
129
140
  - lib/kameleoon/storage/cache_factory.rb
130
141
  - lib/kameleoon/targeting/condition.rb
@@ -136,6 +147,7 @@ files:
136
147
  - lib/kameleoon/targeting/conditions/device_condition.rb
137
148
  - lib/kameleoon/targeting/conditions/exclusive_feature_flag_condition.rb
138
149
  - lib/kameleoon/targeting/conditions/geolocation_condition.rb
150
+ - lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb
139
151
  - lib/kameleoon/targeting/conditions/number_condition.rb
140
152
  - lib/kameleoon/targeting/conditions/operating_system_condition.rb
141
153
  - lib/kameleoon/targeting/conditions/page_title_condition.rb
@@ -156,6 +168,8 @@ files:
156
168
  - lib/kameleoon/targeting/targeting_manager.rb
157
169
  - lib/kameleoon/targeting/tree_builder.rb
158
170
  - lib/kameleoon/types/remote_visitor_data_filter.rb
171
+ - lib/kameleoon/types/variable.rb
172
+ - lib/kameleoon/types/variation.rb
159
173
  - lib/kameleoon/utils.rb
160
174
  - lib/kameleoon/version.rb
161
175
  homepage: https://developers.kameleoon.com/ruby-sdk.html