optimizely-sdk 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 78af637c6334c098627f0f83824494fb30fb0de4
4
- data.tar.gz: d9d32f68014ec5866e521e274ac384fef1e22e81
3
+ metadata.gz: 388b7be07d59d027ef5f08ebce2fa43d62be0513
4
+ data.tar.gz: 0288d19e852dce6a860ceac8c35c6b508ed3363a
5
5
  SHA512:
6
- metadata.gz: 09fdf5e75b3fb050d43f627af4f81225d8bdd2ed0f10c89b8dfe8b485f1849b669497315645e54d7eb8fdcec7209892ebdb9a5a5af68805bd7f3873051738eb7
7
- data.tar.gz: 1bd6b64f56ef37dc6c2c38af651fce3c967832da5cf22a05e67683befb0bd3aeaec61c441a8d4ca6a961b97a6654f1362a8d5ea1d4a63b568c55a0858ff72d15
6
+ metadata.gz: 02cd29fbf79181021dae6d19aae905e4304dc8ce6d338e7d1df2e2086fd7d381b65232ed188f41ef005a1afaefa6de610d195162b144e7c6eccd05b084f9be9f
7
+ data.tar.gz: 281090b33e4e89c8ea7a32276d259a307fa9a3001ce532cc8b373a70d7cc70ec19d7e62c52a360e29e1bbabb5f44786739d05214d276558c5d0b060772ab5488
@@ -18,6 +18,11 @@ module Optimizely
18
18
  attr_accessor :logger
19
19
  attr_accessor :error_handler
20
20
 
21
+ EVENT_BUILDERS_BY_VERSION = {
22
+ Optimizely::V1_CONFIG_VERSION => EventBuilderV1,
23
+ Optimizely::V2_CONFIG_VERSION => EventBuilderV2
24
+ }
25
+
21
26
  def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = nil, skip_json_validation = false)
22
27
  # Constructor for Projects.
23
28
  #
@@ -35,7 +40,7 @@ module Optimizely
35
40
 
36
41
  @config = ProjectConfig.new(datafile, @logger, @error_handler)
37
42
  @bucketer = Bucketer.new(@config)
38
- @event_builder = EventBuilder.new(@config, @bucketer)
43
+ @event_builder = EVENT_BUILDERS_BY_VERSION[@config.version].new(@config, @bucketer)
39
44
  end
40
45
 
41
46
  def activate(experiment_key, user_id, attributes = nil)
@@ -70,7 +75,7 @@ module Optimizely
70
75
  @logger.log(Logger::INFO,
71
76
  'Dispatching impression event to URL %s with params %s.' % [impression_event.url,
72
77
  impression_event.params])
73
- @event_dispatcher.dispatch_event(impression_event.url, impression_event.params)
78
+ @event_dispatcher.dispatch_event(impression_event)
74
79
 
75
80
  @config.get_variation_key_from_id(experiment_key, variation_id)
76
81
  end
@@ -136,7 +141,7 @@ module Optimizely
136
141
  @logger.log(Logger::INFO,
137
142
  'Dispatching conversion event to URL %s with params %s.' % [conversion_event.url,
138
143
  conversion_event.params])
139
- @event_dispatcher.dispatch_event(conversion_event.url, conversion_event.params)
144
+ @event_dispatcher.dispatch_event(conversion_event)
140
145
  end
141
146
 
142
147
  private
@@ -6,28 +6,198 @@ module Optimizely
6
6
  class Event
7
7
  # Representation of an event which can be sent to the Optimizely logging endpoint.
8
8
 
9
- # Event API format
10
- OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event'
9
+ attr_reader :http_verb
10
+ attr_reader :params
11
+ attr_reader :url
12
+ attr_reader :headers
13
+
14
+ def initialize(http_verb, url, params, headers)
15
+ @http_verb = http_verb
16
+ @url = url
17
+ @params = params
18
+ @headers = headers
19
+ end
20
+
21
+ # Override equality operator to make two events with the same contents equal for testing purposes
22
+ def ==(event)
23
+ @http_verb == event.http_verb && @url == event.url && @params == event.params && @headers == event.headers
24
+ end
25
+ end
11
26
 
