optimizely-sdk 0.1.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/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
|