kameleoon-client-ruby 1.0.0 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|