12
- # Gets/Sets event params.
27
+ class BaseEventBuilder
28
+ attr_reader :config
29
+ attr_reader :bucketer
13
30
  attr_accessor :params
14
31
 
15
- def initialize(params)
16
- @params = params
32
+ def initialize(config, bucketer)
33
+ @config = config
34
+ @bucketer = bucketer
35
+ @params = {}
17
36
  end
18
37
 
19
- def url
20
- # URL for sending impression/conversion event.
38
+ private
39
+
40
+ def add_common_params(user_id, attributes)
41
+ # Add params which are used in both conversion and impression events.
21
42
  #
22
- # project_id - ID for the project.
43
+ # user_id - ID for user.
44
+ # attributes - Hash representing user attributes and values which need to be recorded.
45
+
46
+ add_project_id
47
+ add_account_id
48
+ add_user_id(user_id)
49
+ add_attributes(attributes)
50
+ add_source
51
+ add_time
52
+ end
53
+ end
54
+
55
+ class EventBuilderV2 < BaseEventBuilder
56
+ CONVERSION_EVENT_ENDPOINT = 'https://p13nlog.dz.optimizely.com/log/event'
57
+ IMPRESSION_EVENT_ENDPOINT = 'https://p13nlog.dz.optimizely.com/log/decision'
58
+ POST_HEADERS = { 'Content-Type' => 'application/json' }
59
+
60
+ def create_impression_event(experiment_key, variation_id, user_id, attributes)
61
+ # Create conversion Event to be sent to the logging endpoint.
62
+ #
63
+ # experiment_key - Experiment for which impression needs to be recorded.
64
+ # variation_id - ID for variation which would be presented to user.
65
+ # user_id - ID for user.
66
+ # attributes - Hash representing user attributes and values which need to be recorded.
67
+ #
68
+ # Returns event hash encapsulating the impression event.
69
+
70
+ @params = {}
71
+ add_common_params(user_id, attributes)
72
+ add_decision(experiment_key, variation_id)
73
+ add_attributes(attributes)
74
+ Event.new(:post, IMPRESSION_EVENT_ENDPOINT, @params, POST_HEADERS)
75
+ end
76
+
77
+ def create_conversion_event(event_key, user_id, attributes, event_value, experiment_keys)
78
+ # Create conversion Event to be sent to the logging endpoint.
79
+ #
80
+ # event_key - Event key representing the event which needs to be recorded.
81
+ # user_id - ID for user.
82
+ # attributes - Hash representing user attributes and values which need to be recorded.
83
+ # event_value - Value associated with the event. Can be used to represent revenue in cents.
84
+ # experiment_keys - Array of valid experiment keys for the event
85
+ #
86
+ # Returns event hash encapsulating the conversion event.
87
+
88
+ @params = {}
89
+ add_common_params(user_id, attributes)
90
+ add_conversion_event(event_key, event_value)
91
+ add_layer_states(user_id, experiment_keys)
92
+ Event.new(:post, CONVERSION_EVENT_ENDPOINT, @params, POST_HEADERS)
93
+ end
94
+
95
+ private
96
+
97
+ def add_common_params(user_id, attributes)
98
+ super
99
+ @params['isGlobalHoldback'] = false
100
+ end
101
+
102
+ def add_project_id
103
+ @params['projectId'] = @config.project_id
104
+ end
105
+
106
+ def add_account_id
107
+ @params['accountId'] = @config.account_id
108
+ end
109
+
110
+ def add_user_id(user_id)
111
+ @params['visitorId'] = user_id
112
+ end
113
+
114
+ def add_attributes(attributes)
115
+ @params['userFeatures'] = []
116
+
117
+ return if attributes.nil?
118
+
119
+ attributes.keys.each do |attribute_key|
120
+ # Omit falsy attribute values
121
+ attribute_value = attributes[attribute_key]
122
+ next unless attribute_value
123
+
124
+ # Skip attributes not in the datafile
125
+ attribute_id = @config.get_attribute_id(attribute_key)
126
+ next unless attribute_id
127
+
128
+ feature = {
129
+ 'id' => attribute_id,
130
+ 'name' => attribute_key,
131
+ 'type' => 'custom',
132
+ 'value' => attribute_value,
133
+ 'shouldIndex' => true,
134
+ }
135
+ @params['userFeatures'].push(feature)
136
+ end
137
+ end
138
+
139
+ def add_decision(experiment_key, variation_id)
140
+ experiment_id = @config.get_experiment_id(experiment_key)
141
+ @params['layerId'] = @config.experiment_key_map[experiment_key]['layerId']
142
+ @params['decision'] = {
143
+ 'variationId' => variation_id,
144
+ 'experimentId' => experiment_id,
145
+ 'isLayerHoldback' => false,
146
+ }
147
+ end
148
+
149
+ def add_conversion_event(event_key, event_value)
150
+ # Add conversion event information to the event.
23
151
  #
