kameleoon-client-ruby 1.0.0 → 1.0.5
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/README.md +7 -3
- data/lib/kameleoon.rb +1 -1
- data/lib/kameleoon/client.rb +118 -53
- data/lib/kameleoon/exceptions.rb +7 -2
- data/lib/kameleoon/factory.rb +4 -2
- data/lib/kameleoon/request.rb +11 -1
- data/lib/kameleoon/version.rb +3 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0deee13cb73acd60d605f479ab0e2ebf374e38e7516d9bea0590b0adbdd7f53
|
4
|
+
data.tar.gz: 7c06c8d843f6f291d946375d495a3d573e31f932942c5b6edb2f86abf4d9183f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d14ce02c0a2577988a0161292683e9a455c9532dda0fd69e5428d0f0a2bd08bb38114cf8b161d4a123921e08429cf1db126f7b3033d43a0889c4f6eea2d064b
|
7
|
+
data.tar.gz: de09c74c6587cbd9f53f44a6673b1d3e2832d3aa135376405d0e7eaef9933bc0891243cc34cb039d2eb72b8b8fa5eeb5045b9e4c69b4a4979aabcae154e8361b
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ This is the repository for the Kameleoon Ruby SDK.
|
|
6
6
|
#### Prerequisite:
|
7
7
|
* [Install ruby](https://www.ruby-lang.org/en/documentation/installation)
|
8
8
|
|
9
|
-
#### Build and install:
|
9
|
+
#### Build and install the gem:
|
10
10
|
* Run `./buildAndInstallGem.sh`
|
11
11
|
|
12
12
|
### How to run tests
|
@@ -14,5 +14,9 @@ This is the repository for the Kameleoon Ruby SDK.
|
|
14
14
|
* Build and install Kameleoon Gem locally (infos above).
|
15
15
|
* Install rake: `gem install rake`
|
16
16
|
|
17
|
-
####
|
18
|
-
|
17
|
+
#### Run Tests:
|
18
|
+
##### Unit
|
19
|
+
* Run `./test.sh -u`
|
20
|
+
##### Integration
|
21
|
+
Make sure you kill manually the server test app after the tests are done.
|
22
|
+
* Run `./test.sh -i`
|
data/lib/kameleoon.rb
CHANGED
data/lib/kameleoon/client.rb
CHANGED
@@ -19,16 +19,18 @@ module Kameleoon
|
|
19
19
|
|
20
20
|
##
|
21
21
|
# You should create Client with the Client Factory only.
|
22
|
+
#
|
22
23
|
def initialize(site_code, path_config_file, blocking, interval, default_timeout, client_id = nil, client_secret = nil)
|
23
24
|
config = YAML.load_file(path_config_file)
|
24
25
|
@site_code = site_code
|
25
26
|
@blocking = blocking
|
26
|
-
@default_timeout = config['default_timeout'] || default_timeout
|
27
|
-
@interval = config['actions_configuration_refresh_interval'] || interval
|
27
|
+
@default_timeout = config['default_timeout'] || default_timeout # in ms
|
28
|
+
@interval = config['actions_configuration_refresh_interval'].to_s + 'm' || interval
|
28
29
|
@tracking_url = config['tracking_url'] || "https://api-ssx.kameleoon.com"
|
29
30
|
@client_id = client_id || config['client_id']
|
30
31
|
@client_secret = client_secret || config['client_secret']
|
31
|
-
@data_maximum_size = config['visitor_data_maximum_size'] || 500
|
32
|
+
@data_maximum_size = config['visitor_data_maximum_size'] || 500 # mb
|
33
|
+
@verbose_mode = config['verbose_mode'] || false
|
32
34
|
@experiments = []
|
33
35
|
@feature_flags = []
|
34
36
|
@data = {}
|
@@ -37,7 +39,6 @@ module Kameleoon
|
|
37
39
|
##
|
38
40
|
# Obtain a visitor code.
|
39
41
|
#
|
40
|
-
#
|
41
42
|
# This method should be called to obtain the Kameleoon visitor_code for the current visitor.
|
42
43
|
# This is especially important when using Kameleoon in a mixed front-end and back-end environment,
|
43
44
|
# where user identification consistency must be guaranteed.
|
@@ -51,10 +52,9 @@ module Kameleoon
|
|
51
52
|
# In any case, the server-side (via HTTP header) kameleoonVisitorCode cookie is set with the value. Then this
|
52
53
|
# identifier value is finally returned by the method.
|
53
54
|
#
|
54
|
-
#
|
55
55
|
# @param [Hash] cookies Cookies of the request.
|
56
56
|
# @param [String] top_level_domain Top level domain of your website, settled while writing cookie.
|
57
|
-
# @param [String]
|
57
|
+
# @param [String] default_visitor_code - Optional - Define your default visitor_code (maximum length 100 chars).
|
58
58
|
#
|
59
59
|
# @return [String] visitor code
|
60
60
|
#
|
@@ -68,6 +68,7 @@ module Kameleoon
|
|
68
68
|
|
69
69
|
##
|
70
70
|
# Trigger an experiment.
|
71
|
+
#
|
71
72
|
# If such a visitor_code has never been associated with any variation, the SDK returns a randomly selected variation.
|
72
73
|
# If a user with a given visitor_code is already registered with a variation, it will detect the previously
|
73
74
|
# registered variation and return the variation_id.
|
@@ -77,29 +78,33 @@ module Kameleoon
|
|
77
78
|
# @param [String] visitor_code Visitor code
|
78
79
|
# @param [Integer] experiment_id Id of the experiment you want to trigger.
|
79
80
|
#
|
80
|
-
# @return [Integer]
|
81
|
+
# @return [Integer] Id of the variation
|
81
82
|
#
|
82
|
-
# @raise [Kameleoon::Exception::
|
83
|
+
# @raise [Kameleoon::Exception::ExperimentConfigurationNotFound] Raise when experiment configuration is not found
|
83
84
|
# @raise [Kameleoon::Exception::NotActivated] The visitor triggered the experiment, but did not activate it. Usually, this happens because the user has been associated with excluded traffic
|
84
85
|
# @raise [Kameleoon::Exception::NotTargeted] The visitor is not targeted by the experiment, as the associated targeting segment conditions were not fulfilled. He should see the reference variation
|
86
|
+
#
|
85
87
|
def trigger_experiment(visitor_code, experiment_id, timeout = @default_timeout)
|
86
88
|
experiment = @experiments.find { |experiment| experiment['id'].to_s == experiment_id.to_s }
|
87
89
|
if experiment.nil?
|
88
|
-
raise Exception::
|
90
|
+
raise Exception::ExperimentConfigurationNotFound.new(experiment_id)
|
89
91
|
end
|
90
92
|
if @blocking
|
91
93
|
variation_id = nil
|
92
94
|
EM.synchrony do
|
93
|
-
connexion_options = { :connect_timeout => timeout }
|
95
|
+
connexion_options = { :connect_timeout => (timeout.to_f / 1000.0) }
|
94
96
|
body = @data.values.flatten.select { |data| !data.sent }.map { |data| data.obtain_full_post_text_line }
|
95
97
|
.join("\n") || ""
|
96
98
|
path = get_experiment_register_url(visitor_code, experiment_id)
|
97
99
|
request_options = { :path => path, :body => body }
|
100
|
+
log "Trigger experiment request: " + request_options.inspect
|
101
|
+
log "Trigger experiment connexion:" + connexion_options.inspect
|
98
102
|
request = EM::Synchrony.sync post(request_options, @tracking_url, connexion_options)
|
99
103
|
if is_successful(request)
|
100
104
|
variation_id = request.response
|
101
105
|
else
|
102
|
-
|
106
|
+
log "Failed to trigger experiment: " + request.inspect
|
107
|
+
raise Exception::ExperimentConfigurationNotFound.new(experiment_id) if variation_id.nil?
|
103
108
|
end
|
104
109
|
EM.stop
|
105
110
|
end
|
@@ -111,16 +116,16 @@ module Kameleoon
|
|
111
116
|
variation_id.to_i
|
112
117
|
else
|
113
118
|
visitor_data = @data.select { |key, value| key.to_s == visitor_code }.values.flatten! || []
|
114
|
-
if experiment['targetingSegment'].check_tree(visitor_data)
|
119
|
+
if experiment['targetingSegment'].nil? || experiment['targetingSegment'].check_tree(visitor_data)
|
115
120
|
threshold = obtain_hash_double(visitor_code, experiment['respoolTime'], experiment['id'])
|
116
121
|
experiment['deviations'].each do |key, value|
|
117
122
|
threshold -= value
|
118
123
|
if threshold < 0
|
119
|
-
|
124
|
+
track_experiment(visitor_code, experiment_id, key)
|
120
125
|
return key.to_s.to_i
|
121
126
|
end
|
122
127
|
end
|
123
|
-
|
128
|
+
track_experiment(visitor_code, experiment_id, REFERENCE, true)
|
124
129
|
raise Exception::NotActivated.new(visitor_code)
|
125
130
|
end
|
126
131
|
raise Exception::NotTargeted.new(visitor_code)
|
@@ -128,7 +133,9 @@ module Kameleoon
|
|
128
133
|
end
|
129
134
|
|
130
135
|
##
|
131
|
-
# Associate various data to a visitor.
|
136
|
+
# Associate various data to a visitor.
|
137
|
+
#
|
138
|
+
# Note that this method doesn't return any value and doesn't interact with the
|
132
139
|
# Kameleoon back-end servers by itself. Instead, the declared data is saved for future sending via the flush method.
|
133
140
|
# This reduces the number of server calls made, as data is usually grouped into a single server call triggered by
|
134
141
|
# the execution of the flush method.
|
@@ -151,13 +158,14 @@ module Kameleoon
|
|
151
158
|
|
152
159
|
##
|
153
160
|
# Track conversions on a particular goal
|
161
|
+
#
|
154
162
|
# This method requires visitor_code and goal_id to track conversion on this particular goal.
|
155
163
|
# In addition, this method also accepts revenue as a third optional argument to track revenue. The visitor_code usually is identical to the one that was used when triggering the experiment.
|
156
164
|
# The track_conversion method doesn't return any value. This method is non-blocking as the server call is made asynchronously.
|
157
165
|
#
|
158
166
|
# @param [String] visitor_code Visitor code
|
159
167
|
# @param [Integer] goal_id Id of the goal
|
160
|
-
# @param [Float] revenue Revenue of the conversion.
|
168
|
+
# @param [Float] revenue Optional - Revenue of the conversion.
|
161
169
|
#
|
162
170
|
def track_conversion(visitor_code, goal_id, revenue = 0.0)
|
163
171
|
add_data(visitor_code, Conversion.new(goal_id, revenue))
|
@@ -166,6 +174,7 @@ module Kameleoon
|
|
166
174
|
|
167
175
|
##
|
168
176
|
# Flush the associated data.
|
177
|
+
#
|
169
178
|
# The data added with the method add_data, is not directly sent to the kameleoon servers.
|
170
179
|
# It's stored and accumulated until it is sent automatically by the trigger_experiment or track_conversion methods.
|
171
180
|
# With this method you can manually send it.
|
@@ -173,11 +182,12 @@ module Kameleoon
|
|
173
182
|
# @param [String] visitor_code Optional field - Visitor code, without visitor code it flush all of the data
|
174
183
|
#
|
175
184
|
def flush(visitor_code = nil)
|
176
|
-
|
185
|
+
track_data(visitor_code)
|
177
186
|
end
|
178
187
|
|
179
188
|
##
|
180
189
|
# Obtain variation associated data.
|
190
|
+
#
|
181
191
|
# To retrieve JSON data associated with a variation, call the obtain_variation_associated_data method of our SDK.
|
182
192
|
# The JSON data usually represents some metadata of the variation, and can be configured on our web application
|
183
193
|
# interface or via our Automation API.
|
@@ -186,20 +196,21 @@ module Kameleoon
|
|
186
196
|
#
|
187
197
|
# @param [Integer] variation_id
|
188
198
|
#
|
189
|
-
# @return [
|
199
|
+
# @return [Hash] Hash object of the json object.
|
190
200
|
#
|
191
201
|
# @raise [Kameleoon::Exception::VariationNotFound] Raise exception if the variation is not found.
|
202
|
+
#
|
192
203
|
def obtain_variation_associated_data(variation_id)
|
193
204
|
variation = @experiments.map { |experiment| experiment['variations'] }.flatten.select { |variation| variation['id'].to_i == variation_id.to_i }.first
|
194
205
|
if variation.nil?
|
195
206
|
raise Exception::VariationNotFound.new(variation_id)
|
196
207
|
else
|
197
|
-
variation['customJson']
|
208
|
+
variation['customJson']
|
198
209
|
end
|
199
210
|
end
|
200
211
|
|
201
212
|
#
|
202
|
-
#
|
213
|
+
# Activate a feature toggle.
|
203
214
|
#
|
204
215
|
# This method takes a visitor_code and feature_key (or feature_id) as mandatory arguments to check if the specified feature will be active for a given user.
|
205
216
|
# If such a user has never been associated with this feature flag, the SDK returns a boolean value randomly (true if the user should have this feature or false if not). If a user with a given visitorCode is already registered with this feature flag, it will detect the previous featureFlag value.
|
@@ -208,26 +219,31 @@ module Kameleoon
|
|
208
219
|
# @param [String] visitor_code
|
209
220
|
# @param [String | Integer] feature_key
|
210
221
|
#
|
211
|
-
# @raise [Kameleoon::Exception::
|
222
|
+
# @raise [Kameleoon::Exception::FeatureConfigurationNotFound]
|
212
223
|
# @raise [Kameleoon::Exception::NotTargeted]
|
224
|
+
#
|
213
225
|
def activate_feature(visitor_code, feature_key, timeout = @default_timeout)
|
214
226
|
feature_flag = get_feature_flag(feature_key)
|
215
227
|
id = feature_flag['id']
|
216
228
|
if @blocking
|
217
229
|
result = nil
|
218
230
|
EM.synchrony do
|
219
|
-
connexion_options = { :connect_timeout => timeout }
|
231
|
+
connexion_options = { :connect_timeout => (timeout.to_f / 1000.0) }
|
220
232
|
request_options = {
|
221
233
|
:path => get_experiment_register_url(visitor_code, id),
|
222
|
-
:body => (
|
234
|
+
:body => (data_not_sent(visitor_code).values.map { |data| data.obtain_full_post_text_line }.join("\n") || "").encode("UTF-8")
|
223
235
|
}
|
236
|
+
log "Activate feature request: " + request_options.inspect
|
237
|
+
log "Activate feature connexion:" + connexion_options.inspect
|
224
238
|
request = EM::Synchrony.sync post(request_options, @tracking_url, connexion_options)
|
225
239
|
if is_successful(request)
|
226
240
|
result = request.response
|
241
|
+
else
|
242
|
+
log "Failed to get activation:" + result.inspect
|
227
243
|
end
|
228
244
|
EM.stop
|
229
245
|
end
|
230
|
-
raise Exception::
|
246
|
+
raise Exception::FeatureConfigurationNotFound.new(id) if result.nil?
|
231
247
|
result.to_s != "null"
|
232
248
|
|
233
249
|
else
|
@@ -235,10 +251,10 @@ module Kameleoon
|
|
235
251
|
if feature_flag['targetingSegment'].nil? || feature_flag['targetingSegment'].check_tree(visitor_data)
|
236
252
|
threshold = obtain_hash_double(visitor_code, {}, id)
|
237
253
|
if threshold <= feature_flag['expositionRate']
|
238
|
-
|
254
|
+
track_experiment(visitor_code, id, feature_flag["variationsId"].first)
|
239
255
|
return true
|
240
256
|
else
|
241
|
-
|
257
|
+
track_experiment(visitor_code, id, REFERENCE, true)
|
242
258
|
return false
|
243
259
|
end
|
244
260
|
else
|
@@ -248,17 +264,21 @@ module Kameleoon
|
|
248
264
|
end
|
249
265
|
|
250
266
|
##
|
251
|
-
#
|
267
|
+
# Retrieve a feature variable.
|
268
|
+
#
|
269
|
+
# A feature variable can be changed easily via our web application.
|
252
270
|
#
|
253
271
|
# @param [String | Integer] feature_key
|
254
272
|
# @param [String ] variable_key
|
255
273
|
#
|
256
|
-
# @raise [Kameleoon::Exception::
|
274
|
+
# @raise [Kameleoon::Exception::FeatureConfigurationNotFound]
|
275
|
+
# @raise [Kameleoon::Exception::FeatureVariableNotFound]
|
276
|
+
#
|
257
277
|
def obtain_feature_variable(feature_key, variable_key)
|
258
278
|
feature_flag = get_feature_flag(feature_key)
|
259
279
|
custom_json = feature_flag["variations"].first['customJson'][variable_key.to_s]
|
260
280
|
if custom_json.nil?
|
261
|
-
raise Exception::
|
281
|
+
raise Exception::FeatureVariableNotFound.new("Feature variable not found")
|
262
282
|
end
|
263
283
|
case custom_json['type']
|
264
284
|
when "Boolean"
|
@@ -268,9 +288,9 @@ module Kameleoon
|
|
268
288
|
when "Number"
|
269
289
|
return custom_json['value'].to_f
|
270
290
|
when "Json"
|
271
|
-
return custom_json['value']
|
291
|
+
return custom_json['value']
|
272
292
|
else
|
273
|
-
raise
|
293
|
+
raise TypeError.new("Unknown type for feature variable")
|
274
294
|
end
|
275
295
|
end
|
276
296
|
|
@@ -278,15 +298,16 @@ module Kameleoon
|
|
278
298
|
API_SSX_URL = "https://api-ssx.kameleoon.com"
|
279
299
|
REFERENCE = 0
|
280
300
|
attr :site_code, :client_id, :client_secret, :access_token, :experiments, :feature_flags, :scheduler, :data,
|
281
|
-
:blocking, :tracking_url, :default_timeout, :interval, :memory_limit
|
301
|
+
:blocking, :tracking_url, :default_timeout, :interval, :memory_limit, :verbose_mode
|
282
302
|
|
283
303
|
def fetch_configuration
|
284
|
-
print "=> Starting Scheduler"
|
285
304
|
@scheduler = Rufus::Scheduler.singleton
|
286
305
|
@scheduler.every @interval do
|
306
|
+
log("Scheduled job to fetch configuration is starting.")
|
287
307
|
fetch_configuration_job
|
288
308
|
end
|
289
309
|
@scheduler.schedule '0s' do
|
310
|
+
log("Start-up, fetching is starting")
|
290
311
|
fetch_configuration_job
|
291
312
|
end
|
292
313
|
end
|
@@ -300,8 +321,8 @@ module Kameleoon
|
|
300
321
|
@feature_flags ||= []
|
301
322
|
else
|
302
323
|
site_id = site.first['id']
|
303
|
-
@experiments = obtain_tests(site_id)
|
304
|
-
@feature_flags = obtain_feature_flags(site_id)
|
324
|
+
@experiments = obtain_tests(site_id) || @experiments
|
325
|
+
@feature_flags = obtain_feature_flags(site_id) || @feature_flags
|
305
326
|
end
|
306
327
|
EM.stop
|
307
328
|
end
|
@@ -322,15 +343,19 @@ module Kameleoon
|
|
322
343
|
end
|
323
344
|
|
324
345
|
def obtain_site
|
346
|
+
log "Fetching site"
|
325
347
|
query_params = { 'perPage' => 1 }
|
326
348
|
filters = [hash_filter('code', 'EQUAL', [@site_code])]
|
327
349
|
request = fetch_one('sites', query_params, filters)
|
328
350
|
if request != false
|
329
|
-
JSON.parse(request.response)
|
351
|
+
sites = JSON.parse(request.response)
|
352
|
+
log "Sites are fetched: " + sites.inspect
|
353
|
+
sites
|
330
354
|
end
|
331
355
|
end
|
332
356
|
|
333
357
|
def obtain_access_token
|
358
|
+
log "Fetching bearer token"
|
334
359
|
body = {
|
335
360
|
'grant_type' => 'client_credentials',
|
336
361
|
'client_id' => @client_id,
|
@@ -341,6 +366,9 @@ module Kameleoon
|
|
341
366
|
request = EM::Synchrony.sync post({ :path => '/oauth/token', :body => body, :head => header })
|
342
367
|
if is_successful(request)
|
343
368
|
@access_token = JSON.parse(request.response)['access_token']
|
369
|
+
log "Bearer Token is fetched: " + @access_token.to_s
|
370
|
+
else
|
371
|
+
log "Failed to fetch bearer token: " + request.inspect
|
344
372
|
end
|
345
373
|
end
|
346
374
|
|
@@ -369,26 +397,32 @@ module Kameleoon
|
|
369
397
|
end
|
370
398
|
|
371
399
|
def obtain_tests(site_id, per_page = -1)
|
400
|
+
log "Fetching experiments"
|
372
401
|
query_values = { 'perPage' => per_page }
|
373
402
|
filters = [
|
374
403
|
hash_filter('siteId', 'EQUAL', [site_id]),
|
375
404
|
hash_filter('status', 'EQUAL', ['ACTIVE']),
|
376
405
|
hash_filter('type', 'IN', ['SERVER_SIDE', 'HYBRID'])
|
377
406
|
]
|
378
|
-
fetch_all('experiments', query_values, filters).map { |it| JSON.parse(it.response) }.flatten.map do |test|
|
407
|
+
experiments = fetch_all('experiments', query_values, filters).map { |it| JSON.parse(it.response) }.flatten.map do |test|
|
379
408
|
complete_experiment(test)
|
380
409
|
end
|
410
|
+
log "Experiment are fetched: " + experiments.inspect
|
411
|
+
experiments
|
381
412
|
end
|
382
413
|
|
383
414
|
def obtain_feature_flags(site_id, per_page = -1)
|
415
|
+
log "Fetching feature flags"
|
384
416
|
query_values = { 'perPage' => per_page }
|
385
417
|
filters = [
|
386
418
|
hash_filter('siteId', 'EQUAL', [site_id]),
|
387
419
|
hash_filter('status', 'EQUAL', ['ACTIVE'])
|
388
420
|
]
|
389
|
-
fetch_all('feature-flags', query_values, filters).map { |it| JSON.parse(it.response) }.flatten.map do |ff|
|
421
|
+
feature_flags = fetch_all('feature-flags', query_values, filters).map { |it| JSON.parse(it.response) }.flatten.map do |ff|
|
390
422
|
complete_experiment(ff)
|
391
423
|
end
|
424
|
+
log "Feature flags are fetched: " + feature_flags.inspect
|
425
|
+
feature_flags
|
392
426
|
end
|
393
427
|
|
394
428
|
def fetch_all(path, query_values = {}, filters = [])
|
@@ -411,6 +445,7 @@ module Kameleoon
|
|
411
445
|
end
|
412
446
|
request = EM::Synchrony.sync get({ :path => path, :query => query_values, :head => hash_headers })
|
413
447
|
unless is_successful(request)
|
448
|
+
log "Failed to fetch" + request.inspect
|
414
449
|
return false
|
415
450
|
end
|
416
451
|
request
|
@@ -437,6 +472,10 @@ module Kameleoon
|
|
437
472
|
url
|
438
473
|
end
|
439
474
|
|
475
|
+
def get_data_register_url(visitor_code)
|
476
|
+
"/dataTracking?" + URI.encode_www_form(get_common_ssx_parameters(visitor_code))
|
477
|
+
end
|
478
|
+
|
440
479
|
def get_feature_flag(feature_key)
|
441
480
|
if feature_key.is_a?(String)
|
442
481
|
feature_flag = @feature_flags.select { |ff| ff['identificationKey'] == feature_key}.first
|
@@ -446,24 +485,53 @@ module Kameleoon
|
|
446
485
|
raise TypeError.new("Feature key should be a String or an Integer.")
|
447
486
|
end
|
448
487
|
if feature_flag.nil?
|
449
|
-
raise Exception::
|
488
|
+
raise Exception::FeatureConfigurationNotFound.new(feature_key)
|
450
489
|
end
|
451
490
|
feature_flag
|
452
491
|
end
|
453
492
|
|
454
|
-
def
|
493
|
+
def track_experiment(visitor_code, experiment_id, variation_id = nil, none_variation = false)
|
494
|
+
data_not_sent = data_not_sent(visitor_code)
|
495
|
+
options = {
|
496
|
+
:path => get_experiment_register_url(visitor_code, experiment_id, variation_id, none_variation),
|
497
|
+
:body => ((data_not_sent.values[0] || []).map{ |it| it.obtain_full_post_text_line }.join("\n") || "").encode("UTF-8"),
|
498
|
+
:head => { "Content-Type" => "text/plain"}
|
499
|
+
}
|
500
|
+
trial = 0
|
501
|
+
log "Start post tracking experiment: " + data_not_sent.inspect
|
502
|
+
Thread.new do
|
503
|
+
EM.synchrony do
|
504
|
+
while trial < 10
|
505
|
+
request = EM::Synchrony.sync post(options, @tracking_url)
|
506
|
+
log "Request " + request.inspect
|
507
|
+
if is_successful(request)
|
508
|
+
(data_not_sent.values[0] || []).each { |it| it.sent = true }
|
509
|
+
EM.stop
|
510
|
+
end
|
511
|
+
trial += 1
|
512
|
+
end
|
513
|
+
EM.stop
|
514
|
+
end
|
515
|
+
log "Post to experiment tracking is done after " + trial.to_s + " trials"
|
516
|
+
Thread.exit
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def track_data(visitor_code = nil)
|
455
521
|
Thread.new do
|
456
522
|
EM.synchrony do
|
457
|
-
entries = select_data_to_sent(visitor_code)
|
458
523
|
trials = 10
|
459
524
|
concurrency = 1
|
460
|
-
|
461
|
-
|
525
|
+
data_not_sent = data_not_sent(visitor_code)
|
526
|
+
log "Start post tracking data: " + data_not_sent.inspect
|
527
|
+
while trials > 0 && !data_not_sent.empty?
|
528
|
+
EM::Synchrony::Iterator.new(data_not_sent, concurrency).map do |entry, iter|
|
462
529
|
options = {
|
463
|
-
:path =>
|
530
|
+
:path => get_data_register_url(entry.first),
|
464
531
|
:body => (entry.last.map { |data| data.obtain_full_post_text_line }.join("\n") || "").encode("UTF-8"),
|
465
532
|
:head => { "Content-Type" => "text/plain" }
|
466
533
|
}
|
534
|
+
log "Post tracking data for visitor_code: " + entry.first + " with options: " + options.inspect
|
467
535
|
request = post(options, @tracking_url)
|
468
536
|
request.callback {
|
469
537
|
if is_successful(request)
|
@@ -473,16 +541,17 @@ module Kameleoon
|
|
473
541
|
}
|
474
542
|
request.errback { iter.return(request) }
|
475
543
|
end
|
476
|
-
|
544
|
+
data_not_sent = data_not_sent(visitor_code)
|
477
545
|
trials -= 1
|
478
546
|
end
|
547
|
+
log "Post to data tracking is done."
|
479
548
|
EM.stop
|
480
549
|
end
|
481
550
|
Thread.exit
|
482
551
|
end
|
483
552
|
end
|
484
553
|
|
485
|
-
def
|
554
|
+
def data_not_sent(visitor_code = nil)
|
486
555
|
if visitor_code.nil?
|
487
556
|
@data.select {|key, values| values.any? {|data| !data.sent}}
|
488
557
|
else
|
@@ -490,13 +559,9 @@ module Kameleoon
|
|
490
559
|
end
|
491
560
|
end
|
492
561
|
|
493
|
-
def
|
494
|
-
if
|
495
|
-
|
496
|
-
elsif type == "experimentTracking"
|
497
|
-
return get_experiment_register_url(visitor_code, experiment_id, variation_id, none_variation)
|
498
|
-
else
|
499
|
-
raise TypeError("Unknown type for post_beacon: " + type.to_s)
|
562
|
+
def log(text)
|
563
|
+
if @verbose_mode
|
564
|
+
print "Kameleoon Log: " + text.to_s + "\n"
|
500
565
|
end
|
501
566
|
end
|
502
567
|
end
|
data/lib/kameleoon/exceptions.rb
CHANGED
@@ -15,16 +15,21 @@ module Kameleoon
|
|
15
15
|
super("Variation " + id.to_s)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
class
|
18
|
+
class ExperimentConfigurationNotFound < NotFound
|
19
19
|
def initialize(id = "")
|
20
20
|
super("Experiment " + id.to_s)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
class
|
23
|
+
class FeatureConfigurationNotFound < NotFound
|
24
24
|
def initialize(id = "")
|
25
25
|
super("Feature flag " + id.to_s)
|
26
26
|
end
|
27
27
|
end
|
28
|
+
class FeatureVariableNotFound < NotFound
|
29
|
+
def initialize(key = "")
|
30
|
+
super("Feature variable " + key.to_s)
|
31
|
+
end
|
32
|
+
end
|
28
33
|
class CredentialsNotFound < NotFound
|
29
34
|
def initialize
|
30
35
|
super("Credentials")
|
data/lib/kameleoon/factory.rb
CHANGED
@@ -2,9 +2,9 @@ require 'kameleoon/client'
|
|
2
2
|
|
3
3
|
module Kameleoon
|
4
4
|
module ClientFactory
|
5
|
-
CONFIGURATION_UPDATE_INTERVAL=
|
5
|
+
CONFIGURATION_UPDATE_INTERVAL = '60m'
|
6
6
|
CONFIG_PATH = "/etc/kameleoon/client-ruby.yaml"
|
7
|
-
DEFAULT_TIMEOUT =
|
7
|
+
DEFAULT_TIMEOUT = 2000 #milli-seconds
|
8
8
|
|
9
9
|
##
|
10
10
|
# Create a kameleoon client object, each call create a new client.
|
@@ -17,6 +17,8 @@ module Kameleoon
|
|
17
17
|
#
|
18
18
|
def self.create(site_code, blocking = false, config_path = CONFIG_PATH, client_id = nil, client_secret = nil)
|
19
19
|
client = Client.new(site_code, config_path, blocking, CONFIGURATION_UPDATE_INTERVAL, DEFAULT_TIMEOUT, client_id, client_secret)
|
20
|
+
client.send(:log, "Warning: you are using the blocking mode") if blocking
|
21
|
+
client.send(:log, "Client created with site code: " + site_code.to_s)
|
20
22
|
client.send(:fetch_configuration)
|
21
23
|
client
|
22
24
|
end
|
data/lib/kameleoon/request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "em-synchrony/em-http"
|
2
|
+
require "kameleoon/version"
|
2
3
|
|
3
4
|
module Kameleoon
|
4
5
|
# @api private
|
@@ -22,7 +23,8 @@ module Kameleoon
|
|
22
23
|
private
|
23
24
|
|
24
25
|
def request(method, request_options, url, connexion_options)
|
25
|
-
|
26
|
+
connexion_options[:tls] = {verify_peer: false}
|
27
|
+
add_user_agent(request_options)
|
26
28
|
case method
|
27
29
|
when Method::POST then
|
28
30
|
return EventMachine::HttpRequest.new(url, connexion_options).apost request_options
|
@@ -37,6 +39,14 @@ module Kameleoon
|
|
37
39
|
def is_successful(request)
|
38
40
|
!request.nil? && request != false && /20\d/.match(request.response_header.status.to_s)
|
39
41
|
end
|
42
|
+
|
43
|
+
def add_user_agent(request_options)
|
44
|
+
if request_options[:head].nil?
|
45
|
+
request_options[:head] = {'Kameleoon-Client' => 'sdk/ruby/' + Kameleoon::VERSION}
|
46
|
+
else
|
47
|
+
request_options[:head].store('Kameleoon-Client', 'sdk/ruby/' + Kameleoon::VERSION)
|
48
|
+
end
|
49
|
+
end
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
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: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kameleoon - Guillaume Grandjean
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- lib/kameleoon/targeting/models.rb
|
74
74
|
- lib/kameleoon/targeting/tree_builder.rb
|
75
75
|
- lib/kameleoon/utils.rb
|
76
|
+
- lib/kameleoon/version.rb
|
76
77
|
homepage: https://developers.kameleoon.com/ruby-sdk.html
|
77
78
|
licenses:
|
78
79
|
- GPL-3.0
|