optimizely-sdk 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/optimizely.rb +74 -16
- data/lib/optimizely/event_builder.rb +4 -1
- data/lib/optimizely/exceptions.rb +20 -28
- data/lib/optimizely/project_config.rb +22 -1
- data/lib/optimizely/version.rb +1 -1
- data/lib/start.rb +3 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9905095aba19bd32c9a64d53e412c8d0caa101fd
|
4
|
+
data.tar.gz: 4d5103f57b0371c91f17fe45cad9faeebf903ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e049b70cd0548306b2721fcf9c494a8552b6a4c47c9efac4fb4b1f1dfda77e00ff2d5166a75dddc634cf20773ca9bb88591d6a7161596c1e2a0da42025dba7d
|
7
|
+
data.tar.gz: 62de64edcce2328c1b59dcb5cdedb067362f1cd85a8218d5554af1d77f06face671d0cf5b99218d00374654333ac538104b7134e192d75707a8138e530c8ad0d
|
data/lib/optimizely.rb
CHANGED
@@ -11,6 +11,10 @@ require_relative 'optimizely/project_config'
|
|
11
11
|
|
12
12
|
module Optimizely
|
13
13
|
class Project
|
14
|
+
|
15
|
+
# Boolean representing if the instance represents a usable Optimizely Project
|
16
|
+
attr_reader :is_valid
|
17
|
+
|
14
18
|
attr_accessor :config
|
15
19
|
attr_accessor :bucketer
|
16
20
|
attr_accessor :event_builder
|
@@ -33,14 +37,37 @@ module Optimizely
|
|
33
37
|
# By default all exceptions will be suppressed.
|
34
38
|
# skip_json_validation - Optional boolean param to skip JSON schema validation of the provided datafile.
|
35
39
|
|
40
|
+
@is_valid = true
|
36
41
|
@logger = logger || NoOpLogger.new
|
37
42
|
@error_handler = error_handler || NoOpErrorHandler.new
|
38
43
|
@event_dispatcher = event_dispatcher || EventDispatcher.new
|
39
|
-
validate_inputs(datafile, skip_json_validation)
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
begin
|
46
|
+
validate_inputs(datafile, skip_json_validation)
|
47
|
+
rescue InvalidInputError => e
|
48
|
+
@is_valid = false
|
49
|
+
logger = SimpleLogger.new
|
50
|
+
logger.log(Logger::ERROR, e.message)
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
@config = ProjectConfig.new(datafile, @logger, @error_handler)
|
56
|
+
rescue
|
57
|
+
@is_valid = false
|
58
|
+
logger = SimpleLogger.new
|
59
|
+
logger.log(Logger::ERROR, InvalidInputError.new('datafile').message)
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
@bucketer = Bucketer.new(@config)
|
65
|
+
@event_builder = EVENT_BUILDERS_BY_VERSION[@config.version].new(@config, @bucketer)
|
66
|
+
rescue
|
67
|
+
@is_valid = false
|
68
|
+
logger = SimpleLogger.new
|
69
|
+
logger.log(Logger::ERROR, InvalidDatafileVersionError.new)
|
70
|
+
end
|
44
71
|
end
|
45
72
|
|
46
73
|
def activate(experiment_key, user_id, attributes = nil)
|
@@ -51,7 +78,13 @@ module Optimizely
|
|
51
78
|
# attributes - Hash representing user attributes and values to be recorded.
|
52
79
|
#
|
53
80
|
# Returns variation key representing the variation the user will be bucketed in.
|
54
|
-
# Returns nil if experiment is not Running
|
81
|
+
# Returns nil if experiment is not Running, if user is not in experiment, or if datafile is invalid.
|
82
|
+
|
83
|
+
unless @is_valid
|
84
|
+
logger = SimpleLogger.new
|
85
|
+
logger.log(Logger::ERROR, InvalidDatafileError.new('activate').message)
|
86
|
+
return nil
|
87
|
+
end
|
55
88
|
|
56
89
|
if attributes && !attributes_valid?(attributes)
|
57
90
|
@logger.log(Logger::INFO, "Not activating user '#{user_id}'.")
|
@@ -75,7 +108,11 @@ module Optimizely
|
|
75
108
|
@logger.log(Logger::INFO,
|
76
109
|
'Dispatching impression event to URL %s with params %s.' % [impression_event.url,
|
77
110
|
impression_event.params])
|
78
|
-
|
111
|
+
begin
|
112
|
+
@event_dispatcher.dispatch_event(impression_event)
|
113
|
+
rescue => e
|
114
|
+
@logger.log(Logger::ERROR, "Unable to dispatch impression event. Error: #{e}")
|
115
|
+
end
|
79
116
|
|
80
117
|
@config.get_variation_key_from_id(experiment_key, variation_id)
|
81
118
|
end
|
@@ -88,7 +125,13 @@ module Optimizely
|
|
88
125
|
# attributes - Hash representing user attributes.
|
89
126
|
#
|
90
127
|
# Returns variation key where visitor will be bucketed.
|
91
|
-
# Returns nil if experiment is not Running
|
128
|
+
# Returns nil if experiment is not Running, if user is not in experiment, or if datafile is invalid.
|
129
|
+
|
130
|
+
unless @is_valid
|
131
|
+
logger = SimpleLogger.new
|
132
|
+
logger.log(Logger::ERROR, InvalidDatafileError.new('get_variation').message)
|
133
|
+
return nil
|
134
|
+
end
|
92
135
|
|
93
136
|
if attributes && !attributes_valid?(attributes)
|
94
137
|
@logger.log(Logger::INFO, "Not activating user '#{user_id}.")
|
@@ -112,7 +155,11 @@ module Optimizely
|
|
112
155
|
# attributes - Hash representing visitor attributes and values which need to be recorded.
|
113
156
|
# event_value - Value associated with the event. Can be used to represent revenue in cents.
|
114
157
|
|
115
|
-
|
158
|
+
unless @is_valid
|
159
|
+
logger = SimpleLogger.new
|
160
|
+
logger.log(Logger::ERROR, InvalidDatafileError.new('track').message)
|
161
|
+
return nil
|
162
|
+
end
|
116
163
|
|
117
164
|
return nil if attributes && !attributes_valid?(attributes)
|
118
165
|
|
@@ -134,14 +181,21 @@ module Optimizely
|
|
134
181
|
end
|
135
182
|
|
136
183
|
# Don't track events without valid experiments attached
|
137
|
-
|
184
|
+
if valid_experiment_keys.empty?
|
185
|
+
@logger.log(Logger::INFO, "There are no valid experiments for event '#{event_key}' to track.")
|
186
|
+
return nil
|
187
|
+
end
|
138
188
|
|
139
189
|
conversion_event = @event_builder.create_conversion_event(event_key, user_id, attributes,
|
140
190
|
event_value, valid_experiment_keys)
|
141
191
|
@logger.log(Logger::INFO,
|
142
192
|
'Dispatching conversion event to URL %s with params %s.' % [conversion_event.url,
|
143
193
|
conversion_event.params])
|
144
|
-
|
194
|
+
begin
|
195
|
+
@event_dispatcher.dispatch_event(conversion_event)
|
196
|
+
rescue => e
|
197
|
+
@logger.log(Logger::ERROR, "Unable to dispatch conversion event. Error: #{e}")
|
198
|
+
end
|
145
199
|
end
|
146
200
|
|
147
201
|
private
|
@@ -156,13 +210,17 @@ module Optimizely
|
|
156
210
|
# Returns boolean representing whether all preconditions are valid.
|
157
211
|
|
158
212
|
unless @config.experiment_running?(experiment_key)
|
159
|
-
@logger.log(Logger::INFO, "Experiment '#{experiment_key} is not running.")
|
213
|
+
@logger.log(Logger::INFO, "Experiment '#{experiment_key}' is not running.")
|
160
214
|
return false
|
161
215
|
end
|
162
216
|
|
217
|
+
if @config.user_in_forced_variation?(experiment_key, user_id)
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
|
163
221
|
unless Audience.user_in_experiment?(@config, experiment_key, attributes)
|
164
222
|
@logger.log(Logger::INFO,
|
165
|
-
"User '#{user_id} does not meet the conditions to be in experiment '#{experiment_key}.")
|
223
|
+
"User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'.")
|
166
224
|
return false
|
167
225
|
end
|
168
226
|
|
@@ -180,12 +238,12 @@ module Optimizely
|
|
180
238
|
|
181
239
|
def validate_inputs(datafile, skip_json_validation)
|
182
240
|
unless skip_json_validation
|
183
|
-
raise
|
241
|
+
raise InvalidInputError.new('datafile') unless Helpers::Validator.datafile_valid?(datafile)
|
184
242
|
end
|
185
243
|
|
186
|
-
raise
|
187
|
-
raise
|
188
|
-
raise
|
244
|
+
raise InvalidInputError.new('logger') unless Helpers::Validator.logger_valid?(@logger)
|
245
|
+
raise InvalidInputError.new('error_handler') unless Helpers::Validator.error_handler_valid?(@error_handler)
|
246
|
+
raise InvalidInputError.new('event_dispatcher') unless Helpers::Validator.event_dispatcher_valid?(@event_dispatcher)
|
189
247
|
end
|
190
248
|
end
|
191
249
|
end
|
@@ -274,7 +274,10 @@ module Optimizely
|
|
274
274
|
attributes.keys.each do |attribute_key|
|
275
275
|
attribute_value = attributes[attribute_key]
|
276
276
|
next unless attribute_value
|
277
|
-
|
277
|
+
|
278
|
+
# Skip attributes not in the datafile
|
279
|
+
segment_id = @config.get_segment_id(attribute_key)
|
280
|
+
next unless segment_id
|
278
281
|
segment_param = sprintf(ATTRIBUTE_PARAM_FORMAT,
|
279
282
|
segment_prefix: Params::SEGMENT_PREFIX, segment_id: segment_id)
|
280
283
|
params[segment_param] = attribute_value
|
@@ -25,59 +25,51 @@ module Optimizely
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
class
|
29
|
-
# Raised when an invalid
|
30
|
-
|
31
|
-
def initialize(msg = 'Provided datafile is in an invalid format.')
|
32
|
-
super
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class InvalidErrorHandlerError < Error
|
37
|
-
# Raised when an invalid error handler is provided
|
28
|
+
class InvalidExperimentError < Error
|
29
|
+
# Raised when an invalid experiment key is provided
|
38
30
|
|
39
|
-
def initialize(msg = 'Provided
|
31
|
+
def initialize(msg = 'Provided experiment is not in datafile.')
|
40
32
|
super
|
41
33
|
end
|
42
34
|
end
|
43
35
|
|
44
|
-
class
|
45
|
-
# Raised when an invalid event
|
36
|
+
class InvalidEventError < Error
|
37
|
+
# Raised when an invalid event key is provided
|
46
38
|
|
47
|
-
def initialize(msg = 'Provided
|
39
|
+
def initialize(msg = 'Provided event is not in datafile.')
|
48
40
|
super
|
49
41
|
end
|
50
42
|
end
|
51
43
|
|
52
|
-
class
|
53
|
-
# Raised when an invalid
|
44
|
+
class InvalidVariationError < Error
|
45
|
+
# Raised when an invalid variation key or ID is provided
|
54
46
|
|
55
|
-
def initialize(msg = 'Provided
|
47
|
+
def initialize(msg = 'Provided variation is not in datafile.')
|
56
48
|
super
|
57
49
|
end
|
58
50
|
end
|
59
51
|
|
60
|
-
class
|
61
|
-
# Raised when
|
52
|
+
class InvalidDatafileError < Error
|
53
|
+
# Raised when a public method fails due to an invalid datafile
|
62
54
|
|
63
|
-
def initialize(
|
64
|
-
super
|
55
|
+
def initialize(aborted_method)
|
56
|
+
super("Provided datafile is in an invalid format. Aborting #{aborted_method}.")
|
65
57
|
end
|
66
58
|
end
|
67
59
|
|
68
|
-
class
|
69
|
-
# Raised when an
|
60
|
+
class InvalidDatafileVersionError < Error
|
61
|
+
# Raised when a datafile with an unsupported version is provided
|
70
62
|
|
71
|
-
def initialize(msg = 'Provided
|
63
|
+
def initialize(msg = 'Provided datafile is an unsupported version.')
|
72
64
|
super
|
73
65
|
end
|
74
66
|
end
|
75
67
|
|
76
|
-
class
|
77
|
-
#
|
68
|
+
class InvalidInputError < Error
|
69
|
+
# Abstract error raised when an invalid input is provided during Project instantiation
|
78
70
|
|
79
|
-
def initialize(
|
80
|
-
super
|
71
|
+
def initialize(type)
|
72
|
+
super("Provided #{type} is in an invalid format.")
|
81
73
|
end
|
82
74
|
end
|
83
75
|
end
|
@@ -139,7 +139,7 @@ module Optimizely
|
|
139
139
|
goal = @event_key_map[goal_key]
|
140
140
|
return goal['experimentIds'] if goal
|
141
141
|
@logger.log Logger::ERROR, "Event '#{goal_key}' is not in datafile."
|
142
|
-
@error_handler.handle_error
|
142
|
+
@error_handler.handle_error InvalidEventError
|
143
143
|
[]
|
144
144
|
end
|
145
145
|
|
@@ -257,6 +257,27 @@ module Optimizely
|
|
257
257
|
nil
|
258
258
|
end
|
259
259
|
|
260
|
+
def get_segment_id(attribute_key)
|
261
|
+
attribute = @attribute_key_map[attribute_key]
|
262
|
+
return attribute['segmentId'] if attribute
|
263
|
+
@logger.log Logger::ERROR, "Attribute key '#{attribute_key}' is not in datafile."
|
264
|
+
@error_handler.handle_error InvalidAttributeError
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
def user_in_forced_variation?(experiment_key, user_id)
|
269
|
+
# Determines if a given user is in a forced variation
|
270
|
+
#
|
271
|
+
# experiment_key - String experiment key
|
272
|
+
# user_id - String user ID
|
273
|
+
#
|
274
|
+
# Returns true if user is in a forced variation
|
275
|
+
|
276
|
+
forced_variations = get_forced_variations(experiment_key)
|
277
|
+
return forced_variations.include?(user_id) if forced_variations
|
278
|
+
false
|
279
|
+
end
|
280
|
+
|
260
281
|
private
|
261
282
|
|
262
283
|
def generate_key_map(array, key)
|
data/lib/optimizely/version.rb
CHANGED
data/lib/start.rb
ADDED
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.
|
4
|
+
version: 1.0.0
|
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-
|
13
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -110,8 +110,7 @@ dependencies:
|
|
110
110
|
- - "~>"
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: 2.6.2
|
113
|
-
description: A Ruby SDK for Optimizely's
|
114
|
-
in private beta.
|
113
|
+
description: A Ruby SDK for Optimizely's Full Stack product.
|
115
114
|
email:
|
116
115
|
- developers@optimizely.com
|
117
116
|
executables: []
|
@@ -133,6 +132,7 @@ files:
|
|
133
132
|
- lib/optimizely/params.rb
|
134
133
|
- lib/optimizely/project_config.rb
|
135
134
|
- lib/optimizely/version.rb
|
135
|
+
- lib/start.rb
|
136
136
|
homepage: https://www.optimizely.com/
|
137
137
|
licenses:
|
138
138
|
- Apache-2.0
|