24
- # Returns URL for event API.
152
+ # event_key - Event key representing the event which needs to be recorded.
153
+ # event_value - Value associated with the event. Can be used to represent revenue in cents.
154
+
155
+ event_id = @config.event_key_map[event_key]['id']
156
+ event_name = @config.event_key_map[event_key]['key']
157
+
158
+ @params['eventEntityId'] = event_id
159
+ @params['eventFeatures'] = []
160
+ @params['eventName'] = event_name
161
+ @params['eventMetrics'] = []
25
162
 
26
- sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID])
163
+ if event_value
164
+ @params['eventMetrics'].push({
165
+ 'name' => 'revenue',
166
+ 'value' => event_value,
167
+ })
168
+ end
169
+ end
170
+
171
+ def add_layer_states(user_id, experiment_keys)
172
+ @params['layerStates'] = []
173
+
174
+ experiment_keys.each do |experiment_key|
175
+ variation_id = @bucketer.bucket(experiment_key, user_id)
176
+ experiment_id = @config.experiment_key_map[experiment_key]['id']
177
+ layer_state = {
178
+ 'layerId' => @config.experiment_key_map[experiment_key]['layerId'],
179
+ 'decision' => {
180
+ 'variationId' => variation_id,
181
+ 'experimentId' => experiment_id,
182
+ 'isLayerHoldback' => false,
183
+ },
184
+ 'actionTriggered' => true,
185
+ }
186
+ @params['layerStates'].push(layer_state)
187
+ end
188
+ end
189
+
190
+ def add_source
191
+ @params['clientEngine'] = 'ruby-sdk'
192
+ @params['clientVersion'] = VERSION
193
+ end
194
+
195
+ def add_time
196
+ @params['timestamp'] = (Time.now.to_f * 1000).to_i
27
197
  end
28
198
  end
29
199
 
30
- class EventBuilder
200
+ class EventBuilderV1 < BaseEventBuilder
31
201
  # Class which encapsulates methods to build events for tracking impressions and conversions.
32
202
 
33
203
  # Attribute mapping format
@@ -36,15 +206,8 @@ module Optimizely
36
206
  # Experiment mapping format
37
207
  EXPERIMENT_PARAM_FORMAT = '%{experiment_prefix}%{experiment_id}'
38
208
 
39
- attr_accessor :config
40
- attr_accessor :bucketer
41
- attr_accessor :params
42
-
43
- def initialize(config, bucketer)
44
- @config = config
45
- @bucketer = bucketer
46
- @params = {}
47
- end
209
+ # Event endpoint path
210
+ OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event'
48
211
 
49
212
  def create_impression_event(experiment_key, variation_id, user_id, attributes)
50
213
  # Create conversion Event to be sent to the logging endpoint.
@@ -60,7 +223,7 @@ module Optimizely
60
223
  add_common_params(user_id, attributes)
61
224
  add_impression_goal(experiment_key)
62
225
  add_experiment(experiment_key, variation_id)
63
- Event.new(@params)
226
+ Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params, {})
64
227
  end
65
228
 
66
229
  def create_conversion_event(event_key, user_id, attributes, event_value, experiment_keys)
@@ -71,12 +234,14 @@ module Optimizely
71
234
  # attributes - Hash representing user attributes and values which need to be recorded.
