kameleoon-client-ruby 2.1.0 → 2.1.1
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.rb +79 -133
- data/lib/kameleoon/configuration/rule.rb +1 -1
- data/lib/kameleoon/data/browser.rb +33 -0
- data/lib/kameleoon/data/conversion.rb +26 -0
- data/lib/kameleoon/data/custom_data.rb +53 -0
- data/lib/kameleoon/data/data.rb +35 -0
- data/lib/kameleoon/data/device.rb +26 -0
- data/lib/kameleoon/data/page_view.rb +31 -0
- data/lib/kameleoon/data/user_agent.rb +14 -0
- data/lib/kameleoon/network/activity_event.rb +31 -0
- data/lib/kameleoon/network/experiment_event.rb +35 -0
- data/lib/kameleoon/network/uri_helper.rb +36 -0
- data/lib/kameleoon/network/url_provider.rb +71 -0
- data/lib/kameleoon/request.rb +5 -19
- data/lib/kameleoon/targeting/condition.rb +40 -11
- data/lib/kameleoon/targeting/condition_factory.rb +35 -12
- data/lib/kameleoon/targeting/conditions/browser_condition.rb +71 -0
- data/lib/kameleoon/targeting/conditions/conversion_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/custom_datum.rb +60 -65
- data/lib/kameleoon/targeting/conditions/device_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -12
- data/lib/kameleoon/targeting/conditions/page_title_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/page_url_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +65 -0
- data/lib/kameleoon/targeting/conditions/string_value_condition.rb +40 -0
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +4 -8
- data/lib/kameleoon/targeting/conditions/unknown_condition.rb +15 -0
- data/lib/kameleoon/targeting/conditions/visitor_code_condition.rb +16 -0
- data/lib/kameleoon/targeting/models.rb +0 -24
- data/lib/kameleoon/utils.rb +1 -1
- data/lib/kameleoon/version.rb +28 -1
- metadata +22 -3
- data/lib/kameleoon/data.rb +0 -175
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/data/data'
|
4
|
+
require 'kameleoon/version'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
# @api private
|
8
|
+
module Targeting
|
9
|
+
# Helper class for containing sdk information
|
10
|
+
class SdkInfo
|
11
|
+
attr_reader :language, :version
|
12
|
+
|
13
|
+
def initialize(language, version)
|
14
|
+
@language = language
|
15
|
+
@version = version
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# SdkLanguageCondition is a condition for checking targeting sdk type and version
|
20
|
+
class SdkLanguageCondition < Condition
|
21
|
+
def initialize(json_condition)
|
22
|
+
super(json_condition)
|
23
|
+
@sdk_language = json_condition['sdkLanguage']
|
24
|
+
@version = json_condition['version']
|
25
|
+
@operator = json_condition['versionMatchType']
|
26
|
+
end
|
27
|
+
|
28
|
+
def check(data)
|
29
|
+
data.is_a?(SdkInfo) && check_targeting(data)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check_targeting(sdk_info)
|
35
|
+
return false if @sdk_language != sdk_info.language
|
36
|
+
|
37
|
+
return true if @version.nil?
|
38
|
+
|
39
|
+
version_components_condition = SdkVersion.get_version_components(@version)
|
40
|
+
version_components_sdk_info = SdkVersion.get_version_components(sdk_info.version)
|
41
|
+
|
42
|
+
return false if version_components_condition.nil? || version_components_sdk_info.nil?
|
43
|
+
|
44
|
+
major_condition, minor_condition, patch_condition = version_components_condition
|
45
|
+
major_sdk, minor_sdk, patch_sdk = version_components_sdk_info
|
46
|
+
|
47
|
+
case @operator
|
48
|
+
when Operator::EQUAL
|
49
|
+
major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk == patch_condition
|
50
|
+
when Operator::GREATER
|
51
|
+
major_sdk > major_condition ||
|
52
|
+
(major_sdk == major_condition && minor_sdk > minor_condition) ||
|
53
|
+
(major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk > patch_condition)
|
54
|
+
when Operator::LOWER
|
55
|
+
major_sdk < major_condition ||
|
56
|
+
(major_sdk == major_condition && minor_sdk < minor_condition) ||
|
57
|
+
(major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk < patch_condition)
|
58
|
+
else
|
59
|
+
puts "Unexpected comparing operation for SdkLanguage condition: #{@operator}"
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/targeting/condition'
|
4
|
+
|
5
|
+
module Kameleoon
|
6
|
+
# @api private
|
7
|
+
module Targeting
|
8
|
+
# StringValueCondition is a base class for all conditions where string comparison happens
|
9
|
+
class StringValueCondition < Condition
|
10
|
+
def initialize(json_condition, value)
|
11
|
+
super(json_condition)
|
12
|
+
@condition_value = value
|
13
|
+
@operator = json_condition['matchType']
|
14
|
+
end
|
15
|
+
|
16
|
+
def check(data)
|
17
|
+
data.is_a?(String) && check_targeting(data)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def check_targeting(value)
|
23
|
+
return false if value.nil?
|
24
|
+
|
25
|
+
case @operator
|
26
|
+
when Operator::EXACT
|
27
|
+
value == @condition_value
|
28
|
+
when Operator::CONTAINS
|
29
|
+
value.include?(@condition_value)
|
30
|
+
when Operator::REGULAR_EXPRESSION
|
31
|
+
pattern = Regexp.new(@condition_value)
|
32
|
+
pattern.match?(value)
|
33
|
+
else
|
34
|
+
puts "Unexpected comparing operation for #{@type} condition: #{@operator}"
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -13,16 +13,12 @@ module Kameleoon
|
|
13
13
|
def initialize(json_condition)
|
14
14
|
super(json_condition)
|
15
15
|
|
16
|
-
if json_condition['experiment'].nil?
|
17
|
-
raise Exception::NotFound.new('experiment'), 'experiment missed'
|
18
|
-
end
|
19
|
-
|
20
|
-
@experiment = json_condition['experiment']
|
21
|
-
|
16
|
+
raise Exception::NotFound.new('experiment'), 'experiment missed' if json_condition['experiment'].nil?
|
22
17
|
if json_condition['variationMatchType'].nil?
|
23
18
|
raise Exception::NotFound.new('variationMatchType'), 'variationMatchType missed'
|
24
19
|
end
|
25
20
|
|
21
|
+
@experiment = json_condition['experiment']
|
26
22
|
@operator = json_condition['variationMatchType']
|
27
23
|
@variation = json_condition['variation']
|
28
24
|
end
|
@@ -32,9 +28,9 @@ module Kameleoon
|
|
32
28
|
variation_storage_exist = !variation_storage.nil? && !variation_storage.empty?
|
33
29
|
saved_variation = variation_storage[@experiment] unless variation_storage.nil?
|
34
30
|
case @operator
|
35
|
-
when Operator::EXACT
|
31
|
+
when Operator::EXACT
|
36
32
|
is_targeted = variation_storage_exist && saved_variation == @variation
|
37
|
-
when Operator::ANY
|
33
|
+
when Operator::ANY
|
38
34
|
is_targeted = variation_storage_exist
|
39
35
|
end
|
40
36
|
is_targeted
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/targeting/condition'
|
4
|
+
|
5
|
+
module Kameleoon
|
6
|
+
# @api private
|
7
|
+
module Targeting
|
8
|
+
# UnknownCondition represents not defined condition, always returns that visitor is targeted (true)
|
9
|
+
class UnknownCondition < Condition
|
10
|
+
def check(_data)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/data/data'
|
4
|
+
require_relative 'string_value_condition'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
# @api private
|
8
|
+
module Targeting
|
9
|
+
# VisitorCodeCondition is a condition for checking of targeting by visitor code
|
10
|
+
class VisitorCodeCondition < StringValueCondition
|
11
|
+
def initialize(json_condition)
|
12
|
+
super(json_condition, json_condition['visitorCode'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -133,29 +133,5 @@ module Kameleoon
|
|
133
133
|
Marshal.load(Marshal.dump(is_targeted)) # Deep copy
|
134
134
|
end
|
135
135
|
end
|
136
|
-
|
137
|
-
module DataType
|
138
|
-
CUSTOM = 'CUSTOM'
|
139
|
-
end
|
140
|
-
|
141
|
-
module ConditionType
|
142
|
-
CUSTOM_DATUM = 'CUSTOM_DATUM'
|
143
|
-
TARGET_EXPERIMENT = 'TARGET_EXPERIMENT'
|
144
|
-
EXCLUSIVE_EXPERIMENT = 'EXCLUSIVE_EXPERIMENT'
|
145
|
-
end
|
146
|
-
|
147
|
-
module Operator
|
148
|
-
UNDEFINED = 'UNDEFINED'
|
149
|
-
CONTAINS = 'CONTAINS'
|
150
|
-
EXACT = 'EXACT'
|
151
|
-
MATCH = 'REGULAR_EXPRESSION'
|
152
|
-
LOWER = 'LOWER'
|
153
|
-
EQUAL = 'EQUAL'
|
154
|
-
GREATER = 'GREATER'
|
155
|
-
IS_TRUE = 'TRUE'
|
156
|
-
IS_FALSE = 'FALSE'
|
157
|
-
AMONG_VALUES = 'AMONG_VALUES'
|
158
|
-
ANY = 'ANY'
|
159
|
-
end
|
160
136
|
end
|
161
137
|
end
|
data/lib/kameleoon/utils.rb
CHANGED
data/lib/kameleoon/version.rb
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Kameleoon
|
4
|
-
|
4
|
+
SDK_NAME = 'RUBY'
|
5
|
+
SDK_VERSION = '2.1.1'
|
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
|
5
32
|
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: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kameleoon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -84,10 +84,20 @@ files:
|
|
84
84
|
- lib/kameleoon/configuration/variation.rb
|
85
85
|
- lib/kameleoon/configuration/variation_exposition.rb
|
86
86
|
- lib/kameleoon/cookie.rb
|
87
|
-
- lib/kameleoon/data.rb
|
87
|
+
- lib/kameleoon/data/browser.rb
|
88
|
+
- lib/kameleoon/data/conversion.rb
|
89
|
+
- lib/kameleoon/data/custom_data.rb
|
90
|
+
- lib/kameleoon/data/data.rb
|
91
|
+
- lib/kameleoon/data/device.rb
|
92
|
+
- lib/kameleoon/data/page_view.rb
|
93
|
+
- lib/kameleoon/data/user_agent.rb
|
88
94
|
- lib/kameleoon/exceptions.rb
|
89
95
|
- lib/kameleoon/factory.rb
|
90
96
|
- lib/kameleoon/hybrid/manager.rb
|
97
|
+
- lib/kameleoon/network/activity_event.rb
|
98
|
+
- lib/kameleoon/network/experiment_event.rb
|
99
|
+
- lib/kameleoon/network/uri_helper.rb
|
100
|
+
- lib/kameleoon/network/url_provider.rb
|
91
101
|
- lib/kameleoon/real_time/real_time_configuration_service.rb
|
92
102
|
- lib/kameleoon/real_time/real_time_event.rb
|
93
103
|
- lib/kameleoon/real_time/sse_client.rb
|
@@ -100,9 +110,18 @@ files:
|
|
100
110
|
- lib/kameleoon/storage/visitor_variation.rb
|
101
111
|
- lib/kameleoon/targeting/condition.rb
|
102
112
|
- lib/kameleoon/targeting/condition_factory.rb
|
113
|
+
- lib/kameleoon/targeting/conditions/browser_condition.rb
|
114
|
+
- lib/kameleoon/targeting/conditions/conversion_condition.rb
|
103
115
|
- lib/kameleoon/targeting/conditions/custom_datum.rb
|
116
|
+
- lib/kameleoon/targeting/conditions/device_condition.rb
|
104
117
|
- lib/kameleoon/targeting/conditions/exclusive_experiment.rb
|
118
|
+
- lib/kameleoon/targeting/conditions/page_title_condition.rb
|
119
|
+
- lib/kameleoon/targeting/conditions/page_url_condition.rb
|
120
|
+
- lib/kameleoon/targeting/conditions/sdk_language_condition.rb
|
121
|
+
- lib/kameleoon/targeting/conditions/string_value_condition.rb
|
105
122
|
- lib/kameleoon/targeting/conditions/target_experiment.rb
|
123
|
+
- lib/kameleoon/targeting/conditions/unknown_condition.rb
|
124
|
+
- lib/kameleoon/targeting/conditions/visitor_code_condition.rb
|
106
125
|
- lib/kameleoon/targeting/models.rb
|
107
126
|
- lib/kameleoon/targeting/tree_builder.rb
|
108
127
|
- lib/kameleoon/utils.rb
|
data/lib/kameleoon/data.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'kameleoon/exceptions'
|
4
|
-
|
5
|
-
module Kameleoon
|
6
|
-
NONCE_LENGTH = 16
|
7
|
-
|
8
|
-
module DataType
|
9
|
-
CUSTOM = 'CUSTOM'
|
10
|
-
BROWSER = 'BROWSER'
|
11
|
-
CONVERSION = 'CONVERSION'
|
12
|
-
DEVICE = 'DEVICE'
|
13
|
-
PAGE_VIEW = 'PAGE_VIEW'
|
14
|
-
end
|
15
|
-
|
16
|
-
module BrowserType
|
17
|
-
CHROME = 0
|
18
|
-
INTERNET_EXPLORER = 1
|
19
|
-
FIREFOX = 2
|
20
|
-
SAFARI = 3
|
21
|
-
OPERA = 4
|
22
|
-
OTHER = 5
|
23
|
-
end
|
24
|
-
|
25
|
-
module DeviceType
|
26
|
-
PHONE = 'PHONE'
|
27
|
-
TABLET = 'TABLET'
|
28
|
-
DESKTOP = 'DESKTOP'
|
29
|
-
end
|
30
|
-
|
31
|
-
class Data
|
32
|
-
attr_accessor :instance, :sent
|
33
|
-
|
34
|
-
def initialize(*args)
|
35
|
-
raise KameleoonError.new("Cannot initialise interface.")
|
36
|
-
end
|
37
|
-
|
38
|
-
def obtain_full_post_text_line
|
39
|
-
raise KameleoonError.new("ToDo: implement this method.")
|
40
|
-
end
|
41
|
-
|
42
|
-
def encode(url)
|
43
|
-
encoded_url = CGI.escape(url)
|
44
|
-
encoded_url.gsub! "%27", "'"
|
45
|
-
encoded_url.gsub! "%21", "!"
|
46
|
-
encoded_url.gsub! "+", "%20"
|
47
|
-
encoded_url.gsub! "%2A", "*"
|
48
|
-
encoded_url.gsub! "%28", "("
|
49
|
-
encoded_url.gsub! "%29", ")"
|
50
|
-
encoded_url
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class CustomData < Data
|
55
|
-
attr_reader :id, :values
|
56
|
-
|
57
|
-
# @param [Integer] id Id of the custom data
|
58
|
-
# @param [String] value Value of the custom data
|
59
|
-
#
|
60
|
-
# @overload
|
61
|
-
# @param [Hash] hash Json value encoded in a hash.
|
62
|
-
def initialize(arg0, *args)
|
63
|
-
@instance = DataType::CUSTOM
|
64
|
-
@sent = false
|
65
|
-
if arg0.is_a?(Hash)
|
66
|
-
hash = arg0
|
67
|
-
id = hash['id']
|
68
|
-
raise Kameleoon::Exception::NotFound.new('id') if id.nil?
|
69
|
-
@id = id.to_s
|
70
|
-
value = hash['value']
|
71
|
-
values = hash['values']
|
72
|
-
raise Kameleoon::Exception::NotFound.new('values') if values.nil? && value.nil?
|
73
|
-
if values.nil?
|
74
|
-
@values = [value]
|
75
|
-
else
|
76
|
-
@values = values.dup
|
77
|
-
@values.append(value) unless value.nil?
|
78
|
-
end
|
79
|
-
else
|
80
|
-
@id = arg0.to_s
|
81
|
-
@values = args
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def obtain_full_post_text_line
|
86
|
-
return '' if @values.empty?
|
87
|
-
|
88
|
-
str_values = "[[\"#{@values.join('",1],["')}\",1]]"
|
89
|
-
nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
|
90
|
-
"eventType=customData&index=#{@id}&valueToCount=#{encode(str_values)}&overwrite=true&nonce=#{nonce}"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
class Browser < Data
|
95
|
-
attr_accessor :browser
|
96
|
-
|
97
|
-
# @param [BrowserType] browser_type Browser type, can be: CHROME, INTERNET_EXPLORER, FIREFOX, SAFARI, OPERA, OTHER
|
98
|
-
def initialize(browser_type)
|
99
|
-
@instance = DataType::BROWSER
|
100
|
-
@sent = false
|
101
|
-
@browser = browser_type
|
102
|
-
end
|
103
|
-
|
104
|
-
def obtain_full_post_text_line
|
105
|
-
nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
|
106
|
-
"eventType=staticData&browserIndex=" + @browser.to_s + "&nonce=" + nonce
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
class PageView < Data
|
111
|
-
attr_accessor :url, :title, :referrer
|
112
|
-
|
113
|
-
# @param [String] url Url of the page
|
114
|
-
# @param [String] title Title of the page
|
115
|
-
# @param [Array] referrers Optional field - Referrer ids
|
116
|
-
def initialize(url, title, referrers = nil)
|
117
|
-
@instance = DataType::PAGE_VIEW
|
118
|
-
@sent = false
|
119
|
-
@url = url
|
120
|
-
@title = title
|
121
|
-
@referrers = referrers
|
122
|
-
end
|
123
|
-
|
124
|
-
def obtain_full_post_text_line
|
125
|
-
nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
|
126
|
-
referrer_text = ''
|
127
|
-
unless @referrers.nil?
|
128
|
-
referrer_text = "&referrersIndices=" + @referrers.to_s
|
129
|
-
end
|
130
|
-
"eventType=page&href=" + encode(@url) + "&title=" + @title + referrer_text + "&nonce=" + nonce
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
class Conversion < Data
|
135
|
-
attr_accessor :goal_id, :revenue, :negative
|
136
|
-
|
137
|
-
# @param [Integer] goal_id Id of the goal associated to the conversion
|
138
|
-
# @param [Float] revenue Optional field - Revenue associated to the conversion.
|
139
|
-
# @param [Boolean] negative Optional field - If the revenue is negative. By default it's positive.
|
140
|
-
def initialize(goal_id, revenue = 0.0, negative = false)
|
141
|
-
@instance = DataType::CONVERSION
|
142
|
-
@sent = false
|
143
|
-
@goal_id = goal_id
|
144
|
-
@revenue = revenue
|
145
|
-
@negative = negative
|
146
|
-
end
|
147
|
-
|
148
|
-
def obtain_full_post_text_line
|
149
|
-
nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
|
150
|
-
"eventType=conversion&goalId=" + @goal_id.to_s + "&revenue=" + @revenue.to_s + "&negative=" + @negative.to_s + "&nonce=" + nonce
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# Device uses for sending deviceType parameter for tracking calls
|
155
|
-
class Device < Data
|
156
|
-
def initialize(device_type)
|
157
|
-
@instance = DataType::DEVICE
|
158
|
-
@sent = false
|
159
|
-
@device_type = device_type
|
160
|
-
end
|
161
|
-
|
162
|
-
def obtain_full_post_text_line
|
163
|
-
nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
|
164
|
-
"eventType=staticData&deviceType=#{@device_type}&nonce=#{nonce}"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# UserAgent uses for changing User-Agent header for tracking calls
|
169
|
-
class UserAgent
|
170
|
-
attr_accessor :value
|
171
|
-
def initialize(value)
|
172
|
-
@value = value
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|