optimizely-sdk 3.3.1 → 3.5.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 +5 -5
- data/lib/optimizely.rb +199 -38
- data/lib/optimizely/audience.rb +2 -2
- data/lib/optimizely/config/datafile_project_config.rb +15 -3
- data/lib/optimizely/config/proxy_config.rb +34 -0
- data/lib/optimizely/config_manager/async_scheduler.rb +8 -4
- data/lib/optimizely/config_manager/http_project_config_manager.rb +50 -28
- data/lib/optimizely/config_manager/static_project_config_manager.rb +6 -2
- data/lib/optimizely/event/batch_event_processor.rb +67 -58
- data/lib/optimizely/event_dispatcher.rb +35 -17
- data/lib/optimizely/exceptions.rb +8 -9
- data/lib/optimizely/helpers/constants.rb +6 -3
- data/lib/optimizely/helpers/http_utils.rb +64 -0
- data/lib/optimizely/helpers/variable_type.rb +8 -1
- data/lib/optimizely/notification_center.rb +13 -5
- data/lib/optimizely/optimizely_config.rb +116 -0
- data/lib/optimizely/optimizely_factory.rb +54 -5
- data/lib/optimizely/version.rb +1 -1
- metadata +6 -18
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2016-
|
4
|
+
# Copyright 2016-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -18,6 +18,13 @@
|
|
18
18
|
module Optimizely
|
19
19
|
class Error < StandardError; end
|
20
20
|
|
21
|
+
class HTTPCallError < Error
|
22
|
+
# Raised when a 4xx or 5xx response code is recieved.
|
23
|
+
def initialize(msg = 'HTTP call resulted in a response with an error code.')
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
21
28
|
class InvalidAudienceError < Error
|
22
29
|
# Raised when an invalid audience is provided
|
23
30
|
|
@@ -74,14 +81,6 @@ module Optimizely
|
|
74
81
|
end
|
75
82
|
end
|
76
83
|
|
77
|
-
class InvalidDatafileError < Error
|
78
|
-
# Raised when a public method fails due to an invalid datafile
|
79
|
-
|
80
|
-
def initialize(aborted_method)
|
81
|
-
super("Provided datafile is in an invalid format. Aborting #{aborted_method}.")
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
84
|
class InvalidDatafileVersionError < Error
|
86
85
|
# Raised when a datafile with an unsupported version is provided
|
87
86
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2016-
|
4
|
+
# Copyright 2016-2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -304,7 +304,8 @@ module Optimizely
|
|
304
304
|
'BOOLEAN' => 'boolean',
|
305
305
|
'DOUBLE' => 'double',
|
306
306
|
'INTEGER' => 'integer',
|
307
|
-
'STRING' => 'string'
|
307
|
+
'STRING' => 'string',
|
308
|
+
'JSON' => 'json'
|
308
309
|
}.freeze
|
309
310
|
|
310
311
|
INPUT_VARIABLES = {
|
@@ -357,11 +358,13 @@ module Optimizely
|
|
357
358
|
'AB_TEST' => 'ab-test',
|
358
359
|
'FEATURE' => 'feature',
|
359
360
|
'FEATURE_TEST' => 'feature-test',
|
360
|
-
'FEATURE_VARIABLE' => 'feature-variable'
|
361
|
+
'FEATURE_VARIABLE' => 'feature-variable',
|
362
|
+
'ALL_FEATURE_VARIABLES' => 'all-feature-variables'
|
361
363
|
}.freeze
|
362
364
|
|
363
365
|
CONFIG_MANAGER = {
|
364
366
|
'DATAFILE_URL_TEMPLATE' => 'https://cdn.optimizely.com/datafiles/%s.json',
|
367
|
+
'AUTHENTICATED_DATAFILE_URL_TEMPLATE' => 'https://config.optimizely.com/datafiles/auth/%s.json',
|
365
368
|
# Default time in seconds to block the 'config' method call until 'config' instance has been initialized.
|
366
369
|
'DEFAULT_BLOCKING_TIMEOUT' => 15,
|
367
370
|
# Default config update interval of 5 minutes
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright 2020, Optimizely and contributors
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'net/http'
|
20
|
+
|
21
|
+
module Optimizely
|
22
|
+
module Helpers
|
23
|
+
module HttpUtils
|
24
|
+
module_function
|
25
|
+
|
26
|
+
def make_request(url, http_method, request_body = nil, headers = {}, read_timeout = nil, proxy_config = nil)
|
27
|
+
# makes http/https GET/POST request and returns response
|
28
|
+
#
|
29
|
+
uri = URI.parse(url)
|
30
|
+
|
31
|
+
if http_method == :get
|
32
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
33
|
+
elsif http_method == :post
|
34
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
35
|
+
request.body = request_body if request_body
|
36
|
+
else
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# set headers
|
41
|
+
headers&.each do |key, val|
|
42
|
+
request[key] = val
|
43
|
+
end
|
44
|
+
|
45
|
+
# do not try to make request with proxy unless we have at least a host
|
46
|
+
http_class = if proxy_config&.host
|
47
|
+
Net::HTTP::Proxy(
|
48
|
+
proxy_config.host,
|
49
|
+
proxy_config.port,
|
50
|
+
proxy_config.username,
|
51
|
+
proxy_config.password
|
52
|
+
)
|
53
|
+
else
|
54
|
+
Net::HTTP
|
55
|
+
end
|
56
|
+
|
57
|
+
http = http_class.new(uri.host, uri.port)
|
58
|
+
http.read_timeout = read_timeout if read_timeout
|
59
|
+
http.use_ssl = uri.scheme == 'https'
|
60
|
+
http.request(request)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2017, Optimizely and contributors
|
4
|
+
# Copyright 2017, 2020, Optimizely and contributors
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -48,6 +48,13 @@ module Optimizely
|
|
48
48
|
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
|
49
49
|
"'#{variable_type}': #{e.message}.")
|
50
50
|
end
|
51
|
+
when 'json'
|
52
|
+
begin
|
53
|
+
return_value = JSON.parse(value)
|
54
|
+
rescue => e
|
55
|
+
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
|
56
|
+
"'#{variable_type}': #{e.message}.")
|
57
|
+
end
|
51
58
|
else
|
52
59
|
# default case is string
|
53
60
|
return_value = value
|
@@ -39,20 +39,28 @@ module Optimizely
|
|
39
39
|
|
40
40
|
# Adds notification callback to the notification center
|
41
41
|
#
|
42
|
-
# @param notification_type -
|
43
|
-
# @param notification_callback -
|
42
|
+
# @param notification_type - One of the constants in NOTIFICATION_TYPES
|
43
|
+
# @param notification_callback [lambda, Method, Callable] (default: block) - Called when the event is sent
|
44
|
+
# @yield Block to be used as callback if callback omitted.
|
44
45
|
#
|
45
46
|
# @return [notification ID] Used to remove the notification
|
46
47
|
|
47
|
-
def add_notification_listener(notification_type, notification_callback)
|
48
|
+
def add_notification_listener(notification_type, notification_callback = nil, &block)
|
48
49
|
return nil unless notification_type_valid?(notification_type)
|
49
50
|
|
51
|
+
if notification_callback && block_given?
|
52
|
+
@logger.log Logger::ERROR, 'Callback and block are mutually exclusive.'
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
|
56
|
+
notification_callback ||= block
|
57
|
+
|
50
58
|
unless notification_callback
|
51
59
|
@logger.log Logger::ERROR, 'Callback can not be empty.'
|
52
60
|
return nil
|
53
61
|
end
|
54
62
|
|
55
|
-
unless notification_callback.
|
63
|
+
unless notification_callback.respond_to? :call
|
56
64
|
@logger.log Logger::ERROR, 'Invalid notification callback given.'
|
57
65
|
return nil
|
58
66
|
end
|
@@ -70,7 +78,7 @@ module Optimizely
|
|
70
78
|
#
|
71
79
|
# @param notification_id
|
72
80
|
#
|
73
|
-
# @return [Boolean]
|
81
|
+
# @return [Boolean] true if found and removed, false otherwise
|
74
82
|
|
75
83
|
def remove_notification_listener(notification_id)
|
76
84
|
unless notification_id
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019, Optimizely and contributors
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
module Optimizely
|
19
|
+
class OptimizelyConfig
|
20
|
+
def initialize(project_config)
|
21
|
+
@project_config = project_config
|
22
|
+
end
|
23
|
+
|
24
|
+
def config
|
25
|
+
experiments_map_object = experiments_map
|
26
|
+
features_map = get_features_map(experiments_map_object)
|
27
|
+
{
|
28
|
+
'experimentsMap' => experiments_map_object,
|
29
|
+
'featuresMap' => features_map,
|
30
|
+
'revision' => @project_config.revision
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def experiments_map
|
37
|
+
feature_variables_map = @project_config.feature_flags.reduce({}) do |result_map, feature|
|
38
|
+
result_map.update(feature['id'] => feature['variables'])
|
39
|
+
end
|
40
|
+
@project_config.experiments.reduce({}) do |experiments_map, experiment|
|
41
|
+
experiments_map.update(
|
42
|
+
experiment['key'] => {
|
43
|
+
'id' => experiment['id'],
|
44
|
+
'key' => experiment['key'],
|
45
|
+
'variationsMap' => experiment['variations'].reduce({}) do |variations_map, variation|
|
46
|
+
variation_object = {
|
47
|
+
'id' => variation['id'],
|
48
|
+
'key' => variation['key'],
|
49
|
+
'variablesMap' => get_merged_variables_map(variation, experiment['id'], feature_variables_map)
|
50
|
+
}
|
51
|
+
variation_object['featureEnabled'] = variation['featureEnabled'] if @project_config.feature_experiment?(experiment['id'])
|
52
|
+
variations_map.update(variation['key'] => variation_object)
|
53
|
+
end
|
54
|
+
}
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Merges feature key and type from feature variables to variation variables.
|
60
|
+
def get_merged_variables_map(variation, experiment_id, feature_variables_map)
|
61
|
+
feature_ids = @project_config.experiment_feature_map[experiment_id]
|
62
|
+
return {} unless feature_ids
|
63
|
+
|
64
|
+
experiment_feature_variables = feature_variables_map[feature_ids[0]]
|
65
|
+
# temporary variation variables map to get values to merge.
|
66
|
+
temp_variables_id_map = {}
|
67
|
+
if variation['variables']
|
68
|
+
temp_variables_id_map = variation['variables'].reduce({}) do |variables_map, variable|
|
69
|
+
variables_map.update(
|
70
|
+
variable['id'] => {
|
71
|
+
'id' => variable['id'],
|
72
|
+
'value' => variable['value']
|
73
|
+
}
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
experiment_feature_variables.reduce({}) do |variables_map, feature_variable|
|
78
|
+
variation_variable = temp_variables_id_map[feature_variable['id']]
|
79
|
+
variable_value = variation['featureEnabled'] && variation_variable ? variation_variable['value'] : feature_variable['defaultValue']
|
80
|
+
variables_map.update(
|
81
|
+
feature_variable['key'] => {
|
82
|
+
'id' => feature_variable['id'],
|
83
|
+
'key' => feature_variable['key'],
|
84
|
+
'type' => feature_variable['type'],
|
85
|
+
'value' => variable_value
|
86
|
+
}
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_features_map(all_experiments_map)
|
92
|
+
@project_config.feature_flags.reduce({}) do |features_map, feature|
|
93
|
+
features_map.update(
|
94
|
+
feature['key'] => {
|
95
|
+
'id' => feature['id'],
|
96
|
+
'key' => feature['key'],
|
97
|
+
'experimentsMap' => feature['experimentIds'].reduce({}) do |experiments_map, experiment_id|
|
98
|
+
experiment_key = @project_config.experiment_id_map[experiment_id]['key']
|
99
|
+
experiments_map.update(experiment_key => all_experiments_map[experiment_key])
|
100
|
+
end,
|
101
|
+
'variablesMap' => feature['variables'].reduce({}) do |variables, variable|
|
102
|
+
variables.update(
|
103
|
+
variable['key'] => {
|
104
|
+
'id' => variable['id'],
|
105
|
+
'key' => variable['key'],
|
106
|
+
'type' => variable['type'],
|
107
|
+
'value' => variable['defaultValue']
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
}
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -17,16 +17,18 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require 'optimizely'
|
20
|
+
require 'optimizely/error_handler'
|
20
21
|
require 'optimizely/event_dispatcher'
|
21
22
|
require 'optimizely/event/batch_event_processor'
|
23
|
+
require 'optimizely/logger'
|
24
|
+
require 'optimizely/notification_center'
|
25
|
+
|
22
26
|
module Optimizely
|
23
27
|
class OptimizelyFactory
|
24
|
-
attr_reader :max_event_batch_size, :max_event_flush_interval
|
25
|
-
|
26
28
|
# Convenience method for setting the maximum number of events contained within a batch.
|
27
29
|
# @param batch_size Integer - Sets size of EventQueue.
|
28
30
|
# @param logger - Optional LoggerInterface Provides a log method to log messages.
|
29
|
-
def self.max_event_batch_size(batch_size, logger)
|
31
|
+
def self.max_event_batch_size(batch_size, logger = NoOpLogger.new)
|
30
32
|
unless batch_size.is_a? Integer
|
31
33
|
logger.log(
|
32
34
|
Logger::ERROR,
|
@@ -48,7 +50,7 @@ module Optimizely
|
|
48
50
|
# Convenience method for setting the maximum time interval in milliseconds between event dispatches.
|
49
51
|
# @param flush_interval Numeric - Time interval between event dispatches.
|
50
52
|
# @param logger - Optional LoggerInterface Provides a log method to log messages.
|
51
|
-
def self.max_event_flush_interval(flush_interval, logger)
|
53
|
+
def self.max_event_flush_interval(flush_interval, logger = NoOpLogger.new)
|
52
54
|
unless flush_interval.is_a? Numeric
|
53
55
|
logger.log(
|
54
56
|
Logger::ERROR,
|
@@ -67,12 +69,42 @@ module Optimizely
|
|
67
69
|
@max_event_flush_interval = flush_interval
|
68
70
|
end
|
69
71
|
|
72
|
+
# Convenience method for setting frequency at which datafile has to be polled and ProjectConfig updated.
|
73
|
+
#
|
74
|
+
# @param polling_interval Numeric - Time in seconds after which to update datafile.
|
75
|
+
def self.polling_interval(polling_interval)
|
76
|
+
@polling_interval = polling_interval
|
77
|
+
end
|
78
|
+
|
79
|
+
# Convenience method for setting timeout to block the config call until config has been initialized.
|
80
|
+
#
|
81
|
+
# @param blocking_timeout Numeric - Time in seconds.
|
82
|
+
def self.blocking_timeout(blocking_timeout)
|
83
|
+
@blocking_timeout = blocking_timeout
|
84
|
+
end
|
85
|
+
|
70
86
|
# Returns a new optimizely instance.
|
71
87
|
#
|
72
88
|
# @params sdk_key - Required String uniquely identifying the fallback datafile corresponding to project.
|
73
89
|
# @param fallback datafile - Optional JSON string datafile.
|
74
90
|
def self.default_instance(sdk_key, datafile = nil)
|
75
|
-
|
91
|
+
error_handler = NoOpErrorHandler.new
|
92
|
+
logger = NoOpLogger.new
|
93
|
+
notification_center = NotificationCenter.new(logger, error_handler)
|
94
|
+
|
95
|
+
config_manager = Optimizely::HTTPProjectConfigManager.new(
|
96
|
+
sdk_key: sdk_key,
|
97
|
+
polling_interval: @polling_interval,
|
98
|
+
blocking_timeout: @blocking_timeout,
|
99
|
+
datafile: datafile,
|
100
|
+
logger: logger,
|
101
|
+
error_handler: error_handler,
|
102
|
+
notification_center: notification_center
|
103
|
+
)
|
104
|
+
|
105
|
+
Optimizely::Project.new(
|
106
|
+
datafile, nil, logger, error_handler, nil, nil, sdk_key, config_manager, notification_center
|
107
|
+
)
|
76
108
|
end
|
77
109
|
|
78
110
|
# Returns a new optimizely instance.
|
@@ -108,10 +140,27 @@ module Optimizely
|
|
108
140
|
config_manager = nil,
|
109
141
|
notification_center = nil
|
110
142
|
)
|
143
|
+
|
144
|
+
error_handler ||= NoOpErrorHandler.new
|
145
|
+
logger ||= NoOpLogger.new
|
146
|
+
notification_center = notification_center.is_a?(Optimizely::NotificationCenter) ? notification_center : NotificationCenter.new(logger, error_handler)
|
147
|
+
|
111
148
|
event_processor = BatchEventProcessor.new(
|
112
149
|
event_dispatcher: event_dispatcher || EventDispatcher.new,
|
113
150
|
batch_size: @max_event_batch_size,
|
114
151
|
flush_interval: @max_event_flush_interval,
|
152
|
+
logger: logger,
|
153
|
+
notification_center: notification_center
|
154
|
+
)
|
155
|
+
|
156
|
+
config_manager ||= Optimizely::HTTPProjectConfigManager.new(
|
157
|
+
sdk_key: sdk_key,
|
158
|
+
polling_interval: @polling_interval,
|
159
|
+
blocking_timeout: @blocking_timeout,
|
160
|
+
datafile: datafile,
|
161
|
+
logger: logger,
|
162
|
+
error_handler: error_handler,
|
163
|
+
skip_json_validation: skip_json_validation,
|
115
164
|
notification_center: notification_center
|
116
165
|
)
|
117
166
|
|
data/lib/optimizely/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimizely-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Optimizely
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,20 +94,6 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: httparty
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0.11'
|
104
|
-
type: :runtime
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0.11'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: json-schema
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -149,6 +135,7 @@ files:
|
|
149
135
|
- lib/optimizely/bucketer.rb
|
150
136
|
- lib/optimizely/condition_tree_evaluator.rb
|
151
137
|
- lib/optimizely/config/datafile_project_config.rb
|
138
|
+
- lib/optimizely/config/proxy_config.rb
|
152
139
|
- lib/optimizely/config_manager/async_scheduler.rb
|
153
140
|
- lib/optimizely/config_manager/http_project_config_manager.rb
|
154
141
|
- lib/optimizely/config_manager/project_config_manager.rb
|
@@ -178,10 +165,12 @@ files:
|
|
178
165
|
- lib/optimizely/helpers/date_time_utils.rb
|
179
166
|
- lib/optimizely/helpers/event_tag_utils.rb
|
180
167
|
- lib/optimizely/helpers/group.rb
|
168
|
+
- lib/optimizely/helpers/http_utils.rb
|
181
169
|
- lib/optimizely/helpers/validator.rb
|
182
170
|
- lib/optimizely/helpers/variable_type.rb
|
183
171
|
- lib/optimizely/logger.rb
|
184
172
|
- lib/optimizely/notification_center.rb
|
173
|
+
- lib/optimizely/optimizely_config.rb
|
185
174
|
- lib/optimizely/optimizely_factory.rb
|
186
175
|
- lib/optimizely/params.rb
|
187
176
|
- lib/optimizely/project_config.rb
|
@@ -206,8 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
195
|
- !ruby/object:Gem::Version
|
207
196
|
version: '0'
|
208
197
|
requirements: []
|
209
|
-
|
210
|
-
rubygems_version: 2.5.1
|
198
|
+
rubygems_version: 3.0.3
|
211
199
|
signing_key:
|
212
200
|
specification_version: 4
|
213
201
|
summary: Ruby SDK for Optimizely's testing framework
|