72
235
  # event_value - Value associated with the event. Can be used to represent revenue in cents.
73
236
  # experiment_keys - Array of valid experiment keys for the goal
237
+ #
238
+ # Returns event hash encapsulating the conversion event.
74
239
 
75
240
  @params = {}
76
241
  add_common_params(user_id, attributes)
77
242
  add_conversion_goal(event_key, event_value)
78
243
  add_experiment_variation_params(user_id, experiment_keys)
79
- Event.new(@params)
244
+ Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params, {})
80
245
  end
81
246
 
82
247
  private
@@ -128,20 +293,6 @@ module Optimizely
128
293
  @params[Params::TIME] = Time.now.strftime('%s').to_i
129
294
  end
130
295
 
131
- def add_common_params(user_id, attributes)
132
- # Add params which are used same in both conversion and impression events.
133
- #
134
- # user_id - ID for user.
135
- # attributes - Hash representing user attributes and values which need to be recorded.
136
-
137
- add_project_id
138
- add_account_id
139
- add_user_id(user_id)
140
- add_attributes(attributes)
141
- add_source
142
- add_time
143
- end
144
-
145
296
  def add_impression_goal(experiment_key)
146
297
  # Add impression goal information to the event.
147
298
  #
@@ -1,18 +1,37 @@
1
1
  require 'httparty'
2
2
 
3
3
  module Optimizely
4
+ class NoOpEventDispatcher
5
+ # Class providing dispatch_event method which does nothing.
6
+
7
+ def dispatch_event(event)
8
+ end
9
+ end
10
+
4
11
  class EventDispatcher
5
12
  REQUEST_TIMEOUT = 10
6
13
 
7
- def dispatch_event(url, params)
14
+ def dispatch_event(event)
8
15
  # Dispatch the event being represented by the Event object.
9
16
  #
10
- # url - URL to send impression/conversion event to.
11
- # params - Params to be sent to the impression/conversion event.
17
+ # event - Event object
12
18
 
13
- HTTParty.get(url, query: params, timeout: REQUEST_TIMEOUT)
14
- rescue Timeout::Error => e
15
- return e
19
+ if event.http_verb == :get
20
+ begin
21
+ HTTParty.get(event.url, headers: event.headers, query: event.params, timeout: REQUEST_TIMEOUT)
22
+ rescue Timeout::Error => e
23
+ return e
24
+ end
25
+ elsif event.http_verb == :post
26
+ begin
27
+ HTTParty.post(event.url,
28
+ body: event.params.to_json,
29
+ headers: event.headers,
30
+ timeout: REQUEST_TIMEOUT)
31
+ rescue Timeout::Error => e
32
+ return e
33
+ end
34
+ end
16
35
  end
17
36
  end
18
37
  end
@@ -1,7 +1,7 @@
1
1
  module Optimizely
2
2
  module Helpers
3
3
  module Constants
4
- JSON_SCHEMA = {
4
+ JSON_SCHEMA_V1 = {
5
5
  'type' => 'object',
6
6
  'properties' => {
7
7
  'projectId' => {
@@ -186,7 +186,7 @@ module Optimizely
186
186
  'trafficAllocation',
187
187
  'audienceIds',
188
188
  'forcedVariations',
189
- 'status'
189
+ 'status',
190
190
  ]
191
191
  }
192
192
  },
@@ -278,6 +278,288 @@ module Optimizely
278
278
  'revision'
279
279
  ]
280
280
  }
281
+
282
+ JSON_SCHEMA_V2 = {
283
+ 'type' => 'object',
284
+ 'properties' => {
285
+ 'projectId' => {
286
+ 'type' => 'string'
287
+ },
288
+ 'accountId' => {
289
+ 'type' => 'string'
290
+ },
291
+ 'groups' => {
292
+ 'type' => 'array',
293
+ 'items' => {
294
+ 'type' => 'object',
295
+ 'properties' => {
296
+ 'id' => {
297
+ 'type' => 'string'
298
+ },
299
+ 'policy' => {
300
+ 'type' => 'string'
301
+ },
302
+ 'trafficAllocation' => {
303
+ 'type' => 'array',
304
+ 'items' => {
305
+ 'type' => 'object',
306
+ 'properties' => {
307
+ 'entityId' => {
308
+ 'type' => 'string'
309
+ },
310
+ 'endOfRange' => {
311
+ 'type' => 'integer'
312
+ }
313
+ },
314
+ 'required' => [
315
+ 'entityId',
316
+ 'endOfRange'
317
+ ]
318
+ }
319
+ },
320
+ 'experiments' => {
321
+ 'type' => 'array',
322
+ 'items' => {
323
+ 'type' => 'object',
324
+ 'properties' => {
325
+ 'id' => {
326
+ 'type' => 'string'
327
+ },
328
+ 'layerId' => {
329
+ 'type' => 'string'
330
+ },
331
+ 'key' => {
332
+ 'type' => 'string'
333
+ },
334
+ 'status' => {
335
+ 'type' => 'string'
336
+ },
337
+ 'variations' => {
338
+ 'type' => 'array',
339
+ 'items' => {
340
+ 'type' => 'object',
341
+ 'properties' => {
342
+ 'id' => {
343
+ 'type' => 'string'
344
+ },
345
+ 'key' => {
346
+ 'type' => 'string'
347
+ }
348
+ },
349
+ 'required' => [
350
+ 'id',
351
+ 'key'
352
+ ]
353
+ }
354
+ },
355
+ 'trafficAllocation' => {
356
+ 'type' => 'array',
357
+ 'items' => {
358
+ 'type' => 'object',
359
+ 'properties' => {
360
+ 'entityId' => {
361
+ 'type' => 'string'
362
+ },
363
+ 'endOfRange' => {
364
+ 'type' => 'integer'
365
+ }
366
+ },
367
+ 'required' => [
368
+ 'entityId',
369
+ 'endOfRange'
370
+ ]
371
+ }
372
+ },
373
+ 'audienceIds' => {
374
+ 'type' => 'array',
375
+ 'items' => {
376
+ 'type' => 'string'
377
+ }
378
+ },
379
+ 'forcedVariations' => {
380
+ 'type' => 'object'
381
+ }
382
+ },
383
+ 'required' => [
384
+ 'id',
385
+ 'layerId',
386
+ 'key',
387
+ 'status',
388
+ 'variations',
389
+ 'trafficAllocation',
390
+ 'audienceIds',
391
+ 'forcedVariations'
392
+ ]
393
+ }
394
+ }
395
+ },
396
+ 'required' => [
397
+ 'id',
398
+ 'policy',
399
+ 'trafficAllocation',
400
+ 'experiments'
401
+ ]
402
+ }
403
+ },
404
+ 'experiments' => {
405
+ 'type' => 'array',
406
+ 'items' => {
407
+ 'type' => 'object',
408
+ 'properties' => {
409
+ 'id' => {
410
+ 'type' => 'string'
411
+ },
412
+ 'key' => {
413
+ 'type' => 'string'
414
+ },
415
+ 'status' => {
416
+ 'type' => 'string'
417
+ },
418
+ 'layerId' => {
419
+ 'type' => 'string'
420
+ },
421
+ 'variations' => {
422
+ 'type' => 'array',
423
+ 'items' => {
424
+ 'type' => 'object',
425
+ 'properties' => {
426
+ 'id' => {
427
+ 'type' => 'string'
428
+ },
429
+ 'key' => {
430
+ 'type' => 'string'
431
+ }
432
+ },
433
+ 'required' => [
434
+ 'id',
435
+ 'key'
436
+ ]
437
+ }
438
+ },
439
+ 'trafficAllocation' => {
440
+ 'type' => 'array',
441
+ 'items' => {
442
+ 'type' => 'object',
443
+ 'properties' => {
444
+ 'entityId' => {
445
+ 'type' => 'string'
446
+ },
447
+ 'endOfRange' => {
448
+ 'type' => 'integer'
449
+ }
450
+ },
451
+ 'required' => [
452
+ 'entityId',
453
+ 'endOfRange'
454
+ ]
455
+ }
456
+ },
457
+ 'audienceIds' => {
458
+ 'type' => 'array',
459
+ 'items' => {
460
+ 'type' => 'string'
461
+ }
462
+ },
463
+ 'forcedVariations' => {
464
+ 'type' => 'object'
465
+ }
466
+ },
467
+ 'required' => [
468
+ 'id',
469
+ 'key',
470
+ 'variations',
471
+ 'trafficAllocation',
472
+ 'audienceIds',
473
+ 'forcedVariations',
474
+ 'status',
475
+ 'layerId'
476
+ ]
477
+ }
478
+ },
479
+ 'events' => {
480
+ 'type' => 'array',
481
+ 'items' => {
482
+ 'type' => 'object',
483
+ 'properties' => {
484
+ 'key' => {
485
+ 'type' => 'string'
486
+ },
487
+ 'experimentIds' => {
488
+ 'type' => 'array',
489
+ 'items' => {
490
+ 'type' => 'string'
491
+ }
492
+ },
493
+ 'id' => {
494
+ 'type' => 'string'
495
+ }
496
+ },
497
+ 'required' => [
498
+ 'key',
499
+ 'experimentIds',
500
+ 'id'
501
+ ]
502
+ }
503
+ },
504
+ 'audiences' => {
505
+ 'type' => 'array',
506
+ 'items' => {
507
+ 'type' => 'object',
508
+ 'properties' => {
509
+ 'id' => {
510
+ 'type' => 'string'
511
+ },
512
+ 'name' => {
513
+ 'type' => 'string'
514
+ },
515
+ 'conditions' => {
516
+ 'type' => 'string'
517
+ }
518
+ },
519
+ 'required' => [
520
+ 'id',
521
+ 'name',
522
+ 'conditions'
523
+ ]
524
+ }
525
+ },
526
+ 'attributes' => {
527
+ 'type' => 'array',
528
+ 'items' => {
529
+ 'type' => 'object',
530
+ 'properties' => {
531
+ 'id' => {
532
+ 'type' => 'string'
533
+ },
534
+ 'key' => {
535
+ 'type' => 'string'
536
+ },
537
+ },
538
+ 'required' => [
539
+ 'id',
540
+ 'key',
541
+ ]
542
+ }
543
+ },
544
+ 'version' => {
545
+ 'type' => 'string'
546
+ },
547
+ 'revision' => {
548
+ 'type' => 'string'
549
+ }
550
+ },
551
+ 'required' => [
552
+ 'projectId',
553
+ 'accountId',
554
+ 'experiments',
555
+ 'events',
556
+ 'groups',
557
+ 'audiences',
558
+ 'attributes',
559
+ 'version',
560
+ 'revision'
561
+ ]
562
+ }
281
563
  end
282
564
  end
283
565
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'constants'
2
+ require 'json'
2
3
  require 'json-schema'
3
4
 
4
5
  module Optimizely
@@ -23,7 +24,19 @@ module Optimizely
23
24
  #
24
25
  # Returns boolean depending on validity of datafile.
25
26
 
26
- JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA, datafile)
27
+ begin
28
+ datafile = JSON.load(datafile)
29
+ rescue
30
+ return false
31
+ end
32
+
33
+ version = datafile['version']
34
+
35
+ if version == Optimizely::V1_CONFIG_VERSION
36
+ JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA_V1, datafile)
37
+ else
38
+ JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA_V2, datafile)
39
+ end
27
40
  end
28
41
 
29
42
  def error_handler_valid?(error_handler)
@@ -1,6 +1,10 @@
1
1
  require 'json'
2
2
 
3
3
  module Optimizely
4
+
5
+ V1_CONFIG_VERSION = '1'
6
+ V2_CONFIG_VERSION = '2'
7
+
4
8
  class ProjectConfig
5
9
  # Representation of the Optimizely project config.
6
10
 
@@ -11,8 +15,9 @@ module Optimizely
11
15
 
12
16
  # Gets project config attributes.
13
17
  attr_reader :error_handler
14
- attr_accessor :logger
18
+ attr_reader :logger
15
19
 
20
+ attr_reader :version
16
21
  attr_reader :account_id
17
22
  attr_reader :project_id
18
23
  attr_reader :attributes
@@ -41,9 +46,14 @@ module Optimizely
41
46
 
42
47
  @error_handler = error_handler
43
48
  @logger = logger
49
+ @version = config['version']
44
50
  @account_id = config['accountId']
45
51
  @project_id = config['projectId']
46
- @attributes = config['dimensions']
52
+ if @version == V1_CONFIG_VERSION
53
+ @attributes = config['dimensions']
54
+ else
55
+ @attributes = config['attributes']
56
+ end
47
57
  @events = config['events']
48
58
  @experiments = config['experiments']
49
59
  @revision = config['revision']
@@ -73,7 +83,7 @@ module Optimizely
73
83
  end
74
84
 
75
85
  def experiment_running?(experiment_key)
76
- # Determine if experiment coresponding to given key is running
86
+ # Determine if experiment corresponding to given key is running
77
87
  #
78
88
  # experiment_key - String key representing the experiment
79
89
  #
@@ -239,6 +249,14 @@ module Optimizely
239
249
  @error_handler.handle_error InvalidExperimentError
240
250
  end
241
251
 
252
+ def get_attribute_id(attribute_key)
253
+ attribute = @attribute_key_map[attribute_key]
254
+ return attribute['id'] if attribute
255
+ @logger.log Logger::ERROR, "Attribute key '#{attribute_key}' is not in datafile."
256
+ @error_handler.handle_error InvalidAttributeError
257
+ nil
258
+ end
259
+
242
260
  private
243
261
 
244
262
  def generate_key_map(array, key)
@@ -1,3 +1,3 @@
1
1
  module Optimizely
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optimizely-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Delikat
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-08-03 00:00:00.000000000 Z
13
+ date: 2016-09-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -133,7 +133,6 @@ files:
133
133
  - lib/optimizely/params.rb
134
134
  - lib/optimizely/project_config.rb
135
135
  - lib/optimizely/version.rb
136
- - lib/start.rb
137
136
  homepage: https://www.optimizely.com/
138
137
  licenses:
139
138
  - Apache-2.0
@@ -154,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
153
  version: '0'
155
154
  requirements: []
156
155
  rubyforge_project:
157
- rubygems_version: 2.4.8
156
+ rubygems_version: 2.6.6
158
157
  signing_key:
159
158
  specification_version: 4
160
159
  summary: Ruby SDK for Optimizely's testing framework
@@ -1,131 +0,0 @@
1
- require './optimizely'
2
- require 'benchmark'
3
-
4
- class PerformanceTests
5
- @error_handler = Optimizely::NoOpErrorHandler.new
6
- @logger = Optimizely::NoOpLogger.new
7
-
8
- def self.test_initialize(testdata, optly)
9
- Optimizely::Project.new(testdata)
10
- end
11
-
12
- def self.test_initialize_logger(testdata, optly)
13
- Optimizely::Project.new(testdata, nil, @logger)
14
- end
15
-
16
- def self.test_initialize_logger_and_error_handler(testdata, optly)
17
- Optimizely::Project.new(testdata, nil, @logger, @error_handler)
18
- end
19
-
20
- def self.test_initialize_no_schema_validation(testdata, optly)
21
- Optimizely::Project.new(testdata, nil, nil, nil, true)
22
- end
23
-
24
- def self.test_initialize_logger_no_schema_validation(testdata, optly)
25
- Optimizely::Project.new(testdata, nil, @logger, nil, true)
26
- end
27
-
28
- def self.test_initialize_error_handler_no_schema_validation(testdata, optly)
29
- Optimizely::Project.new(testdata, nil, nil, @error_handler, true)
30
- end
31
-
32
- def self.test_initialize_logger_error_handler_no_schema_validation(testdata, optly)
33
- Optimizely::Project.new(testdata, nil, @logger, @error_handler, true)
34
- end
35
-
36
- def self.test_initialize_error_handler_no_schema_validation(testdata, optly)
37
- Optimizely::Project.new(testdata, nil, nil, @error_handler, true)
38
- end
39
-
40
- def self.test_activate(testdata, optly)
41
- optly.activate('testExperiment2', 'optimizely_user')
42
- end
43
-
44
- def self.test_activate_with_attributes(testdata, optly)
45
- optly.activate('testExperimentWithFirefoxAudience', 'optimizely_user', {'browser_type' => 'firefox'})
46
- end
47
-
48
- def self.test_activate_with_forced_variation(testdata, optly)
49
- optly.activate('testExperiment2', 'variation_user')
50
- end
51
-
52
- def self.test_activate_grouped_exp(testdata, optly)
53
- optly.activate('mutex_exp2', 'optimizely_user')
54
- end
55
-
56
- def self.test_activate_grouped_exp_with_attributes(testdata, optly)
57
- optly.activate('mutex_exp1', 'optimizely_user', {'browser_type' => 'firefox'})
58
- end
59
-
60
- def self.test_get_variation(testdata, optly)
61
- optly.get_variation('testExperiment2', 'optimizely_user')
62
- end
63
-
64
- def self.test_get_variation_with_attributes(testdata, optly)
65
- optly.get_variation('testExperimentWithFirefoxAudience', 'optimizely_user', {'browser_type' => 'firefox'})
66
- end
67
-
68
- def self.test_get_variation_with_forced_variation(testdata, optly)
69
- optly.get_variation('testExperiment2', 'variation_user')
70
- end
71
-
72
- def self.test_get_variation_grouped_exp(testdata, optly)
73
- optly.get_variation('mutex_exp2', 'optimizely_user')
74
- end
75
-
76
- def self.test_get_variation_grouped_exp_with_attributes(testdata, optly)
77
- optly.get_variation('mutex_exp1', 'optimizely_user')
78
- end
79
-
80
- def self.test_track(testdata, optly)
81
- optly.track('testEvent', 'optimizely_user')
82
- end
83
-
84
- def self.test_track_with_attributes(testdata, optly)
85
- optly.track('testEventWithAudiences' 'optimizely_user', {'browser_type' => 'firefox'})
86
- end
87
-
88
- def self.test_track_with_revenue(testdata, optly)
89
- optly.track('testEvent', 'optimizely_user', nil, 666)
90
- end
91
-
92
- def self.test_track_with_attributes_and_revenue(testdata, optly)
93
- optly.track('testEventWithAudiences', 'optimizely_user', {'browser_type' => 'firefox'}, 666)
94
- end
95
-
96
- def self.test_track_grouped_exp(testdata, optly)
97
- optly.track('testEventWithMultipleGroupedExperiments', 'optimizely_user')
98
- end
99
-
100
- def self.test_track_grouped_exp_with_attributes(testdata, optly)
101
- optly.track('testEventWithMultipleExperiments', 'optimizely_user', {'browser_type' => 'firefox'})
102
- end
103
-
104
- def self.test_track_grouped_exp_with_revenue(testdata, optly)
105
- optly.track('testEventWithMultipleGroupedExperiments', 'optimizely_user', nil, 666)
106
- end
107
-
108
- def self.test_track_grouped_exp_with_attributes_and_revenue(testdata, optly)
109
- optly.track('testEventWithMultipleExperiments', 'optimizely_user', {'browser_type' => 'firefox'}, 666)
110
- end
111
- end
112
-
113
- def run_tests
114
- testdata10 = File.read('testdata_10.json')
115
- testdata25 = File.read('testdata_25.json')
116
- testdata50 = File.read('testdata_50.json')
117
- optly10 = Optimizely::Project.new(testdata10)
118
- optly25 = Optimizely::Project.new(testdata25)
119
- optly50 = Optimizely::Project.new(testdata50)
120
-
121
- tests = PerformanceTests.methods(false)
122
- tests.each do |test|
123
- puts '', test
124
- Benchmark.bmbm do |x|
125
- x.report('10 exps') { PerformanceTests.send(test, testdata10, optly10) }
126
- x.report('25 exps') { PerformanceTests.send(test, testdata25, optly25) }
127
- x.report('50 exps') { PerformanceTests.send(test, testdata50, optly50) }
128
- end
129
- end
130
- end
